From 14831d98b0e3280696af1e864bc63a6141db60a4 Mon Sep 17 00:00:00 2001 From: wolf Date: Fri, 20 Aug 2004 16:31:03 +0000 Subject: [PATCH] Import UMFPACK 4.3 git-svn-id: https://svn.dealii.org/trunk@9557 0785d39b-7218-0410-832d-ea1e28bc413d --- deal.II/contrib/umfpack/AMD/Demo/Makefile | 79 + deal.II/contrib/umfpack/AMD/Demo/amd_demo.c | 169 + deal.II/contrib/umfpack/AMD/Demo/amd_demo.out | 177 + deal.II/contrib/umfpack/AMD/Demo/amd_demo2.c | 261 ++ .../contrib/umfpack/AMD/Demo/amd_demo2.out | 310 ++ .../contrib/umfpack/AMD/Demo/amd_f77cross.f | 51 + .../contrib/umfpack/AMD/Demo/amd_f77cross.out | 51 + .../contrib/umfpack/AMD/Demo/amd_f77demo.f | 161 + .../contrib/umfpack/AMD/Demo/amd_f77demo.out | 318 ++ .../contrib/umfpack/AMD/Demo/amd_f77simple.f | 38 + .../umfpack/AMD/Demo/amd_f77simple.out | 5 + .../contrib/umfpack/AMD/Demo/amd_f77wrapper.c | 108 + deal.II/contrib/umfpack/AMD/Demo/amd_simple.c | 23 + .../contrib/umfpack/AMD/Demo/amd_simple.out | 5 + .../contrib/umfpack/AMD/Doc/AMD_UserGuide.bib | 86 + .../contrib/umfpack/AMD/Doc/AMD_UserGuide.pdf | Bin 0 -> 176601 bytes .../contrib/umfpack/AMD/Doc/AMD_UserGuide.tex | 1138 ++++++ deal.II/contrib/umfpack/AMD/Doc/ChangeLog | 37 + deal.II/contrib/umfpack/AMD/Doc/License | 25 + deal.II/contrib/umfpack/AMD/Doc/Makefile | 28 + deal.II/contrib/umfpack/AMD/Include/amd.h | 359 ++ deal.II/contrib/umfpack/AMD/MATLAB/Contents.m | 13 + .../contrib/umfpack/AMD/MATLAB/GNUmakefile | 30 + deal.II/contrib/umfpack/AMD/MATLAB/Makefile | 50 + deal.II/contrib/umfpack/AMD/MATLAB/amd.m | 74 + deal.II/contrib/umfpack/AMD/MATLAB/amd_demo.m | 96 + .../contrib/umfpack/AMD/MATLAB/amd_demo.m.out | 136 + deal.II/contrib/umfpack/AMD/MATLAB/amd_make.m | 32 + deal.II/contrib/umfpack/AMD/MATLAB/amd_mex.c | 189 + deal.II/contrib/umfpack/AMD/MATLAB/can_24.mat | Bin 0 -> 1560 bytes deal.II/contrib/umfpack/AMD/README | 202 ++ deal.II/contrib/umfpack/AMD/Source/Makefile | 35 + deal.II/contrib/umfpack/AMD/Source/amd.f | 1214 +++++++ deal.II/contrib/umfpack/AMD/Source/amd_1.c | 181 + deal.II/contrib/umfpack/AMD/Source/amd_2.c | 1823 ++++++++++ deal.II/contrib/umfpack/AMD/Source/amd_aat.c | 180 + .../contrib/umfpack/AMD/Source/amd_control.c | 61 + .../contrib/umfpack/AMD/Source/amd_defaults.c | 33 + deal.II/contrib/umfpack/AMD/Source/amd_dump.c | 176 + deal.II/contrib/umfpack/AMD/Source/amd_info.c | 115 + .../contrib/umfpack/AMD/Source/amd_internal.h | 426 +++ .../contrib/umfpack/AMD/Source/amd_order.c | 139 + .../umfpack/AMD/Source/amd_post_tree.c | 121 + .../umfpack/AMD/Source/amd_postorder.c | 207 ++ .../umfpack/AMD/Source/amd_preprocess.c | 225 ++ .../contrib/umfpack/AMD/Source/amd_valid.c | 78 + deal.II/contrib/umfpack/AMD/Source/amdbar.f | 1206 +++++++ deal.II/contrib/umfpack/Makefile | 32 + deal.II/contrib/umfpack/README | 5 + deal.II/contrib/umfpack/README.deal.II | 14 + deal.II/contrib/umfpack/UMFPACK/Doc/ChangeLog | 334 ++ deal.II/contrib/umfpack/UMFPACK/Doc/License | 53 + deal.II/contrib/umfpack/UMFPACK/Doc/Makefile | 52 + .../umfpack/UMFPACK/Doc/QuickStart.pdf | Bin 0 -> 125745 bytes .../umfpack/UMFPACK/Doc/QuickStart.tex | 1020 ++++++ .../contrib/umfpack/UMFPACK/Doc/UserGuide.bib | 324 ++ .../contrib/umfpack/UMFPACK/Doc/UserGuide.pdf | Bin 0 -> 391768 bytes .../umfpack/UMFPACK/Doc/UserGuide.sed1 | 32 + .../umfpack/UMFPACK/Doc/UserGuide.sed2 | 3 + .../umfpack/UMFPACK/Doc/UserGuide.stex | 2554 ++++++++++++++ .../contrib/umfpack/UMFPACK/Include/umfpack.h | 405 +++ .../UMFPACK/Include/umfpack_col_to_triplet.h | 111 + .../UMFPACK/Include/umfpack_defaults.h | 75 + .../UMFPACK/Include/umfpack_free_numeric.h | 68 + .../UMFPACK/Include/umfpack_free_symbolic.h | 68 + .../UMFPACK/Include/umfpack_get_lunz.h | 134 + .../UMFPACK/Include/umfpack_get_numeric.h | 253 ++ .../UMFPACK/Include/umfpack_get_symbolic.h | 342 ++ .../UMFPACK/Include/umfpack_load_numeric.h | 96 + .../UMFPACK/Include/umfpack_load_symbolic.h | 96 + .../umfpack/UMFPACK/Include/umfpack_numeric.h | 552 +++ .../UMFPACK/Include/umfpack_qsymbolic.h | 140 + .../UMFPACK/Include/umfpack_report_control.h | 77 + .../UMFPACK/Include/umfpack_report_info.h | 87 + .../UMFPACK/Include/umfpack_report_matrix.h | 198 ++ .../UMFPACK/Include/umfpack_report_numeric.h | 113 + .../UMFPACK/Include/umfpack_report_perm.h | 113 + .../UMFPACK/Include/umfpack_report_status.h | 91 + .../UMFPACK/Include/umfpack_report_symbolic.h | 112 + .../UMFPACK/Include/umfpack_report_triplet.h | 149 + .../UMFPACK/Include/umfpack_report_vector.h | 134 + .../UMFPACK/Include/umfpack_save_numeric.h | 93 + .../UMFPACK/Include/umfpack_save_symbolic.h | 93 + .../umfpack/UMFPACK/Include/umfpack_scale.h | 105 + .../umfpack/UMFPACK/Include/umfpack_solve.h | 302 ++ .../UMFPACK/Include/umfpack_symbolic.h | 542 +++ .../umfpack/UMFPACK/Include/umfpack_tictoc.h | 61 + .../umfpack/UMFPACK/Include/umfpack_timer.h | 41 + .../UMFPACK/Include/umfpack_transpose.h | 217 ++ .../UMFPACK/Include/umfpack_triplet_to_col.h | 261 ++ .../umfpack/UMFPACK/Include/umfpack_wsolve.h | 166 + .../contrib/umfpack/UMFPACK/MATLAB/Contents.m | 22 + .../umfpack/UMFPACK/MATLAB/GNUmakefile | 232 ++ .../contrib/umfpack/UMFPACK/MATLAB/Makefile | 436 +++ .../umfpack/UMFPACK/MATLAB/lu_normest.m | 106 + .../contrib/umfpack/UMFPACK/MATLAB/luflop.m | 24 + .../umfpack/UMFPACK/MATLAB/luflopmex.c | 121 + .../contrib/umfpack/UMFPACK/MATLAB/umfpack.m | 44 + .../umfpack/UMFPACK/MATLAB/umfpack_btf.m | 129 + .../umfpack/UMFPACK/MATLAB/umfpack_demo.m | 195 + .../umfpack/UMFPACK/MATLAB/umfpack_demo.m.out | 72 + .../umfpack/UMFPACK/MATLAB/umfpack_details.m | 155 + .../umfpack/UMFPACK/MATLAB/umfpack_make.m | 356 ++ .../umfpack/UMFPACK/MATLAB/umfpack_report.m | 346 ++ .../umfpack/UMFPACK/MATLAB/umfpack_simple.m | 61 + .../UMFPACK/MATLAB/umfpack_simple.m.out | 74 + .../umfpack/UMFPACK/MATLAB/umfpack_solve.m | 96 + .../umfpack/UMFPACK/MATLAB/umfpackmex.c | 1284 +++++++ .../umfpack/UMFPACK/MATLAB/west0067.mat | Bin 0 -> 4456 bytes deal.II/contrib/umfpack/UMFPACK/README | 376 ++ .../contrib/umfpack/UMFPACK/Source/Makefile | 193 + .../contrib/umfpack/UMFPACK/Source/umf_2by2.c | 864 +++++ .../contrib/umfpack/UMFPACK/Source/umf_2by2.h | 37 + .../umfpack/UMFPACK/Source/umf_analyze.c | 707 ++++ .../umfpack/UMFPACK/Source/umf_analyze.h | 24 + .../umfpack/UMFPACK/Source/umf_apply_order.c | 44 + .../umfpack/UMFPACK/Source/umf_apply_order.h | 15 + .../umfpack/UMFPACK/Source/umf_assemble.c | 1216 +++++++ .../umfpack/UMFPACK/Source/umf_assemble.h | 18 + .../umfpack/UMFPACK/Source/umf_blas3_update.c | 165 + .../umfpack/UMFPACK/Source/umf_blas3_update.h | 11 + .../umfpack/UMFPACK/Source/umf_build_tuples.c | 160 + .../umfpack/UMFPACK/Source/umf_build_tuples.h | 12 + .../umfpack/UMFPACK/Source/umf_colamd.c | 3140 +++++++++++++++++ .../umfpack/UMFPACK/Source/umf_colamd.h | 256 ++ .../umfpack/UMFPACK/Source/umf_config.h | 920 +++++ .../UMFPACK/Source/umf_create_element.c | 605 ++++ .../UMFPACK/Source/umf_create_element.h | 13 + .../contrib/umfpack/UMFPACK/Source/umf_dump.c | 1220 +++++++ .../contrib/umfpack/UMFPACK/Source/umf_dump.h | 187 + .../umfpack/UMFPACK/Source/umf_extend_front.c | 393 +++ .../umfpack/UMFPACK/Source/umf_extend_front.h | 12 + .../contrib/umfpack/UMFPACK/Source/umf_free.c | 46 + .../contrib/umfpack/UMFPACK/Source/umf_free.h | 11 + .../umfpack/UMFPACK/Source/umf_fsize.c | 70 + .../umfpack/UMFPACK/Source/umf_fsize.h | 16 + .../UMFPACK/Source/umf_garbage_collection.c | 696 ++++ .../UMFPACK/Source/umf_garbage_collection.h | 15 + .../umfpack/UMFPACK/Source/umf_get_memory.c | 223 ++ .../umfpack/UMFPACK/Source/umf_get_memory.h | 16 + .../umfpack/UMFPACK/Source/umf_grow_front.c | 295 ++ .../umfpack/UMFPACK/Source/umf_grow_front.h | 15 + .../umfpack/UMFPACK/Source/umf_init_front.c | 268 ++ .../umfpack/UMFPACK/Source/umf_init_front.h | 12 + .../umfpack/UMFPACK/Source/umf_internal.h | 756 ++++ .../UMFPACK/Source/umf_is_permutation.c | 56 + .../UMFPACK/Source/umf_is_permutation.h | 14 + .../umfpack/UMFPACK/Source/umf_kernel.c | 300 ++ .../umfpack/UMFPACK/Source/umf_kernel.h | 19 + .../umfpack/UMFPACK/Source/umf_kernel_init.c | 1053 ++++++ .../umfpack/UMFPACK/Source/umf_kernel_init.h | 19 + .../UMFPACK/Source/umf_kernel_wrapup.c | 466 +++ .../UMFPACK/Source/umf_kernel_wrapup.h | 13 + .../umfpack/UMFPACK/Source/umf_local_search.c | 1949 ++++++++++ .../umfpack/UMFPACK/Source/umf_local_search.h | 13 + .../umfpack/UMFPACK/Source/umf_lsolve.c | 151 + .../umfpack/UMFPACK/Source/umf_lsolve.h | 13 + .../umfpack/UMFPACK/Source/umf_ltsolve.c | 225 ++ .../umfpack/UMFPACK/Source/umf_ltsolve.h | 20 + .../umfpack/UMFPACK/Source/umf_malloc.c | 92 + .../umfpack/UMFPACK/Source/umf_malloc.h | 16 + .../UMFPACK/Source/umf_mem_alloc_element.c | 83 + .../UMFPACK/Source/umf_mem_alloc_element.h | 18 + .../UMFPACK/Source/umf_mem_alloc_head_block.c | 55 + .../UMFPACK/Source/umf_mem_alloc_head_block.h | 12 + .../UMFPACK/Source/umf_mem_alloc_tail_block.c | 134 + .../UMFPACK/Source/umf_mem_alloc_tail_block.h | 12 + .../UMFPACK/Source/umf_mem_free_tail_block.c | 143 + .../UMFPACK/Source/umf_mem_free_tail_block.h | 12 + .../UMFPACK/Source/umf_mem_init_memoryspace.c | 65 + .../UMFPACK/Source/umf_mem_init_memoryspace.h | 11 + .../umfpack/UMFPACK/Source/umf_realloc.c | 76 + .../umfpack/UMFPACK/Source/umf_realloc.h | 13 + .../umfpack/UMFPACK/Source/umf_report_perm.c | 86 + .../umfpack/UMFPACK/Source/umf_report_perm.h | 15 + .../UMFPACK/Source/umf_report_vector.c | 119 + .../UMFPACK/Source/umf_report_vector.h | 16 + .../umfpack/UMFPACK/Source/umf_row_search.c | 838 +++++ .../umfpack/UMFPACK/Source/umf_row_search.h | 33 + .../umfpack/UMFPACK/Source/umf_scale.c | 128 + .../umfpack/UMFPACK/Source/umf_scale.h | 13 + .../umfpack/UMFPACK/Source/umf_scale_column.c | 434 +++ .../umfpack/UMFPACK/Source/umf_scale_column.h | 12 + .../umfpack/UMFPACK/Source/umf_set_stats.c | 134 + .../umfpack/UMFPACK/Source/umf_set_stats.h | 25 + .../umfpack/UMFPACK/Source/umf_singletons.c | 891 +++++ .../umfpack/UMFPACK/Source/umf_singletons.h | 31 + .../umfpack/UMFPACK/Source/umf_solve.c | 1372 +++++++ .../umfpack/UMFPACK/Source/umf_solve.h | 26 + .../umfpack/UMFPACK/Source/umf_start_front.c | 284 ++ .../umfpack/UMFPACK/Source/umf_start_front.h | 14 + .../umfpack/UMFPACK/Source/umf_store_lu.c | 1035 ++++++ .../umfpack/UMFPACK/Source/umf_store_lu.h | 18 + .../UMFPACK/Source/umf_symbolic_usage.c | 46 + .../UMFPACK/Source/umf_symbolic_usage.h | 16 + .../umfpack/UMFPACK/Source/umf_transpose.c | 332 ++ .../umfpack/UMFPACK/Source/umf_transpose.h | 28 + .../umfpack/UMFPACK/Source/umf_triplet.c | 386 ++ .../umfpack/UMFPACK/Source/umf_triplet.h | 86 + .../UMFPACK/Source/umf_tuple_lengths.c | 136 + .../UMFPACK/Source/umf_tuple_lengths.h | 13 + .../umfpack/UMFPACK/Source/umf_usolve.c | 226 ++ .../umfpack/UMFPACK/Source/umf_usolve.h | 13 + .../umfpack/UMFPACK/Source/umf_utsolve.c | 331 ++ .../umfpack/UMFPACK/Source/umf_utsolve.h | 21 + .../UMFPACK/Source/umf_valid_numeric.c | 47 + .../UMFPACK/Source/umf_valid_numeric.h | 11 + .../UMFPACK/Source/umf_valid_symbolic.c | 48 + .../UMFPACK/Source/umf_valid_symbolic.h | 11 + .../umfpack/UMFPACK/Source/umf_version.h | 989 ++++++ .../UMFPACK/Source/umfpack_col_to_triplet.c | 73 + .../umfpack/UMFPACK/Source/umfpack_defaults.c | 119 + .../UMFPACK/Source/umfpack_free_numeric.c | 58 + .../UMFPACK/Source/umfpack_free_symbolic.c | 57 + .../umfpack/UMFPACK/Source/umfpack_get_lunz.c | 56 + .../UMFPACK/Source/umfpack_get_numeric.c | 980 +++++ .../UMFPACK/Source/umfpack_get_symbolic.c | 192 + .../UMFPACK/Source/umfpack_load_numeric.c | 162 + .../UMFPACK/Source/umfpack_load_symbolic.c | 166 + .../umfpack/UMFPACK/Source/umfpack_numeric.c | 796 +++++ .../UMFPACK/Source/umfpack_qsymbolic.c | 2366 +++++++++++++ .../UMFPACK/Source/umfpack_report_control.c | 387 ++ .../UMFPACK/Source/umfpack_report_info.c | 616 ++++ .../UMFPACK/Source/umfpack_report_matrix.c | 205 ++ .../UMFPACK/Source/umfpack_report_numeric.c | 664 ++++ .../UMFPACK/Source/umfpack_report_perm.c | 45 + .../UMFPACK/Source/umfpack_report_status.c | 119 + .../UMFPACK/Source/umfpack_report_symbolic.c | 229 ++ .../UMFPACK/Source/umfpack_report_triplet.c | 101 + .../UMFPACK/Source/umfpack_report_vector.c | 44 + .../UMFPACK/Source/umfpack_save_numeric.c | 93 + .../UMFPACK/Source/umfpack_save_symbolic.c | 96 + .../umfpack/UMFPACK/Source/umfpack_scale.c | 112 + .../umfpack/UMFPACK/Source/umfpack_solve.c | 249 ++ .../umfpack/UMFPACK/Source/umfpack_symbolic.c | 40 + .../umfpack/UMFPACK/Source/umfpack_tictoc.c | 107 + .../umfpack/UMFPACK/Source/umfpack_timer.c | 85 + .../UMFPACK/Source/umfpack_transpose.c | 109 + .../UMFPACK/Source/umfpack_triplet_to_col.c | 229 ++ 239 files changed, 58841 insertions(+) create mode 100644 deal.II/contrib/umfpack/AMD/Demo/Makefile create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_demo.c create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_demo.out create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_demo2.c create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_demo2.out create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_f77cross.f create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_f77cross.out create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_f77demo.f create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_f77demo.out create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_f77simple.f create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_f77simple.out create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_f77wrapper.c create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_simple.c create mode 100644 deal.II/contrib/umfpack/AMD/Demo/amd_simple.out create mode 100644 deal.II/contrib/umfpack/AMD/Doc/AMD_UserGuide.bib create mode 100644 deal.II/contrib/umfpack/AMD/Doc/AMD_UserGuide.pdf create mode 100644 deal.II/contrib/umfpack/AMD/Doc/AMD_UserGuide.tex create mode 100644 deal.II/contrib/umfpack/AMD/Doc/ChangeLog create mode 100644 deal.II/contrib/umfpack/AMD/Doc/License create mode 100644 deal.II/contrib/umfpack/AMD/Doc/Makefile create mode 100644 deal.II/contrib/umfpack/AMD/Include/amd.h create mode 100644 deal.II/contrib/umfpack/AMD/MATLAB/Contents.m create mode 100644 deal.II/contrib/umfpack/AMD/MATLAB/GNUmakefile create mode 100644 deal.II/contrib/umfpack/AMD/MATLAB/Makefile create mode 100644 deal.II/contrib/umfpack/AMD/MATLAB/amd.m create mode 100644 deal.II/contrib/umfpack/AMD/MATLAB/amd_demo.m create mode 100644 deal.II/contrib/umfpack/AMD/MATLAB/amd_demo.m.out create mode 100644 deal.II/contrib/umfpack/AMD/MATLAB/amd_make.m create mode 100644 deal.II/contrib/umfpack/AMD/MATLAB/amd_mex.c create mode 100644 deal.II/contrib/umfpack/AMD/MATLAB/can_24.mat create mode 100644 deal.II/contrib/umfpack/AMD/README create mode 100644 deal.II/contrib/umfpack/AMD/Source/Makefile create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd.f create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_1.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_2.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_aat.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_control.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_defaults.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_dump.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_info.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_internal.h create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_order.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_post_tree.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_postorder.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_preprocess.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amd_valid.c create mode 100644 deal.II/contrib/umfpack/AMD/Source/amdbar.f create mode 100644 deal.II/contrib/umfpack/Makefile create mode 100644 deal.II/contrib/umfpack/README create mode 100644 deal.II/contrib/umfpack/README.deal.II create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/ChangeLog create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/License create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/Makefile create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/QuickStart.pdf create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/QuickStart.tex create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.bib create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.pdf create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.sed1 create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.sed2 create mode 100644 deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.stex create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_col_to_triplet.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_defaults.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_free_numeric.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_free_symbolic.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_lunz.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_numeric.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_symbolic.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_load_numeric.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_load_symbolic.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_numeric.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_qsymbolic.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_control.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_info.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_matrix.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_numeric.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_perm.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_status.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_symbolic.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_triplet.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_vector.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_save_numeric.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_save_symbolic.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_scale.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_solve.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_symbolic.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_tictoc.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_timer.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_transpose.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_triplet_to_col.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Include/umfpack_wsolve.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/Contents.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/GNUmakefile create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/Makefile create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/lu_normest.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/luflop.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/luflopmex.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_btf.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_demo.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_demo.m.out create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_details.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_make.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_report.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_simple.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_simple.m.out create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_solve.m create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpackmex.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/MATLAB/west0067.mat create mode 100644 deal.II/contrib/umfpack/UMFPACK/README create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/Makefile create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_2by2.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_2by2.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_analyze.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_analyze.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_apply_order.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_apply_order.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_assemble.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_assemble.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_blas3_update.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_blas3_update.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_build_tuples.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_build_tuples.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_colamd.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_colamd.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_config.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_create_element.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_create_element.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_dump.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_dump.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_extend_front.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_extend_front.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_free.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_free.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_fsize.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_fsize.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_garbage_collection.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_garbage_collection.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_get_memory.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_get_memory.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_grow_front.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_grow_front.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_init_front.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_init_front.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_internal.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_is_permutation.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_is_permutation.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_init.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_init.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_wrapup.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_wrapup.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_local_search.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_local_search.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_lsolve.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_lsolve.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_ltsolve.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_ltsolve.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_malloc.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_malloc.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_element.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_element.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_head_block.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_head_block.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_tail_block.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_tail_block.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_free_tail_block.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_free_tail_block.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_init_memoryspace.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_init_memoryspace.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_realloc.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_realloc.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_report_perm.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_report_perm.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_report_vector.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_report_vector.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_row_search.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_row_search.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_scale.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_scale.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_scale_column.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_scale_column.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_set_stats.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_set_stats.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_singletons.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_singletons.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_solve.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_solve.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_start_front.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_start_front.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_store_lu.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_store_lu.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_symbolic_usage.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_symbolic_usage.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_transpose.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_transpose.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_triplet.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_triplet.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_tuple_lengths.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_tuple_lengths.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_usolve.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_usolve.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_utsolve.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_utsolve.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_numeric.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_numeric.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_symbolic.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_symbolic.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umf_version.h create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_col_to_triplet.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_defaults.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_free_numeric.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_free_symbolic.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_lunz.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_numeric.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_symbolic.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_load_numeric.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_load_symbolic.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_numeric.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_qsymbolic.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_control.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_info.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_matrix.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_numeric.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_perm.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_status.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_symbolic.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_triplet.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_vector.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_save_numeric.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_save_symbolic.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_scale.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_solve.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_symbolic.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_tictoc.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_timer.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_transpose.c create mode 100644 deal.II/contrib/umfpack/UMFPACK/Source/umfpack_triplet_to_col.c diff --git a/deal.II/contrib/umfpack/AMD/Demo/Makefile b/deal.II/contrib/umfpack/AMD/Demo/Makefile new file mode 100644 index 0000000000..395cc73375 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/Makefile @@ -0,0 +1,79 @@ +#------------------------------------------------------------------------------ +# compile the AMD demo (for both GNU make or original make) +#----------------------------------------------------------------------------- + +all: amd_simple amd_demo amd_demo2 + +include ../Make/Make.include + +C = $(CC) $(CFLAGS) $(CONFIG) -I../Include + +INC = ../Include/amd.h + +../Lib/libamd.a: + ( cd ../Source ; make ) + +../Lib/libamdf77.a: + ( cd ../Source ; make fortran ) + +#------------------------------------------------------------------------------ +# Create the demo program, run it, and compare the output +#------------------------------------------------------------------------------ + +dist: + +amd_demo: amd_demo.c ../Lib/libamd.a $(INC) + $(C) -DDINT -o amd_demo amd_demo.c ../Lib/libamd.a $(LIB) + ./amd_demo > my_amd_demo.out + - diff amd_demo.out my_amd_demo.out + +amd_demo2: amd_demo2.c ../Lib/libamd.a $(INC) + $(C) -DDINT -o amd_demo2 amd_demo2.c ../Lib/libamd.a $(LIB) + ./amd_demo2 > my_amd_demo2.out + - diff amd_demo2.out my_amd_demo2.out + +amd_simple: amd_simple.c ../Lib/libamd.a $(INC) + $(C) -DDINT -o amd_simple amd_simple.c ../Lib/libamd.a $(LIB) + ./amd_simple > my_amd_simple.out + - diff amd_simple.out my_amd_simple.out + +#------------------------------------------------------------------------------ +# compile the Fortran demo +#------------------------------------------------------------------------------ + +fortran: amd_f77demo amd_f77simple + +cross: amd_f77cross + +amd_f77demo: amd_f77demo.f ../Lib/libamdf77.a + $(F77) $(F77FLAGS) -o amd_f77demo amd_f77demo.f ../Lib/libamdf77.a \ + $(F77LIB) + ./amd_f77demo > my_amd_f77demo.out + - diff amd_f77demo.out my_amd_f77demo.out + +amd_f77simple: amd_f77simple.f ../Lib/libamdf77.a + $(F77) $(F77FLAGS) -o amd_f77simple amd_f77simple.f \ + ../Lib/libamdf77.a $(F77LIB) + ./amd_f77simple > my_amd_f77simple.out + - diff amd_f77simple.out my_amd_f77simple.out + +amd_f77wrapper.o: amd_f77wrapper.c + $(C) -DDINT -c amd_f77wrapper.c + +amd_f77cross: amd_f77cross.f amd_f77wrapper.o ../Lib/libamd.a + $(F77) $(F77FLAGS) -o amd_f77cross amd_f77cross.f amd_f77wrapper.o \ + ../Lib/libamd.a $(F77LIB) + ./amd_f77cross > my_amd_f77cross.out + - diff amd_f77cross.out my_amd_f77cross.out + +#------------------------------------------------------------------------------ +# Remove all but the files in the original distribution +#------------------------------------------------------------------------------ + +purge: clean + - $(RM) amd_demo my_amd_demo.out + - $(RM) amd_demo2 my_amd_demo2.out + - $(RM) amd_simple my_amd_simple.out + - $(RM) amd_f77demo my_amd_f77demo.out + - $(RM) amd_f77simple my_amd_f77simple.out + - $(RM) amd_f77cross my_amd_f77cross.out diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_demo.c b/deal.II/contrib/umfpack/AMD/Demo/amd_demo.c new file mode 100644 index 0000000000..009dc3d583 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_demo.c @@ -0,0 +1,169 @@ +/* ========================================================================= */ +/* === AMD demo main program =============================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* A simple C main program that illustrates the use of the ANSI C interface + * to AMD. + */ + +#include "amd.h" +#include +#include + +int main (int argc, char **argv) +{ + /* The symmetric can_24 Harwell/Boeing matrix, including upper and lower + * triangular parts, and the diagonal entries. Note that this matrix is + * 0-based, with row and column indices in the range 0 to n-1. */ + int n = 24, nz, + Ap [ ] = { 0, 9, 15, 21, 27, 33, 39, 48, 57, 61, 70, 76, 82, 88, 94, 100, + 106, 110, 119, 128, 137, 143, 152, 156, 160 }, + Ai [ ] = { + /* column 0: */ 0, 5, 6, 12, 13, 17, 18, 19, 21, + /* column 1: */ 1, 8, 9, 13, 14, 17, + /* column 2: */ 2, 6, 11, 20, 21, 22, + /* column 3: */ 3, 7, 10, 15, 18, 19, + /* column 4: */ 4, 7, 9, 14, 15, 16, + /* column 5: */ 0, 5, 6, 12, 13, 17, + /* column 6: */ 0, 2, 5, 6, 11, 12, 19, 21, 23, + /* column 7: */ 3, 4, 7, 9, 14, 15, 16, 17, 18, + /* column 8: */ 1, 8, 9, 14, + /* column 9: */ 1, 4, 7, 8, 9, 13, 14, 17, 18, + /* column 10: */ 3, 10, 18, 19, 20, 21, + /* column 11: */ 2, 6, 11, 12, 21, 23, + /* column 12: */ 0, 5, 6, 11, 12, 23, + /* column 13: */ 0, 1, 5, 9, 13, 17, + /* column 14: */ 1, 4, 7, 8, 9, 14, + /* column 15: */ 3, 4, 7, 15, 16, 18, + /* column 16: */ 4, 7, 15, 16, + /* column 17: */ 0, 1, 5, 7, 9, 13, 17, 18, 19, + /* column 18: */ 0, 3, 7, 9, 10, 15, 17, 18, 19, + /* column 19: */ 0, 3, 6, 10, 17, 18, 19, 20, 21, + /* column 20: */ 2, 10, 19, 20, 21, 22, + /* column 21: */ 0, 2, 6, 10, 11, 19, 20, 21, 22, + /* column 22: */ 2, 20, 21, 22, + /* column 23: */ 6, 11, 12, 23 } ; + + int P [24], Pinv [24], i, j, k, jnew, p, inew, result ; + double Control [AMD_CONTROL], Info [AMD_INFO] ; + char A [24][24] ; + + printf ("AMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24:\n") ; + + /* get the default parameters, and print them */ + amd_defaults (Control) ; + amd_control (Control) ; + + /* print the input matrix */ + nz = Ap [n] ; + printf ("\nInput matrix: %d-by-%d, with %d entries.\n" + " Note that for a symmetric matrix such as this one, only the\n" + " strictly lower or upper triangular parts would need to be\n" + " passed to AMD, since AMD computes the ordering of A+A'. The\n" + " diagonal entries are also not needed, since AMD ignores them.\n" + , n, n, nz) ; + for (j = 0 ; j < n ; j++) + { + printf ("\nColumn: %d, number of entries: %d, with row indices in" + " Ai [%d ... %d]:\n row indices:", + j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + printf (" %d", i) ; + } + printf ("\n") ; + } + + /* print a character plot of the input matrix. This is only reasonable + * because the matrix is small. */ + printf ("\nPlot of input matrix pattern:\n") ; + for (j = 0 ; j < n ; j++) + { + for (i = 0 ; i < n ; i++) A [i][j] = '.' ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + A [i][j] = 'X' ; + } + } + printf (" ") ; + for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; + printf ("\n") ; + for (i = 0 ; i < n ; i++) + { + printf ("%2d: ", i) ; + for (j = 0 ; j < n ; j++) + { + printf (" %c", A [i][j]) ; + } + printf ("\n") ; + } + + /* order the matrix */ + result = amd_order (n, Ap, Ai, P, Control, Info) ; + printf ("return value from amd_order: %d (should be %d)\n", + result, AMD_OK) ; + + /* print the statistics */ + amd_info (Info) ; + + if (result != AMD_OK) + { + printf ("AMD failed\n") ; + exit (1) ; + } + + /* print the permutation vector, P, and compute the inverse permutation */ + printf ("Permutation vector:\n") ; + for (k = 0 ; k < n ; k++) + { + /* row/column j is the kth row/column in the permuted matrix */ + j = P [k] ; + Pinv [j] = k ; + printf (" %2d", j) ; + } + printf ("\n\n") ; + + printf ("Inverse permutation vector:\n") ; + for (j = 0 ; j < n ; j++) + { + k = Pinv [j] ; + printf (" %2d", k) ; + } + printf ("\n\n") ; + + /* print a character plot of the permuted matrix. */ + printf ("\nPlot of permuted matrix pattern:\n") ; + for (jnew = 0 ; jnew < n ; jnew++) + { + j = P [jnew] ; + for (inew = 0 ; inew < n ; inew++) A [inew][jnew] = '.' ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + inew = Pinv [Ai [p]] ; + A [inew][jnew] = 'X' ; + } + } + printf (" ") ; + for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; + printf ("\n") ; + for (i = 0 ; i < n ; i++) + { + printf ("%2d: ", i) ; + for (j = 0 ; j < n ; j++) + { + printf (" %c", A [i][j]) ; + } + printf ("\n") ; + } + + return (0) ; +} diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_demo.out b/deal.II/contrib/umfpack/AMD/Demo/amd_demo.out new file mode 100644 index 0000000000..eb8d845bc1 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_demo.out @@ -0,0 +1,177 @@ +AMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24: + +amd: approximate minimum degree ordering, parameters: + dense row parameter: 10 + (rows with more than max (10 * sqrt (n), 16) entries are + considered "dense", and placed last in output permutation) + aggressive absorption: yes + + +Input matrix: 24-by-24, with 160 entries. + Note that for a symmetric matrix such as this one, only the + strictly lower or upper triangular parts would need to be + passed to AMD, since AMD computes the ordering of A+A'. The + diagonal entries are also not needed, since AMD ignores them. + +Column: 0, number of entries: 9, with row indices in Ai [0 ... 8]: + row indices: 0 5 6 12 13 17 18 19 21 + +Column: 1, number of entries: 6, with row indices in Ai [9 ... 14]: + row indices: 1 8 9 13 14 17 + +Column: 2, number of entries: 6, with row indices in Ai [15 ... 20]: + row indices: 2 6 11 20 21 22 + +Column: 3, number of entries: 6, with row indices in Ai [21 ... 26]: + row indices: 3 7 10 15 18 19 + +Column: 4, number of entries: 6, with row indices in Ai [27 ... 32]: + row indices: 4 7 9 14 15 16 + +Column: 5, number of entries: 6, with row indices in Ai [33 ... 38]: + row indices: 0 5 6 12 13 17 + +Column: 6, number of entries: 9, with row indices in Ai [39 ... 47]: + row indices: 0 2 5 6 11 12 19 21 23 + +Column: 7, number of entries: 9, with row indices in Ai [48 ... 56]: + row indices: 3 4 7 9 14 15 16 17 18 + +Column: 8, number of entries: 4, with row indices in Ai [57 ... 60]: + row indices: 1 8 9 14 + +Column: 9, number of entries: 9, with row indices in Ai [61 ... 69]: + row indices: 1 4 7 8 9 13 14 17 18 + +Column: 10, number of entries: 6, with row indices in Ai [70 ... 75]: + row indices: 3 10 18 19 20 21 + +Column: 11, number of entries: 6, with row indices in Ai [76 ... 81]: + row indices: 2 6 11 12 21 23 + +Column: 12, number of entries: 6, with row indices in Ai [82 ... 87]: + row indices: 0 5 6 11 12 23 + +Column: 13, number of entries: 6, with row indices in Ai [88 ... 93]: + row indices: 0 1 5 9 13 17 + +Column: 14, number of entries: 6, with row indices in Ai [94 ... 99]: + row indices: 1 4 7 8 9 14 + +Column: 15, number of entries: 6, with row indices in Ai [100 ... 105]: + row indices: 3 4 7 15 16 18 + +Column: 16, number of entries: 4, with row indices in Ai [106 ... 109]: + row indices: 4 7 15 16 + +Column: 17, number of entries: 9, with row indices in Ai [110 ... 118]: + row indices: 0 1 5 7 9 13 17 18 19 + +Column: 18, number of entries: 9, with row indices in Ai [119 ... 127]: + row indices: 0 3 7 9 10 15 17 18 19 + +Column: 19, number of entries: 9, with row indices in Ai [128 ... 136]: + row indices: 0 3 6 10 17 18 19 20 21 + +Column: 20, number of entries: 6, with row indices in Ai [137 ... 142]: + row indices: 2 10 19 20 21 22 + +Column: 21, number of entries: 9, with row indices in Ai [143 ... 151]: + row indices: 0 2 6 10 11 19 20 21 22 + +Column: 22, number of entries: 4, with row indices in Ai [152 ... 155]: + row indices: 2 20 21 22 + +Column: 23, number of entries: 4, with row indices in Ai [156 ... 159]: + row indices: 6 11 12 23 + +Plot of input matrix pattern: + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + 0: X . . . . X X . . . . . X X . . . X X X . X . . + 1: . X . . . . . . X X . . . X X . . X . . . . . . + 2: . . X . . . X . . . . X . . . . . . . . X X X . + 3: . . . X . . . X . . X . . . . X . . X X . . . . + 4: . . . . X . . X . X . . . . X X X . . . . . . . + 5: X . . . . X X . . . . . X X . . . X . . . . . . + 6: X . X . . X X . . . . X X . . . . . . X . X . X + 7: . . . X X . . X . X . . . . X X X X X . . . . . + 8: . X . . . . . . X X . . . . X . . . . . . . . . + 9: . X . . X . . X X X . . . X X . . X X . . . . . +10: . . . X . . . . . . X . . . . . . . X X X X . . +11: . . X . . . X . . . . X X . . . . . . . . X . X +12: X . . . . X X . . . . X X . . . . . . . . . . X +13: X X . . . X . . . X . . . X . . . X . . . . . . +14: . X . . X . . X X X . . . . X . . . . . . . . . +15: . . . X X . . X . . . . . . . X X . X . . . . . +16: . . . . X . . X . . . . . . . X X . . . . . . . +17: X X . . . X . X . X . . . X . . . X X X . . . . +18: X . . X . . . X . X X . . . . X . X X X . . . . +19: X . . X . . X . . . X . . . . . . X X X X X . . +20: . . X . . . . . . . X . . . . . . . . X X X X . +21: X . X . . . X . . . X X . . . . . . . X X X X . +22: . . X . . . . . . . . . . . . . . . . . X X X . +23: . . . . . . X . . . . X X . . . . . . . . . . X +return value from amd_order: 0 (should be 0) + +amd: approximate minimum degree ordering, results: + status: OK + n, dimension of A: 24 + nz, number of nonzeros in A: 160 + symmetry of A: 1.0000 + number of nonzeros on diagonal: 24 + nonzeros in pattern of A+A' (excl. diagonal): 136 + # dense rows/columns of A+A': 0 + memory used, in bytes: 1516 + # of memory compactions: 0 + + The following approximate statistics are for a subsequent + factorization of A(P,P) + A(P,P)'. They are slight upper + bounds if there are no dense rows/columns in A+A', and become + looser if dense rows/columns exist. + + nonzeros in L (excluding diagonal): 97 + nonzeros in L (including diagonal): 121 + # divide operations for LDL' or LU: 97 + # multiply-subtract operations for LDL': 275 + # multiply-subtract operations for LU: 453 + max nz. in any column of L (incl. diagonal): 8 + + chol flop count for real A, sqrt counted as 1 flop: 671 + LDL' flop count for real A: 647 + LDL' flop count for complex A: 3073 + LU flop count for real A (with no pivoting): 1003 + LU flop count for complex A (with no pivoting): 4497 + +Permutation vector: + 22 20 10 23 12 5 16 8 14 4 15 7 1 9 13 17 0 2 3 6 11 18 21 19 + +Inverse permutation vector: + 16 12 17 18 9 5 19 11 7 13 2 20 4 14 8 10 6 15 21 23 1 22 0 3 + + +Plot of permuted matrix pattern: + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + 0: X X . . . . . . . . . . . . . . . X . . . . X . + 1: X X X . . . . . . . . . . . . . . X . . . . X X + 2: . X X . . . . . . . . . . . . . . . X . . X X X + 3: . . . X X . . . . . . . . . . . . . . X X . . . + 4: . . . X X X . . . . . . . . . . X . . X X . . . + 5: . . . . X X . . . . . . . . X X X . . X . . . . + 6: . . . . . . X . . X X X . . . . . . . . . . . . + 7: . . . . . . . X X . . . X X . . . . . . . . . . + 8: . . . . . . . X X X . X X X . . . . . . . . . . + 9: . . . . . . X . X X X X . X . . . . . . . . . . +10: . . . . . . X . . X X X . . . . . . X . . X . . +11: . . . . . . X . X X X X . X . X . . X . . X . . +12: . . . . . . . X X . . . X X X X . . . . . . . . +13: . . . . . . . X X X . X X X X X . . . . . X . . +14: . . . . . X . . . . . . X X X X X . . . . . . . +15: . . . . . X . . . . . X X X X X X . . . . X . X +16: . . . . X X . . . . . . . . X X X . . X . X X X +17: X X . . . . . . . . . . . . . . . X . X X . X . +18: . . X . . . . . . . X X . . . . . . X . . X . X +19: . . . X X X . . . . . . . . . . X X . X X . X X +20: . . . X X . . . . . . . . . . . . X . X X . X . +21: . . X . . . . . . . X X . X . X X . X . . X . X +22: X X X . . . . . . . . . . . . . X X . X X . X X +23: . X X . . . . . . . . . . . . X X . X X . X X X diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_demo2.c b/deal.II/contrib/umfpack/AMD/Demo/amd_demo2.c new file mode 100644 index 0000000000..450d7c55ba --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_demo2.c @@ -0,0 +1,261 @@ +/* ========================================================================= */ +/* === AMD demo main program (jumbled matrix version) ====================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* A simple C main program that illustrates the use of the ANSI C interface + * to AMD. + * + * Identical to amd_demo.c, except that it operates on an input matrix that has + * unsorted columns and duplicate entries. + */ + +#include "amd.h" +#include +#include + +int main (int argc, char **argv) +{ + /* The symmetric can_24 Harwell/Boeing matrix (jumbled, and not symmetric). + * Since AMD operates on A+A', only A(i,j) or A(j,i) need to be specified, + * or both. The diagonal entries are optional (some are missing). + * There are many duplicate entries, which must be removed. */ + int n = 24, nz, + Ap [ ] = { 0, 9, 14, 20, 28, 33, 37, 44, 53, 58, 63, 63, 66, 69, 72, 75, + 78, 82, 86, 91, 97, 101, 112, 112, 116 }, + Ai [ ] = { + /* column 0: */ 0, 17, 18, 21, 5, 12, 5, 0, 13, + /* column 1: */ 14, 1, 8, 13, 17, + /* column 2: */ 2, 20, 11, 6, 11, 22, + /* column 3: */ 3, 3, 10, 7, 18, 18, 15, 19, + /* column 4: */ 7, 9, 15, 14, 16, + /* column 5: */ 5, 13, 6, 17, + /* column 6: */ 5, 0, 11, 6, 12, 6, 23, + /* column 7: */ 3, 4, 9, 7, 14, 16, 15, 17, 18, + /* column 8: */ 1, 9, 14, 14, 14, + /* column 9: */ 7, 13, 8, 1, 17, + /* column 10: */ + /* column 11: */ 2, 12, 23, + /* column 12: */ 5, 11, 12, + /* column 13: */ 0, 13, 17, + /* column 14: */ 1, 9, 14, + /* column 15: */ 3, 15, 16, + /* column 16: */ 16, 4, 4, 15, + /* column 17: */ 13, 17, 19, 17, + /* column 18: */ 15, 17, 19, 9, 10, + /* column 19: */ 17, 19, 20, 0, 6, 10, + /* column 20: */ 22, 10, 20, 21, + /* column 21: */ 6, 2, 10, 19, 20, 11, 21, 22, 22, 22, 22, + /* column 22: */ + /* column 23: */ 12, 11, 12, 23 } ; + + int Rp [25], Ri [116] ; + int P [24], Pinv [24], i, j, k, jnew, p, inew, result ; + double Control [AMD_CONTROL], Info [AMD_INFO] ; + char A [24][24] ; + + printf ("AMD demo, with a jumbled version of the 24-by-24\n") ; + printf ("Harwell/Boeing matrix, can_24:\n") ; + + /* get the default parameters, and print them */ + amd_defaults (Control) ; + amd_control (Control) ; + + /* print the input matrix */ + nz = Ap [n] ; + printf ("\nJumbled input matrix: %d-by-%d, with %d entries.\n" + " Note that for a symmetric matrix such as this one, only the\n" + " strictly lower or upper triangular parts would need to be\n" + " passed to AMD, since AMD computes the ordering of A+A'. The\n" + " diagonal entries are also not needed, since AMD ignores them.\n" + " This version of the matrix has jumbled columns and duplicate\n" + " row indices, and must be fixed by amd_preprocess prior to\n" + " ordering it with amd_order.\n" , n, n, nz) ; + for (j = 0 ; j < n ; j++) + { + printf ("\nColumn: %d, number of entries: %d, with row indices in" + " Ai [%d ... %d]:\n row indices:", + j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + printf (" %d", i) ; + } + printf ("\n") ; + } + + /* print a character plot of the input matrix. This is only reasonable + * because the matrix is small. */ + printf ("\nPlot of (jumbled) input matrix pattern:\n") ; + for (j = 0 ; j < n ; j++) + { + for (i = 0 ; i < n ; i++) A [i][j] = '.' ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + A [i][j] = 'X' ; + } + } + printf (" ") ; + for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; + printf ("\n") ; + for (i = 0 ; i < n ; i++) + { + printf ("%2d: ", i) ; + for (j = 0 ; j < n ; j++) + { + printf (" %c", A [i][j]) ; + } + printf ("\n") ; + } + + /* sort, remove duplicates, and transpose A to get R */ + result = amd_preprocess (n, Ap, Ai, Rp, Ri) ; + printf ("return value from amd_preprocess: %d (should be %d)\n", + result, AMD_OK) ; + + if (result != AMD_OK) + { + printf ("AMD failed\n") ; + exit (1) ; + } + + /* print the sorted/transposed matrix R */ + printf ("\nThe column-oriented form of the sorted/transposed matrix R:\n"); + for (j = 0 ; j < n ; j++) + { + printf ("\nColumn: %d, number of entries: %d, with row indices in" + " Ri [%d ... %d]:\n row indices:", + j, Rp [j+1] - Rp [j], Rp [j], Rp [j+1]-1) ; + for (p = Rp [j] ; p < Rp [j+1] ; p++) + { + i = Ri [p] ; + printf (" %d", i) ; + } + printf ("\n") ; + } + + /* print a character plot of the matrix R. */ + printf ("\nPlot of the sorted/transposed matrix R:\n") ; + for (j = 0 ; j < n ; j++) + { + for (i = 0 ; i < n ; i++) A [i][j] = '.' ; + for (p = Rp [j] ; p < Rp [j+1] ; p++) + { + i = Ri [p] ; + A [i][j] = 'X' ; + } + } + printf (" ") ; + for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; + printf (" \n") ; + for (i = 0 ; i < n ; i++) + { + printf ("%2d: ", i) ; + for (j = 0 ; j < n ; j++) + { + printf (" %c", A [i][j]) ; + } + printf (" \n") ; + } + + /* print a character plot of the matrix R+R'. */ + printf ("\nPlot of symmetric matrix to be ordered by amd_order:\n") ; + for (j = 0 ; j < n ; j++) + { + for (i = 0 ; i < n ; i++) A [i][j] = '.' ; + } + for (j = 0 ; j < n ; j++) + { + A [j][j] = 'X' ; + for (p = Rp [j] ; p < Rp [j+1] ; p++) + { + i = Ri [p] ; + A [i][j] = 'X' ; + A [j][i] = 'X' ; + } + } + printf (" ") ; + for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; + printf ("\n") ; + for (i = 0 ; i < n ; i++) + { + printf ("%2d: ", i) ; + for (j = 0 ; j < n ; j++) + { + printf (" %c", A [i][j]) ; + } + printf ("\n") ; + } + + /* order the matrix */ + result = amd_order (n, Rp, Ri, P, Control, Info) ; + printf ("return value from amd_order: %d (should be %d)\n", + result, AMD_OK) ; + + /* print the statistics */ + amd_info (Info) ; + + if (result != AMD_OK) + { + printf ("AMD failed\n") ; + exit (1) ; + } + + /* print the permutation vector, P, and compute the inverse permutation */ + printf ("Permutation vector:\n") ; + for (k = 0 ; k < n ; k++) + { + /* row/column j is the kth row/column in the permuted matrix */ + j = P [k] ; + Pinv [j] = k ; + printf (" %2d", j) ; + } + printf ("\n\n") ; + + printf ("Inverse permutation vector:\n") ; + for (j = 0 ; j < n ; j++) + { + k = Pinv [j] ; + printf (" %2d", k) ; + } + printf ("\n\n") ; + + /* print a character plot of the permuted matrix. */ + printf ("\nPlot of (symmetrized) permuted matrix pattern:\n") ; + for (j = 0 ; j < n ; j++) + { + for (i = 0 ; i < n ; i++) A [i][j] = '.' ; + } + for (jnew = 0 ; jnew < n ; jnew++) + { + j = P [jnew] ; + A [jnew][jnew] = 'X' ; + for (p = Rp [j] ; p < Rp [j+1] ; p++) + { + inew = Pinv [Ri [p]] ; + A [inew][jnew] = 'X' ; + A [jnew][inew] = 'X' ; + } + } + printf (" ") ; + for (j = 0 ; j < n ; j++) printf (" %1d", j % 10) ; + printf ("\n") ; + for (i = 0 ; i < n ; i++) + { + printf ("%2d: ", i) ; + for (j = 0 ; j < n ; j++) + { + printf (" %c", A [i][j]) ; + } + printf ("\n") ; + } + + return (0) ; +} diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_demo2.out b/deal.II/contrib/umfpack/AMD/Demo/amd_demo2.out new file mode 100644 index 0000000000..c03e8d5690 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_demo2.out @@ -0,0 +1,310 @@ +AMD demo, with a jumbled version of the 24-by-24 +Harwell/Boeing matrix, can_24: + +amd: approximate minimum degree ordering, parameters: + dense row parameter: 10 + (rows with more than max (10 * sqrt (n), 16) entries are + considered "dense", and placed last in output permutation) + aggressive absorption: yes + + +Jumbled input matrix: 24-by-24, with 116 entries. + Note that for a symmetric matrix such as this one, only the + strictly lower or upper triangular parts would need to be + passed to AMD, since AMD computes the ordering of A+A'. The + diagonal entries are also not needed, since AMD ignores them. + This version of the matrix has jumbled columns and duplicate + row indices, and must be fixed by amd_preprocess prior to + ordering it with amd_order. + +Column: 0, number of entries: 9, with row indices in Ai [0 ... 8]: + row indices: 0 17 18 21 5 12 5 0 13 + +Column: 1, number of entries: 5, with row indices in Ai [9 ... 13]: + row indices: 14 1 8 13 17 + +Column: 2, number of entries: 6, with row indices in Ai [14 ... 19]: + row indices: 2 20 11 6 11 22 + +Column: 3, number of entries: 8, with row indices in Ai [20 ... 27]: + row indices: 3 3 10 7 18 18 15 19 + +Column: 4, number of entries: 5, with row indices in Ai [28 ... 32]: + row indices: 7 9 15 14 16 + +Column: 5, number of entries: 4, with row indices in Ai [33 ... 36]: + row indices: 5 13 6 17 + +Column: 6, number of entries: 7, with row indices in Ai [37 ... 43]: + row indices: 5 0 11 6 12 6 23 + +Column: 7, number of entries: 9, with row indices in Ai [44 ... 52]: + row indices: 3 4 9 7 14 16 15 17 18 + +Column: 8, number of entries: 5, with row indices in Ai [53 ... 57]: + row indices: 1 9 14 14 14 + +Column: 9, number of entries: 5, with row indices in Ai [58 ... 62]: + row indices: 7 13 8 1 17 + +Column: 10, number of entries: 0, with row indices in Ai [63 ... 62]: + row indices: + +Column: 11, number of entries: 3, with row indices in Ai [63 ... 65]: + row indices: 2 12 23 + +Column: 12, number of entries: 3, with row indices in Ai [66 ... 68]: + row indices: 5 11 12 + +Column: 13, number of entries: 3, with row indices in Ai [69 ... 71]: + row indices: 0 13 17 + +Column: 14, number of entries: 3, with row indices in Ai [72 ... 74]: + row indices: 1 9 14 + +Column: 15, number of entries: 3, with row indices in Ai [75 ... 77]: + row indices: 3 15 16 + +Column: 16, number of entries: 4, with row indices in Ai [78 ... 81]: + row indices: 16 4 4 15 + +Column: 17, number of entries: 4, with row indices in Ai [82 ... 85]: + row indices: 13 17 19 17 + +Column: 18, number of entries: 5, with row indices in Ai [86 ... 90]: + row indices: 15 17 19 9 10 + +Column: 19, number of entries: 6, with row indices in Ai [91 ... 96]: + row indices: 17 19 20 0 6 10 + +Column: 20, number of entries: 4, with row indices in Ai [97 ... 100]: + row indices: 22 10 20 21 + +Column: 21, number of entries: 11, with row indices in Ai [101 ... 111]: + row indices: 6 2 10 19 20 11 21 22 22 22 22 + +Column: 22, number of entries: 0, with row indices in Ai [112 ... 111]: + row indices: + +Column: 23, number of entries: 4, with row indices in Ai [112 ... 115]: + row indices: 12 11 12 23 + +Plot of (jumbled) input matrix pattern: + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + 0: X . . . . . X . . . . . . X . . . . . X . . . . + 1: . X . . . . . . X X . . . . X . . . . . . . . . + 2: . . X . . . . . . . . X . . . . . . . . . X . . + 3: . . . X . . . X . . . . . . . X . . . . . . . . + 4: . . . . . . . X . . . . . . . . X . . . . . . . + 5: X . . . . X X . . . . . X . . . . . . . . . . . + 6: . . X . . X X . . . . . . . . . . . . X . X . . + 7: . . . X X . . X . X . . . . . . . . . . . . . . + 8: . X . . . . . . . X . . . . . . . . . . . . . . + 9: . . . . X . . X X . . . . . X . . . X . . . . . +10: . . . X . . . . . . . . . . . . . . X X X X . . +11: . . X . . . X . . . . . X . . . . . . . . X . X +12: X . . . . . X . . . . X X . . . . . . . . . . X +13: X X . . . X . . . X . . . X . . . X . . . . . . +14: . X . . X . . X X . . . . . X . . . . . . . . . +15: . . . X X . . X . . . . . . . X X . X . . . . . +16: . . . . X . . X . . . . . . . X X . . . . . . . +17: X X . . . X . X . X . . . X . . . X X X . . . . +18: X . . X . . . X . . . . . . . . . . . . . . . . +19: . . . X . . . . . . . . . . . . . X X X . X . . +20: . . X . . . . . . . . . . . . . . . . X X X . . +21: X . . . . . . . . . . . . . . . . . . . X X . . +22: . . X . . . . . . . . . . . . . . . . . X X . . +23: . . . . . . X . . . . X . . . . . . . . . . . X +return value from amd_preprocess: 0 (should be 0) + +The column-oriented form of the sorted/transposed matrix R: + +Column: 0, number of entries: 4, with row indices in Ri [0 ... 3]: + row indices: 0 6 13 19 + +Column: 1, number of entries: 4, with row indices in Ri [4 ... 7]: + row indices: 1 8 9 14 + +Column: 2, number of entries: 3, with row indices in Ri [8 ... 10]: + row indices: 2 11 21 + +Column: 3, number of entries: 3, with row indices in Ri [11 ... 13]: + row indices: 3 7 15 + +Column: 4, number of entries: 2, with row indices in Ri [14 ... 15]: + row indices: 7 16 + +Column: 5, number of entries: 4, with row indices in Ri [16 ... 19]: + row indices: 0 5 6 12 + +Column: 6, number of entries: 5, with row indices in Ri [20 ... 24]: + row indices: 2 5 6 19 21 + +Column: 7, number of entries: 4, with row indices in Ri [25 ... 28]: + row indices: 3 4 7 9 + +Column: 8, number of entries: 2, with row indices in Ri [29 ... 30]: + row indices: 1 9 + +Column: 9, number of entries: 5, with row indices in Ri [31 ... 35]: + row indices: 4 7 8 14 18 + +Column: 10, number of entries: 5, with row indices in Ri [36 ... 40]: + row indices: 3 18 19 20 21 + +Column: 11, number of entries: 5, with row indices in Ri [41 ... 45]: + row indices: 2 6 12 21 23 + +Column: 12, number of entries: 5, with row indices in Ri [46 ... 50]: + row indices: 0 6 11 12 23 + +Column: 13, number of entries: 6, with row indices in Ri [51 ... 56]: + row indices: 0 1 5 9 13 17 + +Column: 14, number of entries: 5, with row indices in Ri [57 ... 61]: + row indices: 1 4 7 8 14 + +Column: 15, number of entries: 6, with row indices in Ri [62 ... 67]: + row indices: 3 4 7 15 16 18 + +Column: 16, number of entries: 4, with row indices in Ri [68 ... 71]: + row indices: 4 7 15 16 + +Column: 17, number of entries: 9, with row indices in Ri [72 ... 80]: + row indices: 0 1 5 7 9 13 17 18 19 + +Column: 18, number of entries: 3, with row indices in Ri [81 ... 83]: + row indices: 0 3 7 + +Column: 19, number of entries: 5, with row indices in Ri [84 ... 88]: + row indices: 3 17 18 19 21 + +Column: 20, number of entries: 4, with row indices in Ri [89 ... 92]: + row indices: 2 19 20 21 + +Column: 21, number of entries: 3, with row indices in Ri [93 ... 95]: + row indices: 0 20 21 + +Column: 22, number of entries: 3, with row indices in Ri [96 ... 98]: + row indices: 2 20 21 + +Column: 23, number of entries: 3, with row indices in Ri [99 ... 101]: + row indices: 6 11 23 + +Plot of the sorted/transposed matrix R: + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + 0: X . . . . X . . . . . . X X . . . X X . . X . . + 1: . X . . . . . . X . . . . X X . . X . . . . . . + 2: . . X . . . X . . . . X . . . . . . . . X . X . + 3: . . . X . . . X . . X . . . . X . . X X . . . . + 4: . . . . . . . X . X . . . . X X X . . . . . . . + 5: . . . . . X X . . . . . . X . . . X . . . . . . + 6: X . . . . X X . . . . X X . . . . . . . . . . X + 7: . . . X X . . X . X . . . . X X X X X . . . . . + 8: . X . . . . . . . X . . . . X . . . . . . . . . + 9: . X . . . . . X X . . . . X . . . X . . . . . . +10: . . . . . . . . . . . . . . . . . . . . . . . . +11: . . X . . . . . . . . . X . . . . . . . . . . X +12: . . . . . X . . . . . X X . . . . . . . . . . . +13: X . . . . . . . . . . . . X . . . X . . . . . . +14: . X . . . . . . . X . . . . X . . . . . . . . . +15: . . . X . . . . . . . . . . . X X . . . . . . . +16: . . . . X . . . . . . . . . . X X . . . . . . . +17: . . . . . . . . . . . . . X . . . X . X . . . . +18: . . . . . . . . . X X . . . . X . X . X . . . . +19: X . . . . . X . . . X . . . . . . X . X X . . . +20: . . . . . . . . . . X . . . . . . . . . X X X . +21: . . X . . . X . . . X X . . . . . . . X X X X . +22: . . . . . . . . . . . . . . . . . . . . . . . . +23: . . . . . . . . . . . X X . . . . . . . . . . X + +Plot of symmetric matrix to be ordered by amd_order: + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + 0: X . . . . X X . . . . . X X . . . X X X . X . . + 1: . X . . . . . . X X . . . X X . . X . . . . . . + 2: . . X . . . X . . . . X . . . . . . . . X X X . + 3: . . . X . . . X . . X . . . . X . . X X . . . . + 4: . . . . X . . X . X . . . . X X X . . . . . . . + 5: X . . . . X X . . . . . X X . . . X . . . . . . + 6: X . X . . X X . . . . X X . . . . . . X . X . X + 7: . . . X X . . X . X . . . . X X X X X . . . . . + 8: . X . . . . . . X X . . . . X . . . . . . . . . + 9: . X . . X . . X X X . . . X X . . X X . . . . . +10: . . . X . . . . . . X . . . . . . . X X X X . . +11: . . X . . . X . . . . X X . . . . . . . . X . X +12: X . . . . X X . . . . X X . . . . . . . . . . X +13: X X . . . X . . . X . . . X . . . X . . . . . . +14: . X . . X . . X X X . . . . X . . . . . . . . . +15: . . . X X . . X . . . . . . . X X . X . . . . . +16: . . . . X . . X . . . . . . . X X . . . . . . . +17: X X . . . X . X . X . . . X . . . X X X . . . . +18: X . . X . . . X . X X . . . . X . X X X . . . . +19: X . . X . . X . . . X . . . . . . X X X X X . . +20: . . X . . . . . . . X . . . . . . . . X X X X . +21: X . X . . . X . . . X X . . . . . . . X X X X . +22: . . X . . . . . . . . . . . . . . . . . X X X . +23: . . . . . . X . . . . X X . . . . . . . . . . X +return value from amd_order: 0 (should be 0) + +amd: approximate minimum degree ordering, results: + status: OK + n, dimension of A: 24 + nz, number of nonzeros in A: 102 + symmetry of A: 0.4000 + number of nonzeros on diagonal: 17 + nonzeros in pattern of A+A' (excl. diagonal): 136 + # dense rows/columns of A+A': 0 + memory used, in bytes: 1516 + # of memory compactions: 0 + + The following approximate statistics are for a subsequent + factorization of A(P,P) + A(P,P)'. They are slight upper + bounds if there are no dense rows/columns in A+A', and become + looser if dense rows/columns exist. + + nonzeros in L (excluding diagonal): 97 + nonzeros in L (including diagonal): 121 + # divide operations for LDL' or LU: 97 + # multiply-subtract operations for LDL': 275 + # multiply-subtract operations for LU: 453 + max nz. in any column of L (incl. diagonal): 8 + + chol flop count for real A, sqrt counted as 1 flop: 671 + LDL' flop count for real A: 647 + LDL' flop count for complex A: 3073 + LU flop count for real A (with no pivoting): 1003 + LU flop count for complex A (with no pivoting): 4497 + +Permutation vector: + 22 20 10 23 12 5 16 8 14 4 15 7 1 9 13 17 0 2 3 6 11 18 21 19 + +Inverse permutation vector: + 16 12 17 18 9 5 19 11 7 13 2 20 4 14 8 10 6 15 21 23 1 22 0 3 + + +Plot of (symmetrized) permuted matrix pattern: + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + 0: X X . . . . . . . . . . . . . . . X . . . . X . + 1: X X X . . . . . . . . . . . . . . X . . . . X X + 2: . X X . . . . . . . . . . . . . . . X . . X X X + 3: . . . X X . . . . . . . . . . . . . . X X . . . + 4: . . . X X X . . . . . . . . . . X . . X X . . . + 5: . . . . X X . . . . . . . . X X X . . X . . . . + 6: . . . . . . X . . X X X . . . . . . . . . . . . + 7: . . . . . . . X X . . . X X . . . . . . . . . . + 8: . . . . . . . X X X . X X X . . . . . . . . . . + 9: . . . . . . X . X X X X . X . . . . . . . . . . +10: . . . . . . X . . X X X . . . . . . X . . X . . +11: . . . . . . X . X X X X . X . X . . X . . X . . +12: . . . . . . . X X . . . X X X X . . . . . . . . +13: . . . . . . . X X X . X X X X X . . . . . X . . +14: . . . . . X . . . . . . X X X X X . . . . . . . +15: . . . . . X . . . . . X X X X X X . . . . X . X +16: . . . . X X . . . . . . . . X X X . . X . X X X +17: X X . . . . . . . . . . . . . . . X . X X . X . +18: . . X . . . . . . . X X . . . . . . X . . X . X +19: . . . X X X . . . . . . . . . . X X . X X . X X +20: . . . X X . . . . . . . . . . . . X . X X . X . +21: . . X . . . . . . . X X . X . X X . X . . X . X +22: X X X . . . . . . . . . . . . . X X . X X . X X +23: . X X . . . . . . . . . . . . X X . X X . X X X diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_f77cross.f b/deal.II/contrib/umfpack/AMD/Demo/amd_f77cross.f new file mode 100644 index 0000000000..ddbfd00268 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_f77cross.f @@ -0,0 +1,51 @@ +C ====================================================================== +C === AMD_cross ======================================================== +C ====================================================================== + +C ---------------------------------------------------------------------- +C AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. +C Davis, Patrick R. Amestoy, and Iain S. Duff. See ../README for +C License. email: davis@cise.ufl.edu CISE Department, Univ. of +C Florida. web: http://www.cise.ufl.edu/research/sparse/amd +C ---------------------------------------------------------------------- + +C This program provides an example of how to call the C version of AMD +C from a Fortran program. It is HIGHLY non-portable. + +C The amd_order routine returns PERM (1) < 0 if an error occurs. +C (-1: out of memory, -2: invalid matrix) + +C Note that the input matrix is 0-based. From Fortran, column j of the +C matrix is in AI (AP (I)+1 ... AP (I+1)). The row indices in this +C set are in the range 0 to N-1. To demonstrate this translation, +C the input matrix is printed in 1-based form. This program uses +C the same 5-by-5 test matrix as amd_simple.c. + + INTEGER N, NZ, K, P + PARAMETER (N = 5, NZ = 14) + INTEGER AP (N+1), AI (NZ), PERM (N) + DATA AP / 0, 2, 6, 10, 12, 14 / + DATA AI / 0,1, 0,1,2,4, 1,2,3,4, 2,3, 1,4 / + DOUBLE PRECISION CONTROL (5), INFO (20) + +C print the input matrix + PRINT 10, N, N, NZ +10 FORMAT ('Input matrix:', I2, '-by-', I2, ' with',I3,' entries') + DO 40 J = 1, N + PRINT 20, J, AP (J+1) - AP (J), AP (J)+1, AP (J+1) +20 FORMAT ( /, 'Column: ', I2, ' number of entries: ', I2, + $ ' with row indices in AI (', I3, ' ... ', I3, ')') + PRINT 30, ((AI (P) + 1), P = AP (J) + 1, AP (J+1)) +30 FORMAT (' row indices: ', 24I3) +40 CONTINUE + + CALL AMDDEFAULTS (CONTROL) + CALL AMDORDER (N, AP, AI, PERM, CONTROL, INFO) + CALL AMDINFO (INFO) + + DO 60 K = 1, N + PRINT 50, K, PERM (K) + 1 +50 FORMAT ('PERM (',I2,') = ', I2) +60 CONTINUE + END + diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_f77cross.out b/deal.II/contrib/umfpack/AMD/Demo/amd_f77cross.out new file mode 100644 index 0000000000..6d8a670da5 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_f77cross.out @@ -0,0 +1,51 @@ +Input matrix: 5-by- 5 with 14 entries + +Column: 1 number of entries: 2 with row indices in AI ( 1 ... 2) + row indices: 1 2 + +Column: 2 number of entries: 4 with row indices in AI ( 3 ... 6) + row indices: 1 2 3 5 + +Column: 3 number of entries: 4 with row indices in AI ( 7 ... 10) + row indices: 2 3 4 5 + +Column: 4 number of entries: 2 with row indices in AI ( 11 ... 12) + row indices: 3 4 + +Column: 5 number of entries: 2 with row indices in AI ( 13 ... 14) + row indices: 2 5 + +amd: approximate minimum degree ordering, results: + status: OK + n, dimension of A: 5 + nz, number of nonzeros in A: 14 + symmetry of A: 0.8889 + number of nonzeros on diagonal: 5 + nonzeros in pattern of A+A' (excl. diagonal): 10 + # dense rows/columns of A+A': 0 + memory used, in bytes: 228 + # of memory compactions: 0 + + The following approximate statistics are for a subsequent + factorization of A(P,P) + A(P,P)'. They are slight upper + bounds if there are no dense rows/columns in A+A', and become + looser if dense rows/columns exist. + + nonzeros in L (excluding diagonal): 5 + nonzeros in L (including diagonal): 10 + # divide operations for LDL' or LU: 5 + # multiply-subtract operations for LDL': 6 + # multiply-subtract operations for LU: 7 + max nz. in any column of L (incl. diagonal): 3 + + chol flop count for real A, sqrt counted as 1 flop: 22 + LDL' flop count for real A: 17 + LDL' flop count for complex A: 93 + LU flop count for real A (with no pivoting): 19 + LU flop count for complex A (with no pivoting): 101 + +PERM ( 1) = 1 +PERM ( 2) = 4 +PERM ( 3) = 3 +PERM ( 4) = 5 +PERM ( 5) = 2 diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_f77demo.f b/deal.II/contrib/umfpack/AMD/Demo/amd_f77demo.f new file mode 100644 index 0000000000..3ed47ab206 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_f77demo.f @@ -0,0 +1,161 @@ +C ====================================================================== +C === Fortran AMD demo main program ==================================== +C ====================================================================== + +C ---------------------------------------------------------------------- +C AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. +C Davis, Patrick R. Amestoy, and Iain S. Duff. See ../README for +C License. email: davis@cise.ufl.edu CISE Department, Univ. of +C Florida. web: http://www.cise.ufl.edu/research/sparse/amd +C ---------------------------------------------------------------------- + +C A simple Fortran 77 main program that illustrates the use of the +C Fortran version of AMD (both the AMD and AMDBAR routines). Note +C that aggressive absorption has no effect on this particular matrix. + +C AP and AI contain the symmetric can_24 Harwell/Boeing matrix, +C including upper and lower triangular parts, but excluding the +C diagonal entries. Note that this matrix is 1-based, with row +C and column indices in the range 1 to N. + + INTEGER N, NZ, IWLEN, PFREE, I, J, K, JNEW, P, INEW, + $ METHOD, NCMPA + PARAMETER (N = 24, NZ = 136, IWLEN = 200) + INTEGER PE (N), DEGREE (N), NV (N), NEXT (N), PERM (N), W (N), + $ HEAD (N), PINV (N), LEN (N), AP (N+1), AI (NZ), IW (IWLEN) + CHARACTER A (24,24) + + DATA AP + $ / 1, 9, 14, 19, 24, 29, 34, 42, 50, 53, 61, 66, 71, + $ 76, 81, 86, 91, 94, 102, 110, 118, 123, 131, 134, 137 / + DATA AI / + $ 6, 7, 13, 14, 18, 19, 20, 22, + $ 9, 10, 14, 15, 18, + $ 7, 12, 21, 22, 23, + $ 8, 11, 16, 19, 20, + $ 8, 10, 15, 16, 17, + $ 1, 7, 13, 14, 18, + $ 1, 3, 6, 12, 13, 20, 22, 24, + $ 4, 5, 10, 15, 16, 17, 18, 19, + $ 2, 10, 15, + $ 2, 5, 8, 9, 14, 15, 18, 19, + $ 4, 19, 20, 21, 22, + $ 3, 7, 13, 22, 24, + $ 1, 6, 7, 12, 24, + $ 1, 2, 6, 10, 18, + $ 2, 5, 8, 9, 10, + $ 4, 5, 8, 17, 19, + $ 5, 8, 16, + $ 1, 2, 6, 8, 10, 14, 19, 20, + $ 1, 4, 8, 10, 11, 16, 18, 20, + $ 1, 4, 7, 11, 18, 19, 21, 22, + $ 3, 11, 20, 22, 23, + $ 1, 3, 7, 11, 12, 20, 21, 23, + $ 3, 21, 22, + $ 7, 12, 13 / + +C print the input matrix + PRINT 11, N, N, NZ +11 FORMAT ('AMD Fortran 77 demo, with the 24-by-24', + $ ' Harwell/Boeing matrix, can_24:' + $ /, 'Input matrix: ', I2, '-by-', I2,' with ',I3,' entries', + $ /, 'Note that the Fortran version of AMD requires that' + $ /, 'no diagonal entries be present.') + DO 20 J = 1, N + PRINT 21, J, AP (J+1) - AP (J), AP (J), AP (J+1)-1 +21 FORMAT ( /, 'Column: ', I2, ' number of entries: ', I2, + $ ' with row indices in AI (', I3, ' ... ', I3, ')') + PRINT 10, ((AI (P)), P = AP (J), AP (J+1) - 1) +10 FORMAT (' row indices: ', 24I3) +20 CONTINUE + +C print a character plot of the input matrix. This is only +C reasonable because the matrix is small. + PRINT 31 +31 FORMAT ('Plot of input matrix pattern:') + DO 50 J = 1,N + DO 30 I = 1,N + A (I, J) = '.' +30 CONTINUE +C add the diagonal entry to the plot + A (J, J) = 'X' + DO 40 P = AP (J), AP (J+1) - 1 + I = AI (P) + A (I, J) = 'X' +40 CONTINUE +50 CONTINUE + PRINT 60, ((MOD (J, 10)), J = 1,N) +60 FORMAT (' ', 24I2) + DO 80 I = 1,N + PRINT 70, I, (A (I, J), J = 1,N) +70 FORMAT (' ', I2, ': ', 24A2) +80 CONTINUE + + DO 190 METHOD = 1,2 + +C load the matrix into AMD's workspace + DO 90 J = 1,N + PE (J) = AP (J) + LEN (J) = AP (J+1) - AP (J) +90 CONTINUE + DO 100 P = 1,NZ + IW (P) = AI (P) +100 CONTINUE + PFREE = NZ + 1 + +C order the matrix using AMD or AMDBAR + IF (METHOD .EQ. 1) THEN + PRINT 101 +101 FORMAT (/, '------------------------------------------', + $ /, 'ordering the matrix with AMD', + $ /, '------------------------------------------') + CALL AMD (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, + $ PERM, HEAD, PINV, DEGREE, NCMPA, W) + ELSE + PRINT 102 +102 FORMAT (/, '------------------------------------------', + $ /, 'ordering the matrix with AMDBAR', + $ /, '------------------------------------------') + CALL AMDBAR (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, + $ PERM, HEAD, PINV, DEGREE, NCMPA, W) + ENDIF + +C print the permutation vector, PERM, and its inverse, PINV. +C row/column J = PERM (K) is the Kth row/column in the +C permuted matrix. + PRINT 110, (PERM (K), K = 1,N) +110 FORMAT (/, 'Permutation vector: ', /, 24I3) + PRINT 120, (PINV (J), J = 1,N) +120 FORMAT (/, 'Inverse permutation vector: ', /, 24I3) + +C print a character plot of the permuted matrix. + PRINT 121 +121 FORMAT ('Plot of permuted matrix pattern:') + DO 150 JNEW = 1,N + J = PERM (JNEW) + DO 130 INEW = 1,N + A (INEW, JNEW) = '.' +130 CONTINUE +C add the diagonal entry to the plot + A (JNEW, JNEW) = 'X' + DO 140 P = AP (J), AP (J+1) - 1 + INEW = PINV (AI (P)) + A (INEW, JNEW) = 'X' +140 CONTINUE +150 CONTINUE + PRINT 60, ((MOD (J, 10)), J = 1,N) + DO 160 I = 1,N + PRINT 70, I, (A (I, J), J = 1,N) +160 CONTINUE + +C print the permuted matrix, PERM*A*PERM' + DO 180 JNEW = 1,N + J = PERM (JNEW) + PRINT 171, JNEW, J, AP (J+1) - AP (J) +171 FORMAT (/, 'New column: ', I2, ' old column: ', I2, + $ ' number of entries: ', I2) + PRINT 170, (PINV (AI (P)), P = AP (J), AP (J+1) - 1) +170 FORMAT (' new row indices: ', 24I3) +180 CONTINUE +190 CONTINUE + END diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_f77demo.out b/deal.II/contrib/umfpack/AMD/Demo/amd_f77demo.out new file mode 100644 index 0000000000..10758dd511 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_f77demo.out @@ -0,0 +1,318 @@ +AMD Fortran 77 demo, with the 24-by-24 Harwell/Boeing matrix, can_24: +Input matrix: 24-by-24 with 136 entries +Note that the Fortran version of AMD requires that +no diagonal entries be present. + +Column: 1 number of entries: 8 with row indices in AI ( 1 ... 8) + row indices: 6 7 13 14 18 19 20 22 + +Column: 2 number of entries: 5 with row indices in AI ( 9 ... 13) + row indices: 9 10 14 15 18 + +Column: 3 number of entries: 5 with row indices in AI ( 14 ... 18) + row indices: 7 12 21 22 23 + +Column: 4 number of entries: 5 with row indices in AI ( 19 ... 23) + row indices: 8 11 16 19 20 + +Column: 5 number of entries: 5 with row indices in AI ( 24 ... 28) + row indices: 8 10 15 16 17 + +Column: 6 number of entries: 5 with row indices in AI ( 29 ... 33) + row indices: 1 7 13 14 18 + +Column: 7 number of entries: 8 with row indices in AI ( 34 ... 41) + row indices: 1 3 6 12 13 20 22 24 + +Column: 8 number of entries: 8 with row indices in AI ( 42 ... 49) + row indices: 4 5 10 15 16 17 18 19 + +Column: 9 number of entries: 3 with row indices in AI ( 50 ... 52) + row indices: 2 10 15 + +Column: 10 number of entries: 8 with row indices in AI ( 53 ... 60) + row indices: 2 5 8 9 14 15 18 19 + +Column: 11 number of entries: 5 with row indices in AI ( 61 ... 65) + row indices: 4 19 20 21 22 + +Column: 12 number of entries: 5 with row indices in AI ( 66 ... 70) + row indices: 3 7 13 22 24 + +Column: 13 number of entries: 5 with row indices in AI ( 71 ... 75) + row indices: 1 6 7 12 24 + +Column: 14 number of entries: 5 with row indices in AI ( 76 ... 80) + row indices: 1 2 6 10 18 + +Column: 15 number of entries: 5 with row indices in AI ( 81 ... 85) + row indices: 2 5 8 9 10 + +Column: 16 number of entries: 5 with row indices in AI ( 86 ... 90) + row indices: 4 5 8 17 19 + +Column: 17 number of entries: 3 with row indices in AI ( 91 ... 93) + row indices: 5 8 16 + +Column: 18 number of entries: 8 with row indices in AI ( 94 ... 101) + row indices: 1 2 6 8 10 14 19 20 + +Column: 19 number of entries: 8 with row indices in AI (102 ... 109) + row indices: 1 4 8 10 11 16 18 20 + +Column: 20 number of entries: 8 with row indices in AI (110 ... 117) + row indices: 1 4 7 11 18 19 21 22 + +Column: 21 number of entries: 5 with row indices in AI (118 ... 122) + row indices: 3 11 20 22 23 + +Column: 22 number of entries: 8 with row indices in AI (123 ... 130) + row indices: 1 3 7 11 12 20 21 23 + +Column: 23 number of entries: 3 with row indices in AI (131 ... 133) + row indices: 3 21 22 + +Column: 24 number of entries: 3 with row indices in AI (134 ... 136) + row indices: 7 12 13 +Plot of input matrix pattern: + 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 + 1: X . . . . X X . . . . . X X . . . X X X . X . . + 2: . X . . . . . . X X . . . X X . . X . . . . . . + 3: . . X . . . X . . . . X . . . . . . . . X X X . + 4: . . . X . . . X . . X . . . . X . . X X . . . . + 5: . . . . X . . X . X . . . . X X X . . . . . . . + 6: X . . . . X X . . . . . X X . . . X . . . . . . + 7: X . X . . X X . . . . X X . . . . . . X . X . X + 8: . . . X X . . X . X . . . . X X X X X . . . . . + 9: . X . . . . . . X X . . . . X . . . . . . . . . + 10: . X . . X . . X X X . . . X X . . X X . . . . . + 11: . . . X . . . . . . X . . . . . . . X X X X . . + 12: . . X . . . X . . . . X X . . . . . . . . X . X + 13: X . . . . X X . . . . X X . . . . . . . . . . X + 14: X X . . . X . . . X . . . X . . . X . . . . . . + 15: . X . . X . . X X X . . . . X . . . . . . . . . + 16: . . . X X . . X . . . . . . . X X . X . . . . . + 17: . . . . X . . X . . . . . . . X X . . . . . . . + 18: X X . . . X . X . X . . . X . . . X X X . . . . + 19: X . . X . . . X . X X . . . . X . X X X . . . . + 20: X . . X . . X . . . X . . . . . . X X X X X . . + 21: . . X . . . . . . . X . . . . . . . . X X X X . + 22: X . X . . . X . . . X X . . . . . . . X X X X . + 23: . . X . . . . . . . . . . . . . . . . . X X X . + 24: . . . . . . X . . . . X X . . . . . . . . . . X + +------------------------------------------ +ordering the matrix with AMD +------------------------------------------ + +Permutation vector: + 24 23 17 9 15 5 21 13 6 11 16 8 2 10 14 18 1 3 4 19 7 12 22 20 + +Inverse permutation vector: + 17 13 18 19 6 9 21 12 4 14 10 22 8 15 5 11 3 16 20 24 7 23 2 1 +Plot of permuted matrix pattern: + 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 + 1: X . . . . . . X . . . . . . . . . . . . X X . . + 2: . X . . . . X . . . . . . . . . . X . . . . X . + 3: . . X . . X . . . . X X . . . . . . . . . . . . + 4: . . . X X . . . . . . . X X . . . . . . . . . . + 5: . . . X X X . . . . . X X X . . . . . . . . . . + 6: . . X . X X . . . . X X . X . . . . . . . . . . + 7: . X . . . . X . . X . . . . . . . X . . . . X X + 8: X . . . . . . X X . . . . . . . X . . . X X . . + 9: . . . . . . . X X . . . . . X X X . . . X . . . + 10: . . . . . . X . . X . . . . . . . . X X . . X X + 11: . . X . . X . . . . X X . . . . . . X X . . . . + 12: . . X . X X . . . . X X . X . X . . X X . . . . + 13: . . . X X . . . . . . . X X X X . . . . . . . . + 14: . . . X X X . . . . . X X X X X . . . X . . . . + 15: . . . . . . . . X . . . X X X X X . . . . . . . + 16: . . . . . . . . X . . X X X X X X . . X . . . X + 17: . . . . . . . X X . . . . . X X X . . X X . X X + 18: . X . . . . X . . . . . . . . . . X . . X X X . + 19: . . . . . . . . . X X X . . . . . . X X . . . X + 20: . . . . . . . . . X X X . X . X X . X X . . . X + 21: X . . . . . . X X . . . . . . . X X . . X X X X + 22: X . . . . . . X . . . . . . . . . X . . X X X . + 23: . X . . . . X . . X . . . . . . X X . . X X X X + 24: . . . . . . X . . X . . . . . X X . X X X . X X + +New column: 1 old column: 24 number of entries: 3 + new row indices: 21 22 8 + +New column: 2 old column: 23 number of entries: 3 + new row indices: 18 7 23 + +New column: 3 old column: 17 number of entries: 3 + new row indices: 6 12 11 + +New column: 4 old column: 9 number of entries: 3 + new row indices: 13 14 5 + +New column: 5 old column: 15 number of entries: 5 + new row indices: 13 6 12 4 14 + +New column: 6 old column: 5 number of entries: 5 + new row indices: 12 14 5 11 3 + +New column: 7 old column: 21 number of entries: 5 + new row indices: 18 10 24 23 2 + +New column: 8 old column: 13 number of entries: 5 + new row indices: 17 9 21 22 1 + +New column: 9 old column: 6 number of entries: 5 + new row indices: 17 21 8 15 16 + +New column: 10 old column: 11 number of entries: 5 + new row indices: 19 20 24 7 23 + +New column: 11 old column: 16 number of entries: 5 + new row indices: 19 6 12 3 20 + +New column: 12 old column: 8 number of entries: 8 + new row indices: 19 6 14 5 11 3 16 20 + +New column: 13 old column: 2 number of entries: 5 + new row indices: 4 14 15 5 16 + +New column: 14 old column: 10 number of entries: 8 + new row indices: 13 6 12 4 15 5 16 20 + +New column: 15 old column: 14 number of entries: 5 + new row indices: 17 13 9 14 16 + +New column: 16 old column: 18 number of entries: 8 + new row indices: 17 13 9 12 14 15 20 24 + +New column: 17 old column: 1 number of entries: 8 + new row indices: 9 21 8 15 16 20 24 23 + +New column: 18 old column: 3 number of entries: 5 + new row indices: 21 22 7 23 2 + +New column: 19 old column: 4 number of entries: 5 + new row indices: 12 10 11 20 24 + +New column: 20 old column: 19 number of entries: 8 + new row indices: 17 19 12 14 10 11 16 24 + +New column: 21 old column: 7 number of entries: 8 + new row indices: 17 18 9 22 8 24 23 1 + +New column: 22 old column: 12 number of entries: 5 + new row indices: 18 21 8 23 1 + +New column: 23 old column: 22 number of entries: 8 + new row indices: 17 18 21 10 22 24 7 2 + +New column: 24 old column: 20 number of entries: 8 + new row indices: 17 19 21 10 16 20 7 23 + +------------------------------------------ +ordering the matrix with AMDBAR +------------------------------------------ + +Permutation vector: + 24 23 17 9 15 5 21 13 6 11 16 8 2 10 14 18 1 3 4 19 7 12 22 20 + +Inverse permutation vector: + 17 13 18 19 6 9 21 12 4 14 10 22 8 15 5 11 3 16 20 24 7 23 2 1 +Plot of permuted matrix pattern: + 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 + 1: X . . . . . . X . . . . . . . . . . . . X X . . + 2: . X . . . . X . . . . . . . . . . X . . . . X . + 3: . . X . . X . . . . X X . . . . . . . . . . . . + 4: . . . X X . . . . . . . X X . . . . . . . . . . + 5: . . . X X X . . . . . X X X . . . . . . . . . . + 6: . . X . X X . . . . X X . X . . . . . . . . . . + 7: . X . . . . X . . X . . . . . . . X . . . . X X + 8: X . . . . . . X X . . . . . . . X . . . X X . . + 9: . . . . . . . X X . . . . . X X X . . . X . . . + 10: . . . . . . X . . X . . . . . . . . X X . . X X + 11: . . X . . X . . . . X X . . . . . . X X . . . . + 12: . . X . X X . . . . X X . X . X . . X X . . . . + 13: . . . X X . . . . . . . X X X X . . . . . . . . + 14: . . . X X X . . . . . X X X X X . . . X . . . . + 15: . . . . . . . . X . . . X X X X X . . . . . . . + 16: . . . . . . . . X . . X X X X X X . . X . . . X + 17: . . . . . . . X X . . . . . X X X . . X X . X X + 18: . X . . . . X . . . . . . . . . . X . . X X X . + 19: . . . . . . . . . X X X . . . . . . X X . . . X + 20: . . . . . . . . . X X X . X . X X . X X . . . X + 21: X . . . . . . X X . . . . . . . X X . . X X X X + 22: X . . . . . . X . . . . . . . . . X . . X X X . + 23: . X . . . . X . . X . . . . . . X X . . X X X X + 24: . . . . . . X . . X . . . . . X X . X X X . X X + +New column: 1 old column: 24 number of entries: 3 + new row indices: 21 22 8 + +New column: 2 old column: 23 number of entries: 3 + new row indices: 18 7 23 + +New column: 3 old column: 17 number of entries: 3 + new row indices: 6 12 11 + +New column: 4 old column: 9 number of entries: 3 + new row indices: 13 14 5 + +New column: 5 old column: 15 number of entries: 5 + new row indices: 13 6 12 4 14 + +New column: 6 old column: 5 number of entries: 5 + new row indices: 12 14 5 11 3 + +New column: 7 old column: 21 number of entries: 5 + new row indices: 18 10 24 23 2 + +New column: 8 old column: 13 number of entries: 5 + new row indices: 17 9 21 22 1 + +New column: 9 old column: 6 number of entries: 5 + new row indices: 17 21 8 15 16 + +New column: 10 old column: 11 number of entries: 5 + new row indices: 19 20 24 7 23 + +New column: 11 old column: 16 number of entries: 5 + new row indices: 19 6 12 3 20 + +New column: 12 old column: 8 number of entries: 8 + new row indices: 19 6 14 5 11 3 16 20 + +New column: 13 old column: 2 number of entries: 5 + new row indices: 4 14 15 5 16 + +New column: 14 old column: 10 number of entries: 8 + new row indices: 13 6 12 4 15 5 16 20 + +New column: 15 old column: 14 number of entries: 5 + new row indices: 17 13 9 14 16 + +New column: 16 old column: 18 number of entries: 8 + new row indices: 17 13 9 12 14 15 20 24 + +New column: 17 old column: 1 number of entries: 8 + new row indices: 9 21 8 15 16 20 24 23 + +New column: 18 old column: 3 number of entries: 5 + new row indices: 21 22 7 23 2 + +New column: 19 old column: 4 number of entries: 5 + new row indices: 12 10 11 20 24 + +New column: 20 old column: 19 number of entries: 8 + new row indices: 17 19 12 14 10 11 16 24 + +New column: 21 old column: 7 number of entries: 8 + new row indices: 17 18 9 22 8 24 23 1 + +New column: 22 old column: 12 number of entries: 5 + new row indices: 18 21 8 23 1 + +New column: 23 old column: 22 number of entries: 8 + new row indices: 17 18 21 10 22 24 7 2 + +New column: 24 old column: 20 number of entries: 8 + new row indices: 17 19 21 10 16 20 7 23 diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_f77simple.f b/deal.II/contrib/umfpack/AMD/Demo/amd_f77simple.f new file mode 100644 index 0000000000..ef189fb416 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_f77simple.f @@ -0,0 +1,38 @@ +C ---------------------------------------------------------------------- +C AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. +C Davis, Patrick R. Amestoy, and Iain S. Duff. See ../README for +C License. email: davis@cise.ufl.edu CISE Department, Univ. of +C Florida. web: http://www.cise.ufl.edu/research/sparse/amd +C ---------------------------------------------------------------------- + +C This program provides an example of how to call the Fortran version +C of AMD. It uses the same matrix as the amd_simple.c demo (in C). +C Note that the diagonal entries are not present, and the matrix is +C symmetric. + + INTEGER N, NZ, J, K, P, IWLEN, PFREE, NCMPA + PARAMETER (N = 5, NZ = 10, IWLEN = 17) + INTEGER AP (N+1), AI (NZ), LAST (N), PE (N), LEN (N), ELEN (N), + $ IW (IWLEN), DEGREE (N), NV (N), NEXT (N), HEAD (N), W (N) + DATA AP / 1, 2, 5, 8, 9, 11/ + DATA AI / 2, 1,3,5, 2,4,5, 3, 2,3 / + +C load the matrix into the AMD workspace + DO 10 J = 1,N + PE (J) = AP (J) + LEN (J) = AP (J+1) - AP (J) +10 CONTINUE + DO 20 P = 1,NZ + IW (P) = AI (P) +20 CONTINUE + PFREE = NZ + 1 + +C order the matrix (destroys the copy of A in IW, PE, and LEN) + CALL AMD (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, LAST, HEAD, + $ ELEN, DEGREE, NCMPA, W) + + DO 60 K = 1, N + PRINT 50, K, LAST (K) +50 FORMAT ('P (',I2,') = ', I2) +60 CONTINUE + END diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_f77simple.out b/deal.II/contrib/umfpack/AMD/Demo/amd_f77simple.out new file mode 100644 index 0000000000..c7f902e0a4 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_f77simple.out @@ -0,0 +1,5 @@ +P ( 1) = 4 +P ( 2) = 1 +P ( 3) = 3 +P ( 4) = 5 +P ( 5) = 2 diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_f77wrapper.c b/deal.II/contrib/umfpack/AMD/Demo/amd_f77wrapper.c new file mode 100644 index 0000000000..01802b8fa7 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_f77wrapper.c @@ -0,0 +1,108 @@ +/* ========================================================================= */ +/* === amd_f77wrapper ====================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Fortran interface for the C-callable AMD library (int version only). This + * is HIGHLY non-portable. You will need to modify this depending on how your + * Fortran and C compilers behave. Two examples are provided. + * + * To avoid using I/O, and to avoid the extra porting step of a Fortran + * function, the status code is returned as the first entry in P (P [0] in C + * and P (1) in Fortran) if an error occurs. The error codes are negative + * (-1: out of memory, -2: invalid matrix). + * + * For some C and Fortran compilers, the Fortran compiler appends a single "_" + * after each routine name. C doesn't do this, so the translation is made + * here. Some Fortran compilers don't append an underscore (xlf on IBM AIX, + * for * example). + * + * Tested with the following compilers: + * Solaris with cc and f77 from Sun WorkShop 6 update 1. + * SGI Irix with MIPSpro cc and f77 compilers version 7.4 + * Linux with GNU gcc or Intel's icc, and GNU g77 Intel's ifc Fortran compiler. + * (any combination). Note that with g77, a call to amd_order in Fortran + * gets translated to a call to amd_order__, with two underscores ("_"). + * Thus, the Fortran names do not include an underscore. + */ + +#include "amd.h" +#include + +/* ------------------------------------------------------------------------- */ +/* Linux, Solaris, SGI */ +/* ------------------------------------------------------------------------- */ + +void amdorder_ (int *n, const int *Ap, const int *Ai, int *P, + double *Control, double *Info) +{ + int result = amd_order (*n, Ap, Ai, P, Control, Info) ; + if (result != AMD_OK && P) P [0] = result ; +} + +void amddefaults_ (double *Control) +{ + amd_defaults (Control) ; +} + +void amdcontrol_ (double *Control) +{ + fflush (stdout) ; + amd_control (Control) ; + fflush (stdout) ; +} + +void amdinfo_ (double *Info) +{ + fflush (stdout) ; + amd_info (Info) ; + fflush (stdout) ; +} + +void amdpreproc_ (int *n, const int *Ap, const int *Ai, int *Rp, int *Ri) +{ + int result = amd_preprocess (*n, Ap, Ai, Rp, Ri) ; + if (result != AMD_OK && Rp) Rp [0] = result ; +} + +/* ------------------------------------------------------------------------- */ +/* IBM AIX. Probably Windows, Compaq Alpha, and HP Unix as well. */ +/* ------------------------------------------------------------------------- */ + +void amdorder (int *n, const int *Ap, const int *Ai, int *P, + double *Control, double *Info) +{ + int result = amd_order (*n, Ap, Ai, P, Control, Info) ; + if (result != AMD_OK && P) P [0] = result ; +} + +void amddefaults (double *Control) +{ + amd_defaults (Control) ; +} + +void amdcontrol (double *Control) +{ + fflush (stdout) ; + amd_control (Control) ; + fflush (stdout) ; +} + +void amdinfo (double *Info) +{ + fflush (stdout) ; + amd_info (Info) ; + fflush (stdout) ; +} + +void amdpreproc (int *n, const int *Ap, const int *Ai, int *Rp, int *Ri) +{ + int result = amd_preprocess (*n, Ap, Ai, Rp, Ri) ; + if (result != AMD_OK && Rp) Rp [0] = result ; +} diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_simple.c b/deal.II/contrib/umfpack/AMD/Demo/amd_simple.c new file mode 100644 index 0000000000..2399526f22 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_simple.c @@ -0,0 +1,23 @@ +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +#include +#include "amd.h" + +int n = 5 ; +int Ap [ ] = { 0, 2, 6, 10, 12, 14} ; +int Ai [ ] = { 0,1, 0,1,2,4, 1,2,3,4, 2,3, 1,4 } ; +int P [5] ; + +int main (void) +{ + int k ; + (void) amd_order (n, Ap, Ai, P, (double *) NULL, (double *) NULL) ; + for (k = 0 ; k < n ; k++) printf ("P [%d] = %d\n", k, P [k]) ; + return (0) ; +} + diff --git a/deal.II/contrib/umfpack/AMD/Demo/amd_simple.out b/deal.II/contrib/umfpack/AMD/Demo/amd_simple.out new file mode 100644 index 0000000000..08a04e0b9f --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Demo/amd_simple.out @@ -0,0 +1,5 @@ +P [0] = 0 +P [1] = 3 +P [2] = 2 +P [3] = 4 +P [4] = 1 diff --git a/deal.II/contrib/umfpack/AMD/Doc/AMD_UserGuide.bib b/deal.II/contrib/umfpack/AMD/Doc/AMD_UserGuide.bib new file mode 100644 index 0000000000..9584598a94 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Doc/AMD_UserGuide.bib @@ -0,0 +1,86 @@ +@string{SIREV = "{SIAM} Review"} +@string{SIMAX = "{SIAM} J. Matrix Anal. Applic."} +@string{SIAMJSC = "{SIAM} J. Sci. Comput."} +@string{TOMS = "{ACM} Trans. Math. Softw."} + +@article{schu:01, + author = {J. Schulze}, + title = {Towards a tighter coupling of bottom-up and top-down sparse matrix ordering methods}, + journal = {BIT}, + volume = {41}, + number = {4}, + pages = "800--841", + year = {2001} + } + +@article{GeorgeLiu89, + author={George, A. and Liu, J. W. H.}, + year={1989}, + title={The Evolution of the Minimum Degree Ordering Algorithm}, + journal=SIREV, + volume={31}, + number={1}, + pages={1--19}} + +@article{AmestoyDavisDuff96, + author={Amestoy, P. R. and Davis, T. A. and Duff, I. S.}, + title={An approximate minimum degree ordering algorithm}, + journal=SIMAX, + year={1996} + ,volume={17} + ,number={4} + ,pages={886-905} + } + +@misc{hsl:2002, + author = {HSL}, + title = "{HSL} 2002: {A} collection of {F}ortran codes for large + scale scientific computation", + note = {{\tt www.cse.clrc.ac.uk/nag/hsl}}, + year = 2002} + + +@article{RothbergEisenstat98, + author={Rothberg, E. and Eisenstat, S. C.}, + title={Node selection strategies for bottom-up sparse matrix orderings}, + journal=SIMAX, + year={1998} + ,volume={19} + ,number={3} + ,pages={682-695} + } + +@article{KarypisKumar98e, + author={Karypis, G. and Kumar, V.}, + title={A fast and high quality multilevel scheme for partitioning irregular graphs}, + journal=SIAMJSC, + year={1998} + ,volume={20} + ,pages={359-392} + } + +@article{Chaco, + author={B. Hendrickson and E. Rothberg}, + title={Improving the runtime and quality of nested dissection ordering}, + journal=SIAMJSC, + year={1999} + ,volume={20} + ,pages={468--489} + } + +@article{PellegriniRomanAmestoy00, + author={Pellegrini, F. and Roman, J. and Amestoy, P.}, + title={Hybridizing nested dissection and halo approximate minimum degree for efficient sparse matrix ordering}, + journal={Concurrency: Practice and Experience}, + year={2000} + ,volume={12} + ,pages={68-84} + } + +@article{DavisGilbertLarimoreNg00pending, + author={Davis, T. A. and Gilbert, J. R. and Larimore, S. I. and Ng, E. G.}, + title={A column approximate minimum degree ordering algorithm}, + journal=TOMS, + year={(to appear)} + } + diff --git a/deal.II/contrib/umfpack/AMD/Doc/AMD_UserGuide.pdf b/deal.II/contrib/umfpack/AMD/Doc/AMD_UserGuide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..39ca0b2abf6963a3f7b1908c4651e38af510297f GIT binary patch literal 176601 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

a-UX~oB$sEJS? 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 +#include "amd.h" + +int n = 5 ; +int Ap [ ] = { 0, 2, 6, 10, 12, 14} ; +int Ai [ ] = { 0,1, 0,1,2,4, 1,2,3,4, 2,3, 1,4 } ; +int P [5] ; + +int main (void) +{ + int k ; + (void) amd_order (n, Ap, Ai, P, (double *) NULL, (double *) NULL) ; + for (k = 0 ; k < n ; k++) printf ("P [%d] = %d\n", k, P [k]) ; + return (0) ; +} +\end{verbatim} +} + +The {\tt Ap} and {\tt Ai} arrays represent the binary matrix +\[ +\m{A} = \left[ +\begin{array}{rrrrr} + 1 & 1 & 0 & 0 & 0 \\ + 1 & 1 & 1 & 0 & 1 \\ + 0 & 1 & 1 & 1 & 0 \\ + 0 & 0 & 1 & 1 & 0 \\ + 0 & 1 & 1 & 0 & 1 \\ +\end{array} +\right]. +\] +The diagonal entries are ignored. +% +AMD constructs the pattern of $\m{A}+\m{A}\tr$, +and returns a permutation vector of $(0, 3, 1, 4, 2)$. +% +Since the matrix is unsymmetric but with a mostly symmetric nonzero +pattern, this would be a suitable permutation for an LU factorization of a +matrix with this nonzero pattern and whose diagonal entries are not too small. +The program uses default control settings and does not return any statistics +about the ordering, factorization, or solution ({\tt Control} and {\tt Info} +are both {\tt (double *) NULL}). It also ignores the status value returned by +{\tt amd\_order}. + +More example programs are included with the AMD package. +The {\tt amd\_demo.c} program provides a more detailed demo of AMD. +Another example is the AMD mexFunction, {\tt amd\_mex.c}. + +%------------------------------------------------------------------------------ +\subsection{A note about zero-sized arrays} +%------------------------------------------------------------------------------ + +AMD uses several user-provided arrays of size {\tt n} or {\tt nz}. +Either {\tt n} or {\tt nz} can be zero. +If you attempt to {\tt malloc} an array of size zero, +however, {\tt malloc} will return a null pointer which AMD will report +as invalid. If you {\tt malloc} an array of +size {\tt n} or {\tt nz} to pass to AMD, make sure that you handle the +{\tt n} = 0 and {\tt nz = 0} cases correctly. + +%------------------------------------------------------------------------------ +\section{Synopsis of C-callable routines} +\label{Synopsis} +%------------------------------------------------------------------------------ + +The matrix $\m{A}$ is {\tt n}-by-{\tt n} with {\tt nz} entries. + +{\footnotesize +\begin{verbatim} +#include "amd.h" +int n, status, Ap [n+1], Ai [nz], P [n], Rp [n+1], Ri [nz] ; +double Control [AMD_CONTROL], Info [AMD_INFO] ; +amd_defaults (Control) ; +status = amd_order (n, Ap, Ai, P, Control, Info) ; +amd_control (Control) ; +amd_info (Info) ; +amd_preprocess (n, Ap, Ai, Rp, Ri) ; +\end{verbatim} +} + +The {\tt amd\_l\_*} routines are identical, except that all {\tt int} +arguments become {\tt long}: + +{\footnotesize +\begin{verbatim} +#include "amd.h" +long n, status, Ap [n+1], Ai [nz], P [n], Rp [n+1], Ri [nz] ; +double Control [AMD_CONTROL], Info [AMD_INFO] ; +amd_l_defaults (Control) ; +status = amd_l_order (n, Ap, Ai, P, Control, Info) ; +amd_l_control (Control) ; +amd_l_info (Info) ; +amd_l_preprocess (n, Ap, Ai, Rp, Ri) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------ +\section{Using AMD in a Fortran program} +%------------------------------------------------------------------------------ + +Two Fortran versions of AMD are provided. The {\tt AMD} routine computes the +approximate minimum degree ordering, using aggressive absorption. The +{\tt AMDBAR} routine is identical, except that it does not perform aggressive +absorption. The {\tt AMD} routine is essentially identical to the HSL +routine {\tt MC47B/BD}. +Note that earlier versions of the Fortran +{\tt AMD} and {\tt AMDBAR} routines included an {\tt IOVFLO} argument, +which is no longer present. + +In contrast to the C version, the Fortran routines require a symmetric +nonzero pattern, with no diagonal entries present although the {\tt MC47A/AD} +wrapper in HSL allows duplicates, ignores out-of-range entries, and only +uses entries from the upper triangular part of the matrix. Although we +have an experimental Fortran code for treating ``dense'' rows, the Fortran +codes in this release do not treat +``dense'' rows and columns of $\m{A}$ differently, and thus their run time +can be high if there are a few dense rows and columns in the matrix. +They do not perform a post-ordering of the elimination tree, +compute statistics on the ordering, or check the validity of their input +arguments. These facilities are provided by {\tt MC47A/AD} and other +subroutines from HSL. +Only one {\tt integer} +version of each Fortran routine is provided. +Both Fortran routines overwrite the user's input +matrix, in contrast to the C version. +% +The C version does not return the elimination or assembly tree. +The Fortran version returns an assembly tree; +refer to the User Guide for details. +The following is the syntax of the {\tt AMD} Fortran routine. +The {\tt AMDBAR} routine is identical except for the routine name. + +{\footnotesize +\begin{verbatim} + INTEGER N, IWLEN, PFREE, NCMPA, IW (IWLEN), PE (N), DEGREE (N), NV (N), + $ NEXT (N), LAST (N), HEAD (N), ELEN (N), W (N), LEN (N) + CALL AMD (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, + $ LAST, HEAD, ELEN, DEGREE, NCMPA, W) + CALL AMDBAR (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, + $ LAST, HEAD, ELEN, DEGREE, NCMPA, W) +\end{verbatim} +} + +The input matrix is provided to {\tt AMD} and {\tt AMDBAR} +in three arrays, {\tt PE}, of size {\tt N}, +{\tt LEN}, of size {\tt N}, and {\tt IW}, of size {\tt IWLEN}. The size of +{\tt IW} must be at least {\tt NZ+N}. The recommended size is +{\tt 1.2*NZ + N}. +On input, the indices of nonzero entries in row {\tt I} are stored in {\tt IW}. +{\tt PE(I)} is the index in {\tt IW} of the start of row {\tt I}. +{\tt LEN(I)} is the number of entries in row {\tt I}. +The matrix is 1-based, with row and column indices in the range 1 to {\tt N}. +Row {\tt I} is contained in +{\tt IW (PE(I)} $\ldots \:$ {\tt PE(I) + LEN(I) - 1)}. +The diagonal entries must not be present. The indices within each row must +not contain any duplicates, but they need not be sorted. The rows +themselves need not be in any particular order, and there may be empty space +between the rows. If {\tt LEN(I)} is zero, then there are no off-diagonal +entries in row {\tt I}, and {\tt PE(I)} is ignored. The integer +{\tt PFREE} defines what part of {\tt IW} contains the user's input matrix, +which is held in {\tt IW(1}~$\ldots~\:${\tt PFREE-1)}. +The contents of {\tt IW} and {\tt LEN} are undefined on output, +and {\tt PE} is modified to contain information about the ordering. + +As the algorithm proceeds, it modifies the {\tt IW} array, placing the +pattern of the partially eliminated matrix in +{\tt IW(PFREE} $\ldots \:${\tt IWLEN)}. +If this space is exhausted, the space is compressed. +The number of compressions performed on the {\tt IW} array is +returned in the scalar {\tt NCMPA}. The value of {\tt PFREE} on output is the +length of {\tt IW} required for no compressions to be needed. + +The output permutation is returned in the array {\tt LAST}, of size {\tt N}. +If {\tt I=LAST(K)}, then {\tt I} is the {\tt K}th row in the permuted +matrix. The inverse permutation is returned in the array {\tt ELEN}, where +{\tt K=ELEN(I)} if {\tt I} is the {\tt K}th row in the permuted matrix. +On output, the {\tt PE} and {\tt NV} arrays hold the assembly tree, +a supernodal elimination tree that represents the relationship between +columns of the Cholesky factor $\m{L}$. +If {\tt NV(I)} $> 0$, then {\tt I} is a node in the assembly +tree, and the parent of {\tt I} is {\tt -PE(I)}. If {\tt I} is a root of +the tree, then {\tt PE(I)} is zero. The value of {\tt NV(I)} is the +number of entries in the corresponding column of $\m{L}$, including the +diagonal. +If {\tt NV(I)} is zero, then {\tt I} is a non-principal node that is +not in the assembly tree. Node {\tt -PE(I)} is the parent of node {\tt I} +in a subtree, the root of which is a node in the assembly tree. All nodes +in one subtree belong to the same supernode in the assembly tree. +The other size {\tt N} arrays +({\tt DEGREE}, {\tt HEAD}, {\tt NEXT}, and {\tt W}) are used as workspace, +and are not defined on input or output. + +If you want to use a simpler user-interface and compute the elimination +tree post-ordering, you should be able to call the C routines {\tt amd\_order} +or {\tt amd\_l\_order} from a Fortran program. Just be sure to take into +account the 0-based indexing in the {\tt P}, {\tt Ap}, and {\tt Ai} arguments +to {\tt amd\_order} and {\tt amd\_l\_order}. A sample interface is provided +in the files {\tt AMD/Demo/amd\_f77cross.f} and +{\tt AMD/Demo/amd\_f77wrapper.c}. To compile the {\tt amd\_f77cross} program, +type {\tt make cross} in the {\tt AMD/Demo} directory. The +Fortran-to-C calling conventions are highly non-portable, so this example +is not guaranteed to work with your compiler C and Fortran compilers. +The output of {\tt amd\_f77cross} is in {\tt amd\_f77cross.out}. + +%------------------------------------------------------------------------------ +\section{Sample Fortran main program} +%------------------------------------------------------------------------------ + +The following program illustrates the basic usage of the Fortran version of AMD. +The {\tt AP} and {\tt AI} arrays represent the binary matrix +\[ +\m{A} = \left[ +\begin{array}{rrrrr} + 1 & 1 & 0 & 0 & 0 \\ + 1 & 1 & 1 & 0 & 1 \\ + 0 & 1 & 1 & 1 & 1 \\ + 0 & 0 & 1 & 1 & 0 \\ + 0 & 1 & 1 & 0 & 1 \\ +\end{array} +\right] +\] +in a conventional 1-based column-oriented form, +except that the diagonal entries are not present. +The matrix has the same as nonzero pattern of $\m{A}+\m{A}\tr$ in the C +program, in Section~\ref{Cversion}. +The output permutation is $(4, 1, 3, 5, 2)$. +It differs from the permutation returned by the C routine {\tt amd\_order} +because a post-order of the elimination tree has not yet been performed. + +{\footnotesize +\begin{verbatim} + INTEGER N, NZ, J, K, P, IWLEN, PFREE, NCMPA + PARAMETER (N = 5, NZ = 10, IWLEN = 17) + INTEGER AP (N+1), AI (NZ), LAST (N), PE (N), LEN (N), ELEN (N), + $ IW (IWLEN), DEGREE (N), NV (N), NEXT (N), HEAD (N), W (N) + DATA AP / 1, 2, 5, 8, 9, 11/ + DATA AI / 2, 1,3,5, 2,4,5, 3, 2,3 / +C load the matrix into the AMD workspace + DO 10 J = 1,N + PE (J) = AP (J) + LEN (J) = AP (J+1) - AP (J) +10 CONTINUE + DO 20 P = 1,NZ + IW (P) = AI (P) +20 CONTINUE + PFREE = NZ + 1 +C order the matrix (destroys the copy of A in IW, PE, and LEN) + CALL AMD (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, LAST, HEAD, + $ ELEN, DEGREE, NCMPA, W) + DO 60 K = 1, N + PRINT 50, K, LAST (K) +50 FORMAT ('P (',I2,') = ', I2) +60 CONTINUE + END +\end{verbatim} +} + +The {\tt Demo} directory contains an example of how the C version +may be called from a Fortran program, but this is highly non-portable. +For this reason, it is placed in the {\tt Demo} directory, not in the +primary {\tt Source} directory. + +%------------------------------------------------------------------------------ +\section{Installation} +\label{Install} +%------------------------------------------------------------------------------ + +The following discussion assumes you have the {\tt make} program, either in +Unix, or in Windows with Cygwin. + +System-dependent configurations are in the {\tt AMD/Make} +directory. You can edit the {\tt Make.include} +file in that directory to customize the compilation. The default +settings will work on most systems. +Sample configuration files are provided +for Linux, Sun Solaris, SGI IRIX, IBM AIX, and the DEC/Compaq Alpha. + +To compile and install the C-callable AMD library, +go to the {\tt AMD} directory and type {\tt make}. +The library will be placed in {\tt AMD/Lib/libamd.a}. +Two demo programs of the AMD ordering routine will be compiled and tested in +the {\tt AMD/Demo} directory. +The outputs of these demo programs will then be compared with output +files in the distribution. The AMD mexFunction for +use in MATLAB will also be compiled. If you do not have MATLAB +type {\tt make lib} instead. + +To compile and install the Fortran-callable AMD library, +go to the {\tt AMD} directory and type {\tt make fortran}. +The library will be placed in {\tt AMD/Lib/libamdf77.a}. +A demo program will be compiled and tested in the {\tt AMD/Demo} directory. +The output will be compared with an output file in the distribution. + +Typing {\tt make clean} will remove all but the final compiled libraries +and demo programs. Typing {\tt make purge} removes all files not in the +original distribution. +If you compile AMD and then later change the {\tt Make.include} +file or your system-specific configuration file such as {\tt Make.linux}, +then you should type {\tt make purge} and then {\tt make} to recompile. + +Here are the various parameters that you can control in your +{\tt Make.include} file: + +\begin{itemize} +\item {\tt CC = } your C compiler, such as {\tt cc}. +\item {\tt RANLIB = } your system's {\tt ranlib} program, if needed. +\item {\tt CFLAGS = } optimization flags, such as {\tt -O}. +\item {\tt LIB = } your libraries, such as {\tt -lm} or {\tt -lblas}. +\item {\tt RM =} the command to delete a file. +\item {\tt MV =} the command to rename a file. +\item {\tt MEX =} the command to compile a MATLAB mexFunction. +\item {\tt F77 =} the command to compile a Fortran program (optional). +\item {\tt F77FLAGS =} the Fortran compiler flags (optional). +\item {\tt F77LIB =} the Fortran libraries (optional). +\end{itemize} + +The {\tt Make.include} includes some definitions regarding the BLAS. +This is so that AMD and UMFPACK (which requires AMD) can share +the same configuration files. If you wish to use AMD only, then +you can ignore any references to the BLAS (the -DNBLAS compile flag). + +When you compile your program that uses the C-callable AMD library, +you need to add the {\tt AMD/Lib/libamd.a} library +and you need to tell your compiler to look in the +{\tt AMD/Include} directory for include +files. To compile a Fortran program that calls the Fortran AMD library, +you need to add the {\tt AMD/Lib/libamdf77.a} library. +See {\tt AMD/Demo/Makefile} for an example. + +If all you want to use is the AMD mexFunction in MATLAB, you can skip +the use of the {\tt make} command entirely. Simply type +{\tt amd\_make} in MATLAB while in the {\tt AMD/MATLAB} directory. +This works on any system with MATLAB, including Windows. + +If you are including AMD as a subset of a larger library and do not want +to link the C standard I/O library, or if you simply do not need to use +them, you can safely remove the {\tt amd\_control.c} and {\tt amd\_info.c} +files. Similarly, if you use default parameters (or define your +own {\tt Control} array), then you can exclude the {\tt amd\_defaults.c} +file. The {\tt amd\_preprocess.c} file is optional as well, if you +can ensure that the input matrix to {\tt amd\_order} is always sorted +and has no duplicate entries. +Each of these files contains the user-callable routines of the same +name. None of these auxiliary routines are directly called by +{\tt amd\_order}. +The {\tt amd\_dump.c} file contains debugging routines +that are neither used nor compiled unless debugging is enabled. +The {\tt amd\_internal.h} file must be edited to enable debugging; +refer to the instructions in that file. Thus, it too can be excluded +if compiled into a larger production program or library. +The bare minimum files required to use just {\tt amd\_order} are +{\tt amd.h} in the {\tt Include} directory, +and +{\tt amd\_1.c}, +{\tt amd\_2.c}, +{\tt amd\_aat.c}, +{\tt and\_order.c}, +{\tt amd\_postorder.c}, +{\tt amd\_post\_tree.c}, +{\tt amd\_valid.c}, +and +{\tt amd\_internal.h}, +in the {\tt Source} directory. + +%------------------------------------------------------------------------------ +\newpage +\section{The AMD routines} +\label{Primary} +%------------------------------------------------------------------------------ + +The file {\tt AMD/Include/amd.h} listed below +describes each user-callable routine in the C version of AMD, +and gives details on their use. + +{\footnotesize +\begin{verbatim} +/* ========================================================================= */ +/* === AMD: approximate minimum degree ordering =========================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* AMD finds a symmetric ordering P of a matrix A so that the Cholesky + * factorization of P*A*P' has fewer nonzeros and takes less work than the + * Cholesky factorization of A. If A is not symmetric, then it performs its + * ordering on the matrix A+A'. Two sets of user-callable routines are + * provided, one for "int" integers and the other for "long" integers. + * + * The method is based on the approximate minimum degree algorithm, discussed + * in Amestoy, Davis, and Duff, "An approximate degree ordering algorithm", + * SIAM Journal of Matrix Analysis and Applications, vol. 17, no. 4, pp. + * 886-905, 1996. This package can perform both the AMD ordering (with + * aggressive absorption), and the AMDBAR ordering (without aggressive + * absorption) discussed in the above paper. This package differs from the + * Fortran codes discussed in the paper: + * + * (1) it can ignore "dense" rows and columns, leading to faster run times + * (2) it computes the ordering of A+A' if A is not symmetric + * (3) it is followed by a depth-first post-ordering of the assembly tree + * (or supernodal elimination tree) + * + * For historical reasons, the Fortran versions, amd.f and amdbar.f, have + * been left (nearly) unchanged. They compute the identical ordering as + * described in the above paper. + */ + +#ifndef AMD_H +#define AMD_H + +int amd_order ( /* returns 0 if OK, negative value if error */ + int n, /* A is n-by-n. n must be >= 0. */ + const int Ap [ ], /* column pointers for A, of size n+1 */ + const int Ai [ ], /* row indices of A, of size nz = Ap [n] */ + int P [ ], /* output permutation, of size n */ + double Control [ ], /* input Control settings, of size AMD_CONTROL */ + double Info [ ] /* output Info statistics, of size AMD_INFO */ +) ; + +long amd_l_order ( /* see above for description of arguments */ + long n, + const long Ap [ ], + const long Ai [ ], + long P [ ], + double Control [ ], + double Info [ ] +) ; + +/* Input arguments (not modified): + * + * n: the matrix A is n-by-n. + * Ap: an int/long array of size n+1, containing the column pointers of A. + * Ai: an int/long array of size nz, containing the row indices of A, + * where nz = Ap [n]. + * Control: a double array of size AMD_CONTROL, containing control + * parameters. Defaults are used if Control is NULL. + * + * Output arguments (not defined on input): + * + * P: an int/long array of size n, containing the output permutation. If + * row i is the kth pivot row, then P [k] = i. In MATLAB notation, + * the reordered matrix is A (P,P). + * Info: a double array of size AMD_INFO, containing statistical + * information. Ignored if Info is NULL. + * + * On input, the matrix A is stored in column-oriented form. The row indices + * of nonzero entries in column j are stored in Ai [Ap [j] ... Ap [j+1]-1]. + * The row indices must appear in ascending order in each column, and there + * must not be any duplicate entries. Row indices must be in the range 0 to + * n-1. Ap [0] must be zero, and thus nz = Ap [n] is the number of nonzeros + * in A. The array Ap is of size n+1, and the array Ai is of size nz = Ap [n]. + * The matrix does not need to be symmetric, and the diagonal does not need to + * be present (if diagonal entries are present, they are ignored except for + * the output statistic Info [AMD_NZDIAG]). The arrays Ai and Ap are not + * modified. This form of the Ap and Ai arrays to represent the nonzero + * pattern of the matrix A is the same as that used internally by MATLAB. + * If you wish to use a more flexible input structure, please see the + * umfpack_*_triplet_to_col routines in the UMFPACK package, at + * http://www.cise.ufl.edu/research/sparse/umfpack, or use the amd_preprocess + * routine discussed below. + * + * Restrictions: n >= 0. Ap [0] = 0. Ap [j] <= Ap [j+1] for all j in the + * range 0 to n-1. nz = Ap [n] >= 0. For all j in the range 0 to n-1, + * and for all p in the range Ap [j] to Ap [j+1]-2, Ai [p] < Ai [p+1] must + * hold. Ai [0..nz-1] must be in the range 0 to n-1. To avoid integer + * overflow, (2.4*nz + 8*n) < INT_MAX / sizeof (int) for must hold for the + * "int" version. (2.4*nz + 8*n) < LONG_MAX / sizeof (long) must hold + * for the "long" version. Finally, Ai, Ap, and P must not be NULL. If + * any of these restrictions are not met, AMD returns AMD_INVALID. + * + * AMD returns: + * + * AMD_OK if the matrix is valid and sufficient memory can be allocated to + * perform the ordering. + * + * AMD_OUT_OF_MEMORY if not enough memory can be allocated. + * + * AMD_INVALID if the input arguments n, Ap, Ai are invalid, or if P is + * NULL. + * + * The AMD routine first forms the pattern of the matrix A+A', and then + * computes a fill-reducing ordering, P. If P [k] = i, then row/column i of + * the original is the kth pivotal row. In MATLAB notation, the permuted + * matrix is A (P,P), except that 0-based indexing is used instead of the + * 1-based indexing in MATLAB. + * + * The Control array is used to set various parameters for AMD. If a NULL + * pointer is passed, default values are used. The Control array is not + * modified. + * + * Control [AMD_DENSE]: controls the threshold for "dense" rows/columns. + * A dense row/column in A+A' can cause AMD to spend a lot of time in + * ordering the matrix. If Control [AMD_DENSE] >= 0, rows/columns + * with more than Control [AMD_DENSE] * sqrt (n) entries are ignored + * during the ordering, and placed last in the output order. The + * default value of Control [AMD_DENSE] is 10. If negative, no + * rows/columns are treated as "dense". Rows/columns with 16 or + * fewer off-diagonal entries are never considered "dense". + * + * Control [AMD_AGGRESSIVE]: controls whether or not to use aggressive + * absorption, in which a prior element is absorbed into the current + * element if is a subset of the current element, even if it is not + * adjacent to the current pivot element (refer to Amestoy, Davis, + * & Duff, 1996, for more details). The default value is nonzero, + * which means to perform aggressive absorption. This nearly always + * leads to a better ordering (because the approximate degrees are + * more accurate) and a lower execution time. There are cases where + * it can lead to a slightly worse ordering, however. To turn it off, + * set Control [AMD_AGGRESSIVE] to 0. + * + * Control [2..4] are not used in the current version, but may be used in + * future versions. + * + * The Info array provides statistics about the ordering on output. If it is + * not present, the statistics are not returned. This is not an error + * condition. + * + * Info [AMD_STATUS]: the return value of AMD, either AMD_OK, + * AMD_OUT_OF_MEMORY, or AMD_INVALID. + * + * Info [AMD_N]: n, the size of the input matrix + * + * Info [AMD_NZ]: the number of nonzeros in A, nz = Ap [n] + * + * Info [AMD_SYMMETRY]: the symmetry of the matrix A. It is the number + * of "matched" off-diagonal entries divided by the total number of + * off-diagonal entries. An entry A(i,j) is matched if A(j,i) is also + * an entry, for any pair (i,j) for which i != j. In MATLAB notation, + * S = spones (A) ; + * B = tril (S, -1) + triu (S, 1) ; + * symmetry = nnz (B & B') / nnz (B) ; + * + * Info [AMD_NZDIAG]: the number of entries on the diagonal of A. + * + * Info [AMD_NZ_A_PLUS_AT]: the number of nonzeros in A+A', excluding the + * diagonal. If A is perfectly symmetric (Info [AMD_SYMMETRY] = 1) + * with a fully nonzero diagonal, then Info [AMD_NZ_A_PLUS_AT] = nz-n + * (the smallest possible value). If A is perfectly unsymmetric + * (Info [AMD_SYMMETRY] = 0, for an upper triangular matrix, for + * example) with no diagonal, then Info [AMD_NZ_A_PLUS_AT] = 2*nz + * (the largest possible value). + * + * Info [AMD_NDENSE]: the number of "dense" rows/columns of A+A' that were + * removed from A prior to ordering. These are placed last in the + * output order P. + * + * Info [AMD_MEMORY]: the amount of memory used by AMD, in bytes. In the + * current version, this is 1.2 * Info [AMD_NZ_A_PLUS_AT] + 9*n + * times the size of an integer. This is at most 2.4nz + 9n. This + * excludes the size of the input arguments Ai, Ap, and P, which have + * a total size of nz + 2*n + 1 integers. + * + * Info [AMD_NCMPA]: the number of garbage collections performed. + * + * Info [AMD_LNZ]: the number of nonzeros in L (excluding the diagonal). + * This is a slight upper bound because mass elimination is combined + * with the approximate degree update. It is a rough upper bound if + * there are many "dense" rows/columns. The rest of the statistics, + * below, are also slight or rough upper bounds, for the same reasons. + * The post-ordering of the assembly tree might also not exactly + * correspond to a true elimination tree postordering. + * + * Info [AMD_NDIV]: the number of divide operations for a subsequent LDL' + * or LU factorization of the permuted matrix A (P,P). + * + * Info [AMD_NMULTSUBS_LDL]: the number of multiply-subtract pairs for a + * subsequent LDL' factorization of A (P,P). + * + * Info [AMD_NMULTSUBS_LU]: the number of multiply-subtract pairs for a + * subsequent LU factorization of A (P,P), assuming that no numerical + * pivoting is required. + * + * Info [AMD_DMAX]: the maximum number of nonzeros in any column of L, + * including the diagonal. + * + * Info [14..19] are not used in the current version, but may be used in + * future versions. + */ + +/* ------------------------------------------------------------------------- */ +/* AMD preprocess */ +/* ------------------------------------------------------------------------- */ + +/* amd_preprocess: sorts, removes duplicate entries, and transposes the + * nonzero pattern of a column-form matrix A, to obtain the matrix R. + * + * Alternatively, you can consider this routine as constructing a row-form + * matrix from a column-form matrix. Duplicate entries are allowed in A (and + * removed in R). The columns of R are sorted. Checks its input A for errors. + * + * On input, A can have unsorted columns, and can have duplicate entries. + * Ap [0] must still be zero, and Ap must be monotonically nondecreasing. + * Row indices must be in the range 0 to n-1. + * + * On output, if this routine returns AMD_OK, then the matrix R is a valid + * input matrix for AMD_order. It has sorted columns, with no duplicate + * entries in each column. Since AMD_order operates on the matrix A+A', it + * can just as easily use A or A', so the transpose has no significant effect + * (except for minor tie-breaking, which can lead to a minor effect in the + * quality of the ordering). As an example, compare the output of amd_demo.c + * and amd_demo2.c. + * + * This routine transposes A to get R because that's the simplest way to + * sort and remove duplicate entries from a matrix. + * + * Allocates 2*n integer work arrays, and free's them when done. + * + * If you wish to call amd_order, but do not know if your matrix has unsorted + * columns or duplicate entries, then you can use the following code, which is + * fairly efficient. amd_order will not allocate any internal matrix until + * it checks that the input matrix is valid, so the method below is memory- + * efficient as well. This code snippet assumes that Rp and Ri are already + * allocated, and are the same size as Ap and Ai respectively. + + result = amd_order (n, p, Ap, Ai, Control, Info) ; + if (result == AMD_INVALID) + { + if (amd_preprocess (n, Ap, Ai, Rp, Ri) == AMD_OK) + { + result = amd_order (n, p, Rp, Ri, Control, Info) ; + } + } + + * amd_preprocess will still return AMD_INVALID if any row index in Ai is out + * of range or if the Ap array is invalid. These errors are not corrected by + * amd_preprocess since they represent a more serious error that should be + * flagged with the AMD_INVALID error code. + */ + +int amd_preprocess +( + int n, + const int Ap [ ], + const int Ai [ ], + int Rp [ ], + int Ri [ ] +) ; + +long amd_l_preprocess +( + long n, + const long Ap [ ], + const long Ai [ ], + long Rp [ ], + long Ri [ ] +) ; + +/* Input arguments (not modified): + * + * n: the matrix A is n-by-n. + * Ap: an int/long array of size n+1, containing the column pointers of A. + * Ai: an int/long array of size nz, containing the row indices of A, + * where nz = Ap [n]. + * The nonzero pattern of column j of A is in Ai [Ap [j] ... Ap [j+1]-1]. + * Ap [0] must be zero, and Ap [j] <= Ap [j+1] must hold for all j in the + * range 0 to n-1. Row indices in Ai must be in the range 0 to n-1. + * The row indices in any one column need not be sorted, and duplicates + * may exist. + * + * Output arguments (not defined on input): + * + * Rp: an int/long array of size n+1, containing the column pointers of R. + * Ri: an int/long array of size rnz, containing the row indices of R, + * where rnz = Rp [n]. Note that Rp [n] will be less than Ap [n] if + * duplicates appear in A. In general, Rp [n] <= Ap [n]. + * The data structure for R is the same as A, except that each column of + * R contains sorted row indices, and no duplicates appear in any column. + * + * amd_preprocess returns: + * + * AMD_OK if the matrix A is valid and sufficient memory can be allocated + * to perform the preprocessing. + * + * AMD_OUT_OF_MEMORY if not enough memory can be allocated. + * + * AMD_INVALID if the input arguments n, Ap, Ai are invalid, or if Rp or + * Ri are NULL. + */ + +/* ------------------------------------------------------------------------- */ +/* AMD Control and Info arrays */ +/* ------------------------------------------------------------------------- */ + +/* amd_defaults: sets the default control settings */ +void amd_defaults (double Control [ ]) ; +void amd_l_defaults (double Control [ ]) ; + +/* amd_control: prints the control settings */ +void amd_control (double Control [ ]) ; +void amd_l_control (double Control [ ]) ; + +/* amd_info: prints the statistics */ +void amd_info (double Info [ ]) ; +void amd_l_info (double Info [ ]) ; + +#define AMD_CONTROL 5 /* size of Control array */ +#define AMD_INFO 20 /* size of Info array */ + +/* contents of Control */ +#define AMD_DENSE 0 /* "dense" if degree > Control [0] * sqrt (n) */ +#define AMD_AGGRESSIVE 1 /* do aggressive absorption if Control [1] != 0 */ + +/* default Control settings */ +#define AMD_DEFAULT_DENSE 10.0 /* default "dense" degree 10*sqrt(n) */ +#define AMD_DEFAULT_AGGRESSIVE 1 /* do aggressive absorption by default */ + +/* contents of Info */ +#define AMD_STATUS 0 /* return value of amd_order and amd_l_order */ +#define AMD_N 1 /* A is n-by-n */ +#define AMD_NZ 2 /* number of nonzeros in A */ +#define AMD_SYMMETRY 3 /* symmetry of pattern (1 is sym., 0 is unsym.) */ +#define AMD_NZDIAG 4 /* # of entries on diagonal */ +#define AMD_NZ_A_PLUS_AT 5 /* nz in A+A' */ +#define AMD_NDENSE 6 /* number of "dense" rows/columns in A */ +#define AMD_MEMORY 7 /* amount of memory used by AMD */ +#define AMD_NCMPA 8 /* number of garbage collections in AMD */ +#define AMD_LNZ 9 /* approx. nz in L, excluding the diagonal */ +#define AMD_NDIV 10 /* number of fl. point divides for LU and LDL' */ +#define AMD_NMULTSUBS_LDL 11 /* number of fl. point (*,-) pairs for LDL' */ +#define AMD_NMULTSUBS_LU 12 /* number of fl. point (*,-) pairs for LU */ +#define AMD_DMAX 13 /* max nz. in any column of L, incl. diagonal */ + +/* ------------------------------------------------------------------------- */ +/* return values of AMD */ +/* ------------------------------------------------------------------------- */ + +#define AMD_OK 0 /* success */ +#define AMD_OUT_OF_MEMORY -1 /* malloc failed */ +#define AMD_INVALID -2 /* input arguments are not valid */ + +#endif +\end{verbatim} +} + +%------------------------------------------------------------------------------ +\newpage +% References +%------------------------------------------------------------------------------ + +\bibliographystyle{plain} +\bibliography{AMD_UserGuide} + +\end{document} diff --git a/deal.II/contrib/umfpack/AMD/Doc/ChangeLog b/deal.II/contrib/umfpack/AMD/Doc/ChangeLog new file mode 100644 index 0000000000..69c1806d0d --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Doc/ChangeLog @@ -0,0 +1,37 @@ +Jan. 21, 2004: AMD Version 1.1 + + * No bugs found or fixed - new features added, only + * amd_preprocess added, to allow for more general input of the matrix A. + * ME=0 added to amd*.f, unused DEXT variable removed from amdbar.f, + to avoid spurious compiler warnings (this was not a bug). + * amd_demo2.c and amd_demo2.out added, to test/demo amd_preprocess. + * option to allow compile-time redefinition of malloc, free, printf added + * amd_demo.c shortened slightly (removed printing of PAP') + * User Guide modified (more details added) + * linewidth reduced from 80 to 79 columns + +Oct. 7, 2003: AMD version 1.0.1. + + * MATLAB mexFunction modified, to remove call to mexCallMATLAB function. + This function can take a long time to call, particularly if you are + ordering many small matrices. + +May 6, 2003: AMD Version 1.0 released. + + * converted to C (compare amd.f and amdbar.f with amd_2.c) + * dense rows/column removed prior to ordering + * elimination tree post-ordering added + * demos, user guide written + * statistics added (nz in L, flop count, symmetry of A) + * computes the pattern of A+A' if A is unsymmetric + * user's input matrix no longer overwritten + * degree lists initialized differently + * IOVFLO argument removed from Fortran versions (amd.f and amdbar.f) + * parameters added (dense row/column detection, aggressive absorption) + * MATLAB mexFunction added + +Jan, 1996: + + * amdbar.f posted at http://www.netlib.org (with a restricted License) + * amd.f appears as MC47B/BD in the Harwell Subroutine Library + (without the IOVFLO argument) diff --git a/deal.II/contrib/umfpack/AMD/Doc/License b/deal.II/contrib/umfpack/AMD/Doc/License new file mode 100644 index 0000000000..ed8f2ab901 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Doc/License @@ -0,0 +1,25 @@ +AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. +Davis, Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. + +AMD License: + + Your use or distribution of AMD or any modified version of + AMD implies that you agree to this License. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY + EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to use or copy this program, provided + that the Copyright, this License, and the Availability of the original + version is retained on all copies. User documentation of any code that + uses AMD or any modified version of AMD code must cite the + Copyright, this License, the Availability note, and "Used by permission." + Permission to modify the code and to distribute modified code is granted, + provided the Copyright, this License, and the Availability note are + retained, and a notice that the code was modified is included. This + software was developed with support from the National Science Foundation, + and is provided to you free of charge. + +Availability: + + http://www.cise.ufl.edu/research/sparse/amd diff --git a/deal.II/contrib/umfpack/AMD/Doc/Makefile b/deal.II/contrib/umfpack/AMD/Doc/Makefile new file mode 100644 index 0000000000..c0cf976c74 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Doc/Makefile @@ -0,0 +1,28 @@ +#------------------------------------------------------------------------------ +# AMD Makefile for compiling on Unix systems (for GNU or original make) +#------------------------------------------------------------------------------ + +all: dist + +include ../Make/Make.include + +#------------------------------------------------------------------------------ +# Remove all but the files in the original distribution +#------------------------------------------------------------------------------ + +purge: clean + - $(RM) *.aux *.bbl *.blg *.log *.toc + +#------------------------------------------------------------------------------ +# Create the User Guide and Quick Start Guide +#------------------------------------------------------------------------------ + +AMD_UserGuide.pdf: AMD_UserGuide.tex AMD_UserGuide.bib + pdflatex AMD_UserGuide + bibtex AMD_UserGuide + pdflatex AMD_UserGuide + pdflatex AMD_UserGuide + +dist: AMD_UserGuide.pdf + - $(RM) *.aux *.bbl *.blg *.log *.toc + diff --git a/deal.II/contrib/umfpack/AMD/Include/amd.h b/deal.II/contrib/umfpack/AMD/Include/amd.h new file mode 100644 index 0000000000..d23c0bbe36 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Include/amd.h @@ -0,0 +1,359 @@ +/* ========================================================================= */ +/* === AMD: approximate minimum degree ordering =========================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* AMD finds a symmetric ordering P of a matrix A so that the Cholesky + * factorization of P*A*P' has fewer nonzeros and takes less work than the + * Cholesky factorization of A. If A is not symmetric, then it performs its + * ordering on the matrix A+A'. Two sets of user-callable routines are + * provided, one for "int" integers and the other for "long" integers. + * + * The method is based on the approximate minimum degree algorithm, discussed + * in Amestoy, Davis, and Duff, "An approximate degree ordering algorithm", + * SIAM Journal of Matrix Analysis and Applications, vol. 17, no. 4, pp. + * 886-905, 1996. This package can perform both the AMD ordering (with + * aggressive absorption), and the AMDBAR ordering (without aggressive + * absorption) discussed in the above paper. This package differs from the + * Fortran codes discussed in the paper: + * + * (1) it can ignore "dense" rows and columns, leading to faster run times + * (2) it computes the ordering of A+A' if A is not symmetric + * (3) it is followed by a depth-first post-ordering of the assembly tree + * (or supernodal elimination tree) + * + * For historical reasons, the Fortran versions, amd.f and amdbar.f, have + * been left (nearly) unchanged. They compute the identical ordering as + * described in the above paper. + */ + +#ifndef AMD_H +#define AMD_H + +int amd_order ( /* returns 0 if OK, negative value if error */ + int n, /* A is n-by-n. n must be >= 0. */ + const int Ap [ ], /* column pointers for A, of size n+1 */ + const int Ai [ ], /* row indices of A, of size nz = Ap [n] */ + int P [ ], /* output permutation, of size n */ + double Control [ ], /* input Control settings, of size AMD_CONTROL */ + double Info [ ] /* output Info statistics, of size AMD_INFO */ +) ; + +long amd_l_order ( /* see above for description of arguments */ + long n, + const long Ap [ ], + const long Ai [ ], + long P [ ], + double Control [ ], + double Info [ ] +) ; + +/* Input arguments (not modified): + * + * n: the matrix A is n-by-n. + * Ap: an int/long array of size n+1, containing the column pointers of A. + * Ai: an int/long array of size nz, containing the row indices of A, + * where nz = Ap [n]. + * Control: a double array of size AMD_CONTROL, containing control + * parameters. Defaults are used if Control is NULL. + * + * Output arguments (not defined on input): + * + * P: an int/long array of size n, containing the output permutation. If + * row i is the kth pivot row, then P [k] = i. In MATLAB notation, + * the reordered matrix is A (P,P). + * Info: a double array of size AMD_INFO, containing statistical + * information. Ignored if Info is NULL. + * + * On input, the matrix A is stored in column-oriented form. The row indices + * of nonzero entries in column j are stored in Ai [Ap [j] ... Ap [j+1]-1]. + * The row indices must appear in ascending order in each column, and there + * must not be any duplicate entries. Row indices must be in the range 0 to + * n-1. Ap [0] must be zero, and thus nz = Ap [n] is the number of nonzeros + * in A. The array Ap is of size n+1, and the array Ai is of size nz = Ap [n]. + * The matrix does not need to be symmetric, and the diagonal does not need to + * be present (if diagonal entries are present, they are ignored except for + * the output statistic Info [AMD_NZDIAG]). The arrays Ai and Ap are not + * modified. This form of the Ap and Ai arrays to represent the nonzero + * pattern of the matrix A is the same as that used internally by MATLAB. + * If you wish to use a more flexible input structure, please see the + * umfpack_*_triplet_to_col routines in the UMFPACK package, at + * http://www.cise.ufl.edu/research/sparse/umfpack, or use the amd_preprocess + * routine discussed below. + * + * Restrictions: n >= 0. Ap [0] = 0. Ap [j] <= Ap [j+1] for all j in the + * range 0 to n-1. nz = Ap [n] >= 0. For all j in the range 0 to n-1, + * and for all p in the range Ap [j] to Ap [j+1]-2, Ai [p] < Ai [p+1] must + * hold. Ai [0..nz-1] must be in the range 0 to n-1. To avoid integer + * overflow, (2.4*nz + 8*n) < INT_MAX / sizeof (int) for must hold for the + * "int" version. (2.4*nz + 8*n) < LONG_MAX / sizeof (long) must hold + * for the "long" version. Finally, Ai, Ap, and P must not be NULL. If + * any of these restrictions are not met, AMD returns AMD_INVALID. + * + * AMD returns: + * + * AMD_OK if the matrix is valid and sufficient memory can be allocated to + * perform the ordering. + * + * AMD_OUT_OF_MEMORY if not enough memory can be allocated. + * + * AMD_INVALID if the input arguments n, Ap, Ai are invalid, or if P is + * NULL. + * + * The AMD routine first forms the pattern of the matrix A+A', and then + * computes a fill-reducing ordering, P. If P [k] = i, then row/column i of + * the original is the kth pivotal row. In MATLAB notation, the permuted + * matrix is A (P,P), except that 0-based indexing is used instead of the + * 1-based indexing in MATLAB. + * + * The Control array is used to set various parameters for AMD. If a NULL + * pointer is passed, default values are used. The Control array is not + * modified. + * + * Control [AMD_DENSE]: controls the threshold for "dense" rows/columns. + * A dense row/column in A+A' can cause AMD to spend a lot of time in + * ordering the matrix. If Control [AMD_DENSE] >= 0, rows/columns + * with more than Control [AMD_DENSE] * sqrt (n) entries are ignored + * during the ordering, and placed last in the output order. The + * default value of Control [AMD_DENSE] is 10. If negative, no + * rows/columns are treated as "dense". Rows/columns with 16 or + * fewer off-diagonal entries are never considered "dense". + * + * Control [AMD_AGGRESSIVE]: controls whether or not to use aggressive + * absorption, in which a prior element is absorbed into the current + * element if is a subset of the current element, even if it is not + * adjacent to the current pivot element (refer to Amestoy, Davis, + * & Duff, 1996, for more details). The default value is nonzero, + * which means to perform aggressive absorption. This nearly always + * leads to a better ordering (because the approximate degrees are + * more accurate) and a lower execution time. There are cases where + * it can lead to a slightly worse ordering, however. To turn it off, + * set Control [AMD_AGGRESSIVE] to 0. + * + * Control [2..4] are not used in the current version, but may be used in + * future versions. + * + * The Info array provides statistics about the ordering on output. If it is + * not present, the statistics are not returned. This is not an error + * condition. + * + * Info [AMD_STATUS]: the return value of AMD, either AMD_OK, + * AMD_OUT_OF_MEMORY, or AMD_INVALID. + * + * Info [AMD_N]: n, the size of the input matrix + * + * Info [AMD_NZ]: the number of nonzeros in A, nz = Ap [n] + * + * Info [AMD_SYMMETRY]: the symmetry of the matrix A. It is the number + * of "matched" off-diagonal entries divided by the total number of + * off-diagonal entries. An entry A(i,j) is matched if A(j,i) is also + * an entry, for any pair (i,j) for which i != j. In MATLAB notation, + * S = spones (A) ; + * B = tril (S, -1) + triu (S, 1) ; + * symmetry = nnz (B & B') / nnz (B) ; + * + * Info [AMD_NZDIAG]: the number of entries on the diagonal of A. + * + * Info [AMD_NZ_A_PLUS_AT]: the number of nonzeros in A+A', excluding the + * diagonal. If A is perfectly symmetric (Info [AMD_SYMMETRY] = 1) + * with a fully nonzero diagonal, then Info [AMD_NZ_A_PLUS_AT] = nz-n + * (the smallest possible value). If A is perfectly unsymmetric + * (Info [AMD_SYMMETRY] = 0, for an upper triangular matrix, for + * example) with no diagonal, then Info [AMD_NZ_A_PLUS_AT] = 2*nz + * (the largest possible value). + * + * Info [AMD_NDENSE]: the number of "dense" rows/columns of A+A' that were + * removed from A prior to ordering. These are placed last in the + * output order P. + * + * Info [AMD_MEMORY]: the amount of memory used by AMD, in bytes. In the + * current version, this is 1.2 * Info [AMD_NZ_A_PLUS_AT] + 9*n + * times the size of an integer. This is at most 2.4nz + 9n. This + * excludes the size of the input arguments Ai, Ap, and P, which have + * a total size of nz + 2*n + 1 integers. + * + * Info [AMD_NCMPA]: the number of garbage collections performed. + * + * Info [AMD_LNZ]: the number of nonzeros in L (excluding the diagonal). + * This is a slight upper bound because mass elimination is combined + * with the approximate degree update. It is a rough upper bound if + * there are many "dense" rows/columns. The rest of the statistics, + * below, are also slight or rough upper bounds, for the same reasons. + * The post-ordering of the assembly tree might also not exactly + * correspond to a true elimination tree postordering. + * + * Info [AMD_NDIV]: the number of divide operations for a subsequent LDL' + * or LU factorization of the permuted matrix A (P,P). + * + * Info [AMD_NMULTSUBS_LDL]: the number of multiply-subtract pairs for a + * subsequent LDL' factorization of A (P,P). + * + * Info [AMD_NMULTSUBS_LU]: the number of multiply-subtract pairs for a + * subsequent LU factorization of A (P,P), assuming that no numerical + * pivoting is required. + * + * Info [AMD_DMAX]: the maximum number of nonzeros in any column of L, + * including the diagonal. + * + * Info [14..19] are not used in the current version, but may be used in + * future versions. + */ + +/* ------------------------------------------------------------------------- */ +/* AMD preprocess */ +/* ------------------------------------------------------------------------- */ + +/* amd_preprocess: sorts, removes duplicate entries, and transposes the + * nonzero pattern of a column-form matrix A, to obtain the matrix R. + * + * Alternatively, you can consider this routine as constructing a row-form + * matrix from a column-form matrix. Duplicate entries are allowed in A (and + * removed in R). The columns of R are sorted. Checks its input A for errors. + * + * On input, A can have unsorted columns, and can have duplicate entries. + * Ap [0] must still be zero, and Ap must be monotonically nondecreasing. + * Row indices must be in the range 0 to n-1. + * + * On output, if this routine returns AMD_OK, then the matrix R is a valid + * input matrix for AMD_order. It has sorted columns, with no duplicate + * entries in each column. Since AMD_order operates on the matrix A+A', it + * can just as easily use A or A', so the transpose has no significant effect + * (except for minor tie-breaking, which can lead to a minor effect in the + * quality of the ordering). As an example, compare the output of amd_demo.c + * and amd_demo2.c. + * + * This routine transposes A to get R because that's the simplest way to + * sort and remove duplicate entries from a matrix. + * + * Allocates 2*n integer work arrays, and free's them when done. + * + * If you wish to call amd_order, but do not know if your matrix has unsorted + * columns or duplicate entries, then you can use the following code, which is + * fairly efficient. amd_order will not allocate any internal matrix until + * it checks that the input matrix is valid, so the method below is memory- + * efficient as well. This code snippet assumes that Rp and Ri are already + * allocated, and are the same size as Ap and Ai respectively. + + result = amd_order (n, p, Ap, Ai, Control, Info) ; + if (result == AMD_INVALID) + { + if (amd_preprocess (n, Ap, Ai, Rp, Ri) == AMD_OK) + { + result = amd_order (n, p, Rp, Ri, Control, Info) ; + } + } + + * amd_preprocess will still return AMD_INVALID if any row index in Ai is out + * of range or if the Ap array is invalid. These errors are not corrected by + * amd_preprocess since they represent a more serious error that should be + * flagged with the AMD_INVALID error code. + */ + +int amd_preprocess +( + int n, + const int Ap [ ], + const int Ai [ ], + int Rp [ ], + int Ri [ ] +) ; + +long amd_l_preprocess +( + long n, + const long Ap [ ], + const long Ai [ ], + long Rp [ ], + long Ri [ ] +) ; + +/* Input arguments (not modified): + * + * n: the matrix A is n-by-n. + * Ap: an int/long array of size n+1, containing the column pointers of A. + * Ai: an int/long array of size nz, containing the row indices of A, + * where nz = Ap [n]. + * The nonzero pattern of column j of A is in Ai [Ap [j] ... Ap [j+1]-1]. + * Ap [0] must be zero, and Ap [j] <= Ap [j+1] must hold for all j in the + * range 0 to n-1. Row indices in Ai must be in the range 0 to n-1. + * The row indices in any one column need not be sorted, and duplicates + * may exist. + * + * Output arguments (not defined on input): + * + * Rp: an int/long array of size n+1, containing the column pointers of R. + * Ri: an int/long array of size rnz, containing the row indices of R, + * where rnz = Rp [n]. Note that Rp [n] will be less than Ap [n] if + * duplicates appear in A. In general, Rp [n] <= Ap [n]. + * The data structure for R is the same as A, except that each column of + * R contains sorted row indices, and no duplicates appear in any column. + * + * amd_preprocess returns: + * + * AMD_OK if the matrix A is valid and sufficient memory can be allocated + * to perform the preprocessing. + * + * AMD_OUT_OF_MEMORY if not enough memory can be allocated. + * + * AMD_INVALID if the input arguments n, Ap, Ai are invalid, or if Rp or + * Ri are NULL. + */ + +/* ------------------------------------------------------------------------- */ +/* AMD Control and Info arrays */ +/* ------------------------------------------------------------------------- */ + +/* amd_defaults: sets the default control settings */ +void amd_defaults (double Control [ ]) ; +void amd_l_defaults (double Control [ ]) ; + +/* amd_control: prints the control settings */ +void amd_control (double Control [ ]) ; +void amd_l_control (double Control [ ]) ; + +/* amd_info: prints the statistics */ +void amd_info (double Info [ ]) ; +void amd_l_info (double Info [ ]) ; + +#define AMD_CONTROL 5 /* size of Control array */ +#define AMD_INFO 20 /* size of Info array */ + +/* contents of Control */ +#define AMD_DENSE 0 /* "dense" if degree > Control [0] * sqrt (n) */ +#define AMD_AGGRESSIVE 1 /* do aggressive absorption if Control [1] != 0 */ + +/* default Control settings */ +#define AMD_DEFAULT_DENSE 10.0 /* default "dense" degree 10*sqrt(n) */ +#define AMD_DEFAULT_AGGRESSIVE 1 /* do aggressive absorption by default */ + +/* contents of Info */ +#define AMD_STATUS 0 /* return value of amd_order and amd_l_order */ +#define AMD_N 1 /* A is n-by-n */ +#define AMD_NZ 2 /* number of nonzeros in A */ +#define AMD_SYMMETRY 3 /* symmetry of pattern (1 is sym., 0 is unsym.) */ +#define AMD_NZDIAG 4 /* # of entries on diagonal */ +#define AMD_NZ_A_PLUS_AT 5 /* nz in A+A' */ +#define AMD_NDENSE 6 /* number of "dense" rows/columns in A */ +#define AMD_MEMORY 7 /* amount of memory used by AMD */ +#define AMD_NCMPA 8 /* number of garbage collections in AMD */ +#define AMD_LNZ 9 /* approx. nz in L, excluding the diagonal */ +#define AMD_NDIV 10 /* number of fl. point divides for LU and LDL' */ +#define AMD_NMULTSUBS_LDL 11 /* number of fl. point (*,-) pairs for LDL' */ +#define AMD_NMULTSUBS_LU 12 /* number of fl. point (*,-) pairs for LU */ +#define AMD_DMAX 13 /* max nz. in any column of L, incl. diagonal */ + +/* ------------------------------------------------------------------------- */ +/* return values of AMD */ +/* ------------------------------------------------------------------------- */ + +#define AMD_OK 0 /* success */ +#define AMD_OUT_OF_MEMORY -1 /* malloc failed */ +#define AMD_INVALID -2 /* input arguments are not valid */ + +#endif diff --git a/deal.II/contrib/umfpack/AMD/MATLAB/Contents.m b/deal.II/contrib/umfpack/AMD/MATLAB/Contents.m new file mode 100644 index 0000000000..8200578956 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/MATLAB/Contents.m @@ -0,0 +1,13 @@ +%Contents of the AMD sparse matrix ordering package: +% +% amd p = amd (A), the approximate minimum degree ordering of A +% amd_demo a demo of amd, using the can_24 matrix +% amd_make to comple amd for use in MATLAB +% can_24.mat a sample sparse matrix from the Harwell/Boeing collection +% +% See also: amd, colamd, symamd, colmmd, symmmd, umfpack +% +% AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, +% Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. + +help Contents diff --git a/deal.II/contrib/umfpack/AMD/MATLAB/GNUmakefile b/deal.II/contrib/umfpack/AMD/MATLAB/GNUmakefile new file mode 100644 index 0000000000..ad97a781f5 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/MATLAB/GNUmakefile @@ -0,0 +1,30 @@ +#------------------------------------------------------------------------------ +# GNUmakefile for the AMD MATLAB mexFunction +#------------------------------------------------------------------------------ + +all: amd + +include ../Make/Make.include + +MX = $(MEX) -I../Include + +AMD = amd_aat amd_1 amd_2 amd_dump amd_postorder amd_post_tree amd_defaults \ + amd_order amd_control amd_info amd_valid + +INC = ../Include/amd.h ../Source/amd_internal.h + +MEXAMD = $(addsuffix .o, $(subst amd_,amd_m_,$(AMD))) + +amd_m_%.o: ../Source/amd_%.c $(INC) + $(MX) -DDINT -c $< + - $(MV) amd_$*.o $@ + +amd: amd_mex.c $(MEXAMD) $(INC) + $(MX) -output amd amd_mex.c $(MEXAMD) + +#------------------------------------------------------------------------------ +# Remove all but the files in the original distribution +#------------------------------------------------------------------------------ + +purge: clean + - $(RM) amd.mex* amd.dll diff --git a/deal.II/contrib/umfpack/AMD/MATLAB/Makefile b/deal.II/contrib/umfpack/AMD/MATLAB/Makefile new file mode 100644 index 0000000000..d3c4f8cc2f --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/MATLAB/Makefile @@ -0,0 +1,50 @@ +#------------------------------------------------------------------------------ +# compile the AMD mexFunction for MATLAB (original make only) +#------------------------------------------------------------------------------ + +# This is a very ugly Makefile, and is only provided for those who do not +# have GNU make. Note that it is not used if you have GNU make. It ignores +# dependency checking and just compiles everything. It was created +# automatically, via make -n using the GNUmakefile. That way, I don't have +# maintain two Makefiles. + +all: amd + +include ../Make/Make.include + +MX = $(MEX) -I../Include + +amd: + $(MX) -DDINT -c ../Source/amd_aat.c + $(MV) amd_aat.o amd_m_aat.o + $(MX) -DDINT -c ../Source/amd_1.c + $(MV) amd_1.o amd_m_1.o + $(MX) -DDINT -c ../Source/amd_2.c + $(MV) amd_2.o amd_m_2.o + $(MX) -DDINT -c ../Source/amd_dump.c + $(MV) amd_dump.o amd_m_dump.o + $(MX) -DDINT -c ../Source/amd_postorder.c + $(MV) amd_postorder.o amd_m_postorder.o + $(MX) -DDINT -c ../Source/amd_post_tree.c + $(MV) amd_post_tree.o amd_m_post_tree.o + $(MX) -DDINT -c ../Source/amd_defaults.c + $(MV) amd_defaults.o amd_m_defaults.o + $(MX) -DDINT -c ../Source/amd_order.c + $(MV) amd_order.o amd_m_order.o + $(MX) -DDINT -c ../Source/amd_control.c + $(MV) amd_control.o amd_m_control.o + $(MX) -DDINT -c ../Source/amd_info.c + $(MV) amd_info.o amd_m_info.o + $(MX) -DDINT -c ../Source/amd_valid.c + $(MV) amd_valid.o amd_m_valid.o + $(MX) -output amd amd_mex.c amd_m_aat.o \ + amd_m_1.o amd_m_2.o amd_m_dump.o amd_m_postorder.o \ + amd_m_post_tree.o amd_m_defaults.o amd_m_order.o amd_m_control.o \ + amd_m_info.o amd_m_valid.o + +#------------------------------------------------------------------------------ +# Remove all but the files in the original distribution +#------------------------------------------------------------------------------ + +purge: clean + - $(RM) amd.mex* amd.dll diff --git a/deal.II/contrib/umfpack/AMD/MATLAB/amd.m b/deal.II/contrib/umfpack/AMD/MATLAB/amd.m new file mode 100644 index 0000000000..610ce0207d --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/MATLAB/amd.m @@ -0,0 +1,74 @@ +function [p, Info] = amd (A, Control) +%AMD Approximate minimum degree permutation. +% P = AMD (S) returns the approximate minimum degree permutation vector for +% the sparse matrix C = S+S'. The Cholesky factorization of C (P,P), or +% S (P,P), tends to be sparser than that of C or S. AMD tends to be faster +% than SYMMMD and SYMAMD, and tends to return better orderings than SYMMMD. +% S must be square. If S is full, amd (S) is equivalent to amd (sparse (S)). +% +% Usage: P = amd (S) ; % finds the ordering +% [P, Info] = amd (S, Control) ; % optional parameters & statistics +% Control = amd ; % returns default parameters +% amd ; % prints default parameters. +% +% Control (1); If S is n-by-n, then rows/columns with more than +% max (16, (Control (1))* sqrt(n)) entries in S+S' are considered +% "dense", and ignored during ordering. They are placed last in the +% output permutation. The default is 10.0 if Control is not present. +% Control (2): If nonzero, then aggressive absorption is performed. +% This is the default if Control is not present. +% Control (3): If nonzero, print statistics about the ordering. +% +% Info (1): status (0: ok, -1: out of memory, -2: matrix invalid) +% Info (2): n = size (A,1) +% Info (3): nnz (A) +% Info (4): the symmetry of the matrix S (0.0 means purely unsymmetric, +% 1.0 means purely symmetric). Computed as: +% B = tril (S, -1) + triu (S, 1) ; symmetry = nnz (B & B') / nnz (B); +% Info (5): nnz (diag (S)) +% Info (6): nnz in S+S', excluding the diagonal (= nnz (B+B')) +% Info (7): number "dense" rows/columns in S+S' +% Info (8): the amount of memory used by AMD, in bytes +% Info (9): the number of memory compactions performed by AMD +% +% The following statistics are slight upper bounds because of the +% approximate degree in AMD. The bounds are looser if "dense" rows/columns +% are ignored during ordering (Info (7) > 0). The statistics are for a +% subsequent factorization of the matrix C (P,P). The LU factorization +% statistics assume no pivoting. +% +% Info (10): the number of nonzeros in L, excluding the diagonal +% Info (11): the number of divide operations for LL', LDL', or LU +% Info (12): the number of multiply-subtract pairs for LL' or LDL' +% Info (13): the number of multiply-subtract pairs for LU +% Info (14): the max # of nonzeros in any column of L (incl. diagonal) +% Info (15:20): unused, reserved for future use +% +% An assembly tree post-ordering is performed, which is typically the same +% as an elimination tree post-ordering. It is not always identical because +% of the approximate degree update used, and because "dense" rows/columns +% do not take part in the post-order. It well-suited for a subsequent +% "chol", however. If you require a precise elimination tree post-ordering, +% then do: +% +% P = amd (S) ; +% C = spones (S) + spones (S') ; % skip this if S already symmetric +% [ignore, Q] = sparsfun ('symetree', C (P,P)) ; +% P = P (Q) ; +% +% -------------------------------------------------------------------------- +% AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, +% Patrick R. Amestoy, and Iain S. Duff. See ../README for License. +% email: davis@cise.ufl.edu CISE Department, Univ. of Florida. +% web: http://www.cise.ufl.edu/research/sparse/amd +% -------------------------------------------------------------------------- +% +% Acknowledgements: This work was supported by the National Science +% Foundation, under grants ASC-9111263, DMS-9223088, and CCR-0203270. +% +% See also COLMMD, COLAMD, COLPERM, SYMAMD, SYMMMD, SYMRCM. + +more on +help amd +more off +error ('amd mexFunction not found! Type "amd_make" in MATLAB to compile amd'); diff --git a/deal.II/contrib/umfpack/AMD/MATLAB/amd_demo.m b/deal.II/contrib/umfpack/AMD/MATLAB/amd_demo.m new file mode 100644 index 0000000000..a2d6c7226b --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/MATLAB/amd_demo.m @@ -0,0 +1,96 @@ +function amd_demo +% AMD DEMO +% +% A demo of AMD for MATLAB. +% +% -------------------------------------------------------------------------- +% AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, +% Patrick R. Amestoy, and Iain S. Duff. See ../README for License. +% email: davis@cise.ufl.edu CISE Department, Univ. of Florida. +% web: http://www.cise.ufl.edu/research/sparse/amd +% -------------------------------------------------------------------------- +% +% See also: amd, amd_make + +% This orders the same matrix as the ANSI C demo, amd_demo.c. It includes an +% additional analysis of the matrix via MATLAB's symbfact routine. + +% First, print the help information for AMD +help amd + +% Get the Harwell/Boeing can_24 matrix. This is an example matrix from the +% MATLAB-accessible UF sparse matrix collection, and can be loaded into +% MATLAB with the statment "Problem = UFget ('HB/can_24')", after obtaining +% the UFget function and its supporting routines at +% http://www.cise.ufl.edu/sparse/mat . + +load can_24 +A = Problem.A ; +n = size (A,1) ; + +figure (1) +clf +hold off +subplot (2,2,1) ; +spy (A) +% remove the "_" from the name before printing it in the plot title +title (sprintf ('%s', strrep (Problem.name, '_', '-'))) ; +fprintf ('Matrix name: %s\n', Problem.name) ; +fprintf ('Matrix title: %s\n', Problem.title) ; + +% print the details during AMD ordering and SYMBFACT +spparms ('spumoni', 1) ; + +% order the matrix. Note that the Info argument is optional. +fprintf ('\nIf the next step fails, then you have\n') ; +fprintf ('not yet compiled the AMD mexFunction.\n') ; +[p, Info] = amd (A) ; + +% order again, but this time print some statistics +[p, Info] = amd (A, [10 1 1]) ; + +fprintf ('Permutation vector:\n') ; +fprintf (' %2d', p) ; +fprintf ('\n\n') ; + +subplot (2,2,2) ; +spy (A (p,p)) +title ('Permuted matrix') ; + +% The amd_demo.c program stops here. + +fprintf ('Analyze A(p,p) with MATLAB''s symbfact routine:\n') ; +[cn, height, parent, post, R] = symbfact (A (p,p)) ; + +subplot (2,2,3) ; +spy (R') ; +title ('Cholesky factor, L') ; + +subplot (2,2,4) ; +treeplot (parent) ; +title ('elimination tree') ; + +% results from symbfact +lnz = sum (cn) ; % number of nonzeros in L, incl. diagonal +cn = cn - 1 ; % get the count of off-diagonal entries +fl = n + sum (cn.^2 + 2*cn) ; % flop count for chol (A (p,p) +fprintf ('number of nonzeros in L (including diagonal): %d\n', lnz) ; +fprintf ('floating point operation count for chol (A (p,p)): %d\n', fl) ; + +% approximations from amd: +lnz2 = n + Info (10) ; +fl2 = n + Info (11) + 2 * Info (12) ; +fprintf ('\nResults from AMD''s approximate analysis:\n') ; +fprintf ('number of nonzeros in L (including diagonal): %d\n', lnz2) ; +fprintf ('floating point operation count for chol (A (p,p)): %d\n\n', fl2) ; + +if (lnz2 ~= lnz | fl ~= fl2) + fprintf ('Note that the nonzero and flop counts from AMD are slight\n') ; + fprintf ('upper bounds. This is due to the approximate minimum degree\n'); + fprintf ('method used, in conjunction with "mass elimination".\n') ; + fprintf ('See the discussion about mass elimination in amd.h and\n') ; + fprintf ('amd_2.c for more details.\n') ; +end + +% turn off diagnostic output in MATLAB's sparse matrix routines +spparms ('spumoni', 0) ; diff --git a/deal.II/contrib/umfpack/AMD/MATLAB/amd_demo.m.out b/deal.II/contrib/umfpack/AMD/MATLAB/amd_demo.m.out new file mode 100644 index 0000000000..b77732f00c --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/MATLAB/amd_demo.m.out @@ -0,0 +1,136 @@ +>> amd_demo + + AMD Approximate minimum degree permutation. + P = AMD (S) returns the approximate minimum degree permutation vector for + the sparse matrix C = S+S'. The Cholesky factorization of C (P,P), or + S (P,P), tends to be sparser than that of C or S. AMD tends to be faster + than SYMMMD and SYMAMD, and tends to return better orderings than SYMMMD. + S must be square. If S is full, amd (S) is equivalent to amd (sparse (S)). + + Usage: P = amd (S) ; % finds the ordering + [P, Info] = amd (S, Control) ; % optional parameters & statistics + Control = amd ; % returns default parameters + amd ; % prints default parameters. + + Control (1); If S is n-by-n, then rows/columns with more than + max (16, (Control (1))* sqrt(n)) entries in S+S' are considered + "dense", and ignored during ordering. They are placed last in the + output permutation. The default is 10.0 if Control is not present. + Control (2): If nonzero, then aggressive absorption is performed. + This is the default if Control is not present. + Control (3): If nonzero, print statistics about the ordering. + + Info (1): status (0: ok, -1: out of memory, -2: matrix invalid) + Info (2): n = size (A,1) + Info (3): nnz (A) + Info (4): the symmetry of the matrix S (0.0 means purely unsymmetric, + 1.0 means purely symmetric). Computed as: + B = tril (S, -1) + triu (S, 1) ; symmetry = nnz (B & B') / nnz (B); + Info (5): nnz (diag (S)) + Info (6): nnz in S+S', excluding the diagonal (= nnz (B+B')) + Info (7): number "dense" rows/columns in S+S' + Info (8): the amount of memory used by AMD, in bytes + Info (9): the number of memory compactions performed by AMD + + The following statistics are slight upper bounds because of the + approximate degree in AMD. The bounds are looser if "dense" rows/columns + are ignored during ordering (Info (7) > 0). The statistics are for a + subsequent factorization of the matrix C (P,P). The LU factorization + statistics assume no pivoting. + + Info (10): the number of nonzeros in L, excluding the diagonal + Info (11): the number of divide operations for LL', LDL', or LU + Info (12): the number of multiply-subtract pairs for LL' or LDL' + Info (13): the number of multiply-subtract pairs for LU + Info (14): the max # of nonzeros in any column of L (incl. diagonal) + Info (15:20): unused, reserved for future use + + An assembly tree post-ordering is performed, which is typically the same + as an elimination tree post-ordering. It is not always identical because + of the approximate degree update used, and because "dense" rows/columns + do not take part in the post-order. It well-suited for a subsequent + "chol", however. If you require a precise elimination tree post-ordering, + then do: + + P = amd (S) ; + C = spones (S) + spones (S') ; % skip this if S already symmetric + [ignore, Q] = sparsfun ('symetree', C (P,P)) ; + P = P (Q) ; + + -------------------------------------------------------------------------- + AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, + Patrick R. Amestoy, and Iain S. Duff. See ../README for License. + email: davis@cise.ufl.edu CISE Department, Univ. of Florida. + web: http://www.cise.ufl.edu/research/sparse/amd + -------------------------------------------------------------------------- + + Acknowledgements: This work was supported by the National Science + Foundation, under grants ASC-9111263, DMS-9223088, and CCR-0203270. + + See also COLMMD, COLAMD, COLPERM, SYMAMD, SYMMMD, SYMRCM. + +Matrix name: HB/can_24 +Matrix title: 1SYMMETRIC PATTERN FROM CANNES,LUCIEN MARRO,JUNE 1981. + +If the next step fails, then you have +not yet compiled the AMD mexFunction. + +amd: approximate minimum degree ordering, parameters: + dense row parameter: 10 + (rows with more than max (10 * sqrt (n), 16) entries are + considered "dense", and placed last in output permutation) + aggressive absorption: yes + + input matrix A is 24-by-24 + input matrix A has 160 nonzero entries + +amd: approximate minimum degree ordering, results: + status: OK + n, dimension of A: 24 + nz, number of nonzeros in A: 160 + symmetry of A: 1.0000 + number of nonzeros on diagonal: 24 + nonzeros in pattern of A+A' (excl. diagonal): 136 + # dense rows/columns of A+A': 0 + memory used, in bytes: 1516 + # of memory compactions: 0 + + The following approximate statistics are for a subsequent + factorization of A(P,P) + A(P,P)'. They are slight upper + bounds if there are no dense rows/columns in A+A', and become + looser if dense rows/columns exist. + + nonzeros in L (excluding diagonal): 97 + nonzeros in L (including diagonal): 121 + # divide operations for LDL' or LU: 97 + # multiply-subtract operations for LDL': 275 + # multiply-subtract operations for LU: 453 + max nz. in any column of L (incl. diagonal): 8 + + chol flop count for real A, sqrt counted as 1 flop: 671 + LDL' flop count for real A: 647 + LDL' flop count for complex A: 3073 + LU flop count for real A (with no pivoting): 1003 + LU flop count for complex A (with no pivoting): 4497 + +Permutation vector: + 23 21 11 24 13 6 17 9 15 5 16 8 2 10 14 18 1 3 4 7 12 19 22 20 + +Analyze A(p,p) with MATLAB's symbfact routine: +predicted nonzeros: 120 +predicted flops: 656 +predicted height: 16 +predicted front size: 7 +number of nonzeros in L (including diagonal): 120 +floating point operation count for chol (A (p,p)): 656 + +Results from AMD's approximate analysis: +number of nonzeros in L (including diagonal): 121 +floating point operation count for chol (A (p,p)): 671 + +Note that the nonzero and flop counts from AMD are slight +upper bounds. This is due to the approximate minimum degree +method used, in conjunction with "mass elimination". +See the discussion about mass elimination in amd.h and +amd_2.c for more details. +>> diary off diff --git a/deal.II/contrib/umfpack/AMD/MATLAB/amd_make.m b/deal.II/contrib/umfpack/AMD/MATLAB/amd_make.m new file mode 100644 index 0000000000..4668c55fce --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/MATLAB/amd_make.m @@ -0,0 +1,32 @@ +function amd_make +% AMD_MAKE: compiles the AMD mexFunction for MATLAB +% +% -------------------------------------------------------------------------- +% AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, +% Patrick R. Amestoy, and Iain S. Duff. See ../README for License. +% email: davis@cise.ufl.edu CISE Department, Univ. of Florida. +% web: http://www.cise.ufl.edu/research/sparse/amd +% -------------------------------------------------------------------------- +% +% See also: amd, amd_demo + +help amd_make +fprintf ('Compiling the AMD mexFunction:\n') ; +cmd = sprintf ('mex -inline -O -output amd -I..%sInclude amd_mex.c', filesep) ; +files = {'amd_order', 'amd_dump', 'amd_postorder', 'amd_post_tree', ... + 'amd_aat', 'amd_2', 'amd_1', 'amd_defaults', 'amd_control', 'amd_info', ... + 'amd_valid' } ; +for i = 1 : length (files) + cmd = sprintf ('%s ..%sSource%s%s.c', cmd, filesep, filesep, files {i}) ; +end +fprintf ('%s\n', cmd) ; +try + eval (cmd) ; +catch + fprintf ('Compilation not successful.\n') ; +end + +input ('\nHit enter to run the AMD demo\n') ; +more on +amd_demo +more off diff --git a/deal.II/contrib/umfpack/AMD/MATLAB/amd_mex.c b/deal.II/contrib/umfpack/AMD/MATLAB/amd_mex.c new file mode 100644 index 0000000000..0b36690292 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/MATLAB/amd_mex.c @@ -0,0 +1,189 @@ +/* ========================================================================= */ +/* === AMD mexFunction ===================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* + * Usage: + * p = amd (A) + * p = amd (A, Control) + * [p, Info] = amd (A) + * [p, Info] = amd (A, Control) + * Control = amd ; % return the default Control settings for AMD + * amd ; % print the default Control settings for AMD + * + * Given a square matrix A, compute a permutation P suitable for a Cholesky + * factorization of the matrix B (P,P), where B = spones (A) + spones (A'). + * The method used is the approximate minimum degree ordering method. See + * amd.m and amd.h for more information. + */ + +#include "amd.h" +#include "mex.h" +#include "matrix.h" + +void mexFunction +( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] +) +{ + int i, m, n, *Ap, *Ai, *P, nc, result, spumoni, full ; + double *Pout, *InfoOut, Control [AMD_CONTROL], Info [AMD_INFO], *ControlIn; + mxArray *A, *string, *parameter ; + + /* --------------------------------------------------------------------- */ + /* get control parameters */ + /* --------------------------------------------------------------------- */ + + spumoni = 0 ; + if (nrhs == 0) + { + /* get the default control parameters, and return */ + plhs [0] = mxCreateDoubleMatrix (AMD_CONTROL, 1, mxREAL) ; + amd_defaults (mxGetPr (plhs [0])) ; + if (nlhs == 0) + { + amd_control (mxGetPr (plhs [0])) ; + } + return ; + } + + amd_defaults (Control) ; + if (nrhs > 1) + { + ControlIn = mxGetPr (prhs [1]) ; + nc = mxGetM (prhs [1]) * mxGetN (prhs [1]) ; + Control [AMD_DENSE] + = (nc > 0) ? ControlIn [AMD_DENSE] : AMD_DEFAULT_DENSE ; + Control [AMD_AGGRESSIVE] + = (nc > 1) ? ControlIn [AMD_AGGRESSIVE] : AMD_DEFAULT_AGGRESSIVE ; + spumoni = (nc > 2) ? (ControlIn [2] != 0) : 0 ; + } + + if (spumoni > 0) + { + amd_control (Control) ; + } + + /* --------------------------------------------------------------------- */ + /* get inputs */ + /* --------------------------------------------------------------------- */ + + if (nlhs > 2 || nrhs > 2) + { + mexErrMsgTxt ("Usage: p = amd (A)\nor [p, Info] = amd (A, Control)") ; + } + + A = (mxArray *) prhs [0] ; + n = mxGetN (A) ; + m = mxGetM (A) ; + if (spumoni > 0) + { + mexPrintf (" input matrix A is %d-by-%d\n", m, n) ; + } + if (mxGetNumberOfDimensions (A) != 2) + { + mexErrMsgTxt ("amd: A must be 2-dimensional") ; + } + if (m != n) + { + mexErrMsgTxt ("amd: A must be square") ; + } + + /* --------------------------------------------------------------------- */ + /* allocate workspace for output permutation */ + /* --------------------------------------------------------------------- */ + + P = mxMalloc ((n+1) * sizeof (int)) ; + + /* --------------------------------------------------------------------- */ + /* if A is full, convert to a sparse matrix */ + /* --------------------------------------------------------------------- */ + + full = !mxIsSparse (A) ; + if (full) + { + if (spumoni > 0) + { + mexPrintf ( + " input matrix A is full (sparse copy of A will be created)\n"); + } + mexCallMATLAB (1, &A, 1, (mxArray **) prhs, "sparse") ; + } + Ap = mxGetJc (A) ; + Ai = mxGetIr (A) ; + if (spumoni > 0) + { + mexPrintf (" input matrix A has %d nonzero entries\n", Ap [n]) ; + } + + /* --------------------------------------------------------------------- */ + /* order the matrix */ + /* --------------------------------------------------------------------- */ + + result = amd_order (n, Ap, Ai, P, Control, Info) ; + + /* --------------------------------------------------------------------- */ + /* if A is full, free the sparse copy of A */ + /* --------------------------------------------------------------------- */ + + if (full) + { + mxDestroyArray (A) ; + } + + /* --------------------------------------------------------------------- */ + /* print results (including return value) */ + /* --------------------------------------------------------------------- */ + + if (spumoni > 0) + { + amd_info (Info) ; + } + + /* --------------------------------------------------------------------- */ + /* check error conditions */ + /* --------------------------------------------------------------------- */ + + if (result == AMD_OUT_OF_MEMORY) + { + mexErrMsgTxt ("amd: out of memory") ; + } + else if (result == AMD_INVALID) + { + mexErrMsgTxt ("amd: input matrix A is corrupted") ; + } + + /* --------------------------------------------------------------------- */ + /* copy the outputs to MATLAB */ + /* --------------------------------------------------------------------- */ + + /* output permutation, P */ + plhs [0] = mxCreateDoubleMatrix (1, n, mxREAL) ; + Pout = mxGetPr (plhs [0]) ; + for (i = 0 ; i < n ; i++) + { + Pout [i] = P [i] + 1 ; /* change to 1-based indexing for MATLAB */ + } + mxFree (P) ; + + /* Info */ + if (nlhs > 1) + { + plhs [1] = mxCreateDoubleMatrix (AMD_INFO, 1, mxREAL) ; + InfoOut = mxGetPr (plhs [1]) ; + for (i = 0 ; i < AMD_INFO ; i++) + { + InfoOut [i] = Info [i] ; + } + } +} diff --git a/deal.II/contrib/umfpack/AMD/MATLAB/can_24.mat b/deal.II/contrib/umfpack/AMD/MATLAB/can_24.mat new file mode 100644 index 0000000000000000000000000000000000000000..a346d0d3fbdb93df3f2912c3be7c7ac2b56a8b2e GIT binary patch literal 1560 zcmd6nJ#Q015QfM3AbbYHp%yjjj~ewzgJ>=MyHzY{n%QNu#Up*sh_hl& zV2grzrNmNaGJZSAhGq*zJePlPiDKO% zvue*3AuH&Fb$hJ8s~Z3PSJW~8Q`)=^^nVv#$8C+FA~5;qKK&jRIjp-VE{UsRMO+g% z1Z%jnbAmWMBZoU>-34)7kb6_iizRVhToJw(y~EEw>_Kcnqoc7HUQZHm%eBoJY+2hpy8!psiew_XiEG*L`vo zd$6zfbo%bd@w?z`zYp&VeZ=#{hObP%1 literal 0 HcmV?d00001 diff --git a/deal.II/contrib/umfpack/AMD/README b/deal.II/contrib/umfpack/AMD/README new file mode 100644 index 0000000000..e47f18e60a --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/README @@ -0,0 +1,202 @@ +AMD version 1.1: a set of routines for permuting sparse matrices prior to + factorization. Includes a version in C, a version in Fortran, and a MATLAB + mexFunction. + +Quick start (Unix, or Windows with Cygwin): + + To compile, test, and install AMD, you may wish to first configure the + installation by editting the AMD/Make/Make.include file. Next, cd to this + directory (AMD) and type "make" (or "make lib" if you do not have MATLAB). + To compile and run a demo program for the Fortran version, type + "make fortran". When done, type "make clean" to remove unused *.o files + (keeps the compiled libraries and demo programs). See the User Guide + (Doc/AMD_UserGuide.pdf), or AMD/Make/Make.include, for more details. + +Quick start (for MATLAB users); + + To compile, test, and install the AMD mexFunction, cd to the + AMD/MATLAB directory and type amd_make at the MATLAB prompt. + This works on any system supported by MATLAB. + +------------------------------------------------------------------------------- + +AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. +Davis, Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. + +AMD License: + + Your use or distribution of AMD or any modified version of + AMD implies that you agree to this License. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY + EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to use or copy this program, provided + that the Copyright, this License, and the Availability of the original + version is retained on all copies. User documentation of any code that + uses AMD or any modified version of AMD code must cite the + Copyright, this License, the Availability note, and "Used by permission." + Permission to modify the code and to distribute modified code is granted, + provided the Copyright, this License, and the Availability note are + retained, and a notice that the code was modified is included. This + software was developed with support from the National Science Foundation, + and is provided to you free of charge. + +Availability: + + http://www.cise.ufl.edu/research/sparse/amd + +------------------------------------------------------------------------------- + +This is the AMD Version 1.1 README file. It is a terse overview of AMD. +Refer to the User Guide (Doc/AMD_UserGuide.pdf) for how to install and use AMD. + +Description: + + AMD is a set of routines for pre-ordering sparse matrices prior to Cholesky + or LU factorization, using the approximate minimum degree ordering + algorithm. Written in ANSI/ISO C with a MATLAB interface, and in + Fortran 77. + +Authors: + + Timothy A. Davis (davis@cise.ufl.edu), University of Florida. + Patrick R. Amestory, ENSEEIHT, Toulouse, France. + Iain S. Duff, Rutherford Appleton Laboratory, UK. + +Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974, DMS-9803599, and CCR-0203270. + + Portions of this work were done while on sabbatical at Stanford University + and Lawrence Berkeley National Laboratory (with funding from the SciDAC + program). I would like to thank Gene Golub, Esmond Ng, and Horst Simon + for making this sabbatical possible. + +------------------------------------------------------------------------------- +Files and directories in the AMD v1.0 distribution: +------------------------------------------------------------------------------- + + --------------------------------------------------------------------------- + Subdirectories of the AMD directory: + --------------------------------------------------------------------------- + + Doc documentation + Make for compiling AMD + Source primary source code + Include include file for use in your code that calls AMD + Demo demo programs. also serves as test of the AMD installation. + MATLAB AMD mexFunction for MATLAB, and supporting m-files + Lib where the compiled C-callable and Fortran-callable + AMD libraries placed. + + --------------------------------------------------------------------------- + Files in the AMD directory: + --------------------------------------------------------------------------- + + Makefile top-level Makefile for GNU make or original make. + Windows users would require Cygwin to use "make" + + README this file + + --------------------------------------------------------------------------- + Doc directory: documentation + --------------------------------------------------------------------------- + + ChangeLog change log + License the AMD License + Makefile for creating the documentation + AMD_UserGuide.bib AMD User Guide (references) + AMD_UserGuide.tex AMD User Guide (LaTeX) + AMD_UserGuide.pdf AmD User Guide (PDF) + + --------------------------------------------------------------------------- + Make directory: for compiling AMD (Lib/libamd.a and Lib/libamdf77.a) + --------------------------------------------------------------------------- + + Make.include overall configurations. Can use one of: + Make.alpha Makefile additions for Compaq Alpha + Make.linux Makefile additions for Linux + Make.rs6000 Makefile additions for RS 6000 + Make.sgi Makefile additions for SGI + Make.solaris Makefile additions for Solaris + + --------------------------------------------------------------------------- + Source directory: + --------------------------------------------------------------------------- + + GNUmakefile a nice Makefile, for GNU make + Makefile an ugly Unix Makefile (for older make's) + + amd_order.c user-callable, primary AMD ordering routine + amd_control.c user-callable, prints the control parameters + amd_defaults.c user-callable, sets default control parameters + amd_info.c user-callable, prints the statistics from AMD + + amd_1.c non-user-callable, construct A+A' + amd_2.c non-user-callable, primary ordering kernel + (a C version of amd.f and amdbar.f, with + post-ordering added) + amd_aat.c non-user-callable, computes nnz (A+A') + amd_dump.c non-user-callable, debugging routines + amd_internal.h non-user-callable, include file for AMD + amd_mex.c non-user-callable, MATLAB mexFunction + amd_postorder.c non-user-callable, postorder + amd_post_tree.c non-user-callable, postorder just one tree + amd_valid.c non-user-callable, verifies a matrix + + amd.f user-callable Fortran 77 version + amdbar.f user-callable Fortran 77 version + + --------------------------------------------------------------------------- + Include directory: + --------------------------------------------------------------------------- + + amd.h include file for C programs that use AMD + + --------------------------------------------------------------------------- + Demo directory: + --------------------------------------------------------------------------- + + Makefile for GNU make or original make + + amd_demo.c C demo program for AMD + amd_demo.out output of amd_demo.c + + amd_simple.c simple C demo program for AMD + amd_simple.out output of amd_simple.c + + amd_f77demo.f Fortran 77 demo program for AMD + amd_f77demo.out output of amd_f77demo.f + + amd_f77simple.c simple Fortran 77 demo program for AMD + amd_f77simple.out output of amd_f77simple.f + + amd_f77cross.f Fortran 77 demo, calls the C version of AMD + amd_f77cross.out output of amd_f77cross.f + amd_f77wrapper.c Fortran-callable wrapper for C version of AMD + + --------------------------------------------------------------------------- + MATLAB directory: + --------------------------------------------------------------------------- + + GNUmakefile a nice Makefile, for GNU make + Makefile an ugly Unix Makefile (for older make's) + + Contents.m for "help amd" listing of toolbox contents + + amd.m MATLAB help file for AMD + amd_make.m MATLAB m-file for compiling AMD mexFunction + + amd_mex.c AMD mexFunction for MATLAB + + amd_demo.m MATLAB demo for AMD + amd_demo.m.out diary output of amd_demo.m + can_24.mat input file for AMD demo + + --------------------------------------------------------------------------- + Lib directory: libamd.a and libamdf77.a libraries placed here + --------------------------------------------------------------------------- + + libamd.def AMD definitions for Windows diff --git a/deal.II/contrib/umfpack/AMD/Source/Makefile b/deal.II/contrib/umfpack/AMD/Source/Makefile new file mode 100644 index 0000000000..a285ac591b --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/Makefile @@ -0,0 +1,35 @@ +#------------------------------------------------------------------------------- +# AMD Makefile for compiling on Unix systems (for GNU make only) +#------------------------------------------------------------------------------- + +include ../../../../common/Make.global_options + +#------------------------------------------------------------------------------- +# source files +#------------------------------------------------------------------------------- + +AMD = amd_aat amd_1 amd_2 amd_dump amd_postorder amd_post_tree amd_defaults \ + amd_order amd_control amd_info amd_valid amd_preprocess + +INC = ../Include/amd.h amd_internal.h + +#------------------------------------------------------------------------------- +# object files for each version +#------------------------------------------------------------------------------- + +AMDI = $(addprefix $D/lib/umfpack/, $(addsuffix .$(OBJEXT), $(subst amd_,amd_i_,$(AMD)))) +AMDL = $(addprefix $D/lib/umfpack/, $(addsuffix .$(OBJEXT), $(subst amd_,amd_l_,$(AMD)))) + +#------------------------------------------------------------------------------- +# compile each int and long routine (with no real/complex version) +#------------------------------------------------------------------------------- + +$D/lib/umfpack/amd_i_%.$(OBJEXT): amd_%.c $(INC) + @echo =====umfpack==========optimized==$(MT)== $( 0 */ + const Int Ap [ ], /* input of size n+1, not modified */ + const Int Ai [ ], /* input of size nz = Ap [n], not modified */ + Int P [ ], /* size n output permutation */ + Int Pinv [ ], /* size n output inverse permutation */ + Int Len [ ], /* size n input, undefined on output */ + Int slen, /* slen >= sum (Len [0..n-1]) + 7n, + * ideally slen = 1.2 * sum (Len) + 8n */ + Int S [ ], /* size slen workspace */ + double Control [ ], /* input array of size AMD_CONTROL */ + double Info [ ] /* output array of size AMD_INFO */ +) +{ + Int i, j, k, p, pfree, iwlen, pj, p1, p2, pj2, *Iw, *Pe, *Nv, *Head, + *Elen, *Degree, *s, *W, *Sp, *Tp ; + + /* --------------------------------------------------------------------- */ + /* construct the matrix for AMD_2 */ + /* --------------------------------------------------------------------- */ + + ASSERT (n > 0) ; + + iwlen = slen - 6*n ; + s = S ; + Pe = s ; s += n ; + Nv = s ; s += n ; + Head = s ; s += n ; + Elen = s ; s += n ; + Degree = s ; s += n ; + W = s ; s += n ; + Iw = s ; s += iwlen ; + + ASSERT (AMD_valid (n, n, Ap, Ai)) ; + + /* construct the pointers for A+A' */ + Sp = Nv ; /* use Nv and W as workspace for Sp and Tp [ */ + Tp = W ; + pfree = 0 ; + for (j = 0 ; j < n ; j++) + { + Pe [j] = pfree ; + Sp [j] = pfree ; + pfree += Len [j] ; + } + + /* Note that this restriction on iwlen is slightly more restrictive than + * what is strictly required in AMD_2. AMD_2 can operate with no elbow + * room at all, but it will be very slow. For better performance, at + * least size-n elbow room is enforced. */ + ASSERT (iwlen >= pfree + n) ; + +#ifndef NDEBUG + for (p = 0 ; p < iwlen ; p++) Iw [p] = EMPTY ; +#endif + + for (k = 0 ; k < n ; k++) + { + AMD_DEBUG1 (("Construct row/column k= "ID" of A+A'\n", k)) ; + p1 = Ap [k] ; + p2 = Ap [k+1] ; + + /* construct A+A' */ + for (p = p1 ; p < p2 ; ) + { + /* scan the upper triangular part of A */ + j = Ai [p] ; + ASSERT (j >= 0 && j < n) ; + if (j < k) + { + /* entry A (j,k) in the strictly upper triangular part */ + ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; + ASSERT (Sp [k] < (k == n-1 ? pfree : Pe [k+1])) ; + Iw [Sp [j]++] = k ; + Iw [Sp [k]++] = j ; + p++ ; + } + else if (j == k) + { + /* skip the diagonal */ + p++ ; + break ; + } + else /* j > k */ + { + /* first entry below the diagonal */ + break ; + } + /* scan lower triangular part of A, in column j until reaching + * row k. Start where last scan left off. */ + ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; + pj2 = Ap [j+1] ; + for (pj = Tp [j] ; pj < pj2 ; ) + { + i = Ai [pj] ; + ASSERT (i >= 0 && i < n) ; + if (i < k) + { + /* A (i,j) is only in the lower part, not in upper */ + ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; + ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; + Iw [Sp [i]++] = j ; + Iw [Sp [j]++] = i ; + pj++ ; + } + else if (i == k) + { + /* entry A (k,j) in lower part and A (j,k) in upper */ + pj++ ; + break ; + } + else /* i > k */ + { + /* consider this entry later, when k advances to i */ + break ; + } + } + Tp [j] = pj ; + } + Tp [k] = p ; + } + + /* clean up, for remaining mismatched entries */ + for (j = 0 ; j < n ; j++) + { + for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) + { + i = Ai [pj] ; + ASSERT (i >= 0 && i < n) ; + /* A (i,j) is only in the lower part, not in upper */ + ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; + ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; + Iw [Sp [i]++] = j ; + Iw [Sp [j]++] = i ; + } + } + +#ifndef NDEBUG + for (j = 0 ; j < n-1 ; j++) ASSERT (Sp [j] == Pe [j+1]) ; + ASSERT (Sp [n-1] == pfree) ; +#endif + + /* Tp and Sp no longer needed ] */ + + /* --------------------------------------------------------------------- */ + /* order the matrix */ + /* --------------------------------------------------------------------- */ + + AMD_2 (n, Pe, Iw, Len, iwlen, pfree, + Nv, Pinv, P, Head, Elen, Degree, W, Control, Info) ; +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_2.c b/deal.II/contrib/umfpack/AMD/Source/amd_2.c new file mode 100644 index 0000000000..ca1141e265 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_2.c @@ -0,0 +1,1823 @@ +/* ========================================================================= */ +/* === AMD_2 =============================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* AMD_2: performs the AMD ordering on a symmetric sparse matrix A, followed + * by a postordering (via depth-first search) of the assembly tree using the + * AMD_postorder routine. + */ + +#include "amd_internal.h" + +GLOBAL void AMD_2 +( + Int n, /* A is n-by-n, where n > 0 */ + Int Pe [ ], /* Pe [0..n-1]: index in Iw of row i on input */ + Int Iw [ ], /* workspace of size iwlen. Iw [0..pfree-1] + * holds the matrix on input */ + Int Len [ ], /* Len [0..n-1]: length for row/column i on input */ + Int iwlen, /* length of Iw. iwlen >= pfree + n */ + Int pfree, /* Iw [pfree ... iwlen-1] is empty on input */ + + /* 7 size-n workspaces, not defined on input: */ + Int Nv [ ], /* the size of each supernode on output */ + Int Next [ ], /* the output inverse permutation */ + Int Last [ ], /* the output permutation */ + Int Head [ ], + Int Elen [ ], /* the size columns of L for each supernode */ + Int Degree [ ], + Int W [ ], + + /* control parameters and output statistics */ + double Control [ ], /* array of size AMD_CONTROL */ + double Info [ ] /* array of size AMD_INFO */ +) +{ + +/* + * Given a representation of the nonzero pattern of a symmetric matrix, A, + * (excluding the diagonal) perform an approximate minimum (UMFPACK/MA38-style) + * degree ordering to compute a pivot order such that the introduction of + * nonzeros (fill-in) in the Cholesky factors A = LL' is kept low. At each + * step, the pivot selected is the one with the minimum UMFAPACK/MA38-style + * upper-bound on the external degree. This routine can optionally perform + * aggresive absorption (as done by MC47B in the Harwell Subroutine + * Library). + * + * The approximate degree algorithm implemented here is the symmetric analog of + * the degree update algorithm in MA38 and UMFPACK (the Unsymmetric-pattern + * MultiFrontal PACKage, both by Davis and Duff). The routine is based on the + * MA27 minimum degree ordering algorithm by Iain Duff and John Reid. + * + * This routine is a translation of the original AMDBAR and MC47B routines, + * in Fortran, with the following modifications: + * + * (1) dense rows/columns are removed prior to ordering the matrix, and placed + * last in the output order. The presence of a dense row/column can + * increase the ordering time by up to O(n^2), unless they are removed + * prior to ordering. + * + * (2) the minimum degree ordering is followed by a postordering (depth-first + * search) of the assembly tree. Note that mass elimination (discussed + * below) combined with the approximate degree update can lead to the mass + * elimination of nodes with lower exact degree than the current pivot + * element. No additional fill-in is caused in the representation of the + * Schur complement. The mass-eliminated nodes merge with the current + * pivot element. They are ordered prior to the current pivot element. + * Because they can have lower exact degree than the current element, the + * merger of two or more of these nodes in the current pivot element can + * lead to a single element that is not a "fundamental supernode". The + * diagonal block can have zeros in it. Thus, the assembly tree used here + * is not guaranteed to be the precise supernodal elemination tree (with + * "funadmental" supernodes), and the postordering performed by this + * routine is not guaranteed to be a precise postordering of the + * elimination tree. + * + * (3) input parameters are added, to control aggressive absorption and the + * detection of "dense" rows/columns of A. + * + * (4) additional statistical information is returned, such as the number of + * nonzeros in L, and the flop counts for subsequent LDL' and LU + * factorizations. These are slight upper bounds, because of the mass + * elimination issue discussed above. + * + * (5) additional routines are added to interface this routine to MATLAB + * to provide a simple C-callable user-interface, to check inputs for + * errors, compute the symmetry of the pattern of A and the number of + * nonzeros in each row/column of A+A', to compute the pattern of A+A', + * to perform the assembly tree postordering, and to provide debugging + * ouput. Many of these functions are also provided by the Fortran + * Harwell Subroutine Library routine MC47A. + * + * (6) both "int" and "long" versions are provided. In the descriptions below + * and integer is and "int" or "long", depending on which version is + * being used. + + ********************************************************************** + ***** CAUTION: ARGUMENTS ARE NOT CHECKED FOR ERRORS ON INPUT. ****** + ********************************************************************** + ** If you want error checking, a more versatile input format, and a ** + ** simpler user interface, use amd_order or amd_l_order instead. ** + ** This routine is not meant to be user-callable. ** + ********************************************************************** + + * ---------------------------------------------------------------------------- + * References: + * ---------------------------------------------------------------------------- + * + * [1] Timothy A. Davis and Iain Duff, "An unsymmetric-pattern multifrontal + * method for sparse LU factorization", SIAM J. Matrix Analysis and + * Applications, vol. 18, no. 1, pp. 140-158. Discusses UMFPACK / MA38, + * which first introduced the approximate minimum degree used by this + * routine. + * + * [2] Patrick Amestoy, Timothy A. Davis, and Iain S. Duff, "An approximate + * minimum degree ordering algorithm," SIAM J. Matrix Analysis and + * Applications, vol. 17, no. 4, pp. 886-905, 1996. Discusses AMDBAR and + * MC47B, which are the Fortran versions of this routine. + * + * [3] Alan George and Joseph Liu, "The evolution of the minimum degree + * ordering algorithm," SIAM Review, vol. 31, no. 1, pp. 1-19, 1989. + * We list below the features mentioned in that paper that this code + * includes: + * + * mass elimination: + * Yes. MA27 relied on supervariable detection for mass elimination. + * + * indistinguishable nodes: + * Yes (we call these "supervariables"). This was also in the MA27 + * code - although we modified the method of detecting them (the + * previous hash was the true degree, which we no longer keep track + * of). A supervariable is a set of rows with identical nonzero + * pattern. All variables in a supervariable are eliminated together. + * Each supervariable has as its numerical name that of one of its + * variables (its principal variable). + * + * quotient graph representation: + * Yes. We use the term "element" for the cliques formed during + * elimination. This was also in the MA27 code. The algorithm can + * operate in place, but it will work more efficiently if given some + * "elbow room." + * + * element absorption: + * Yes. This was also in the MA27 code. + * + * external degree: + * Yes. The MA27 code was based on the true degree. + * + * incomplete degree update and multiple elimination: + * No. This was not in MA27, either. Our method of degree update + * within MC47B is element-based, not variable-based. It is thus + * not well-suited for use with incomplete degree update or multiple + * elimination. + * + * Authors, and Copyright (C) 2004 by: + * Timothy A. Davis, Patrick Amestoy, Iain S. Duff, John K. Reid. + * + * Acknowledgements: This work (and the UMFPACK package) was supported by the + * National Science Foundation (ASC-9111263, DMS-9223088, and CCR-0203270). + * The UMFPACK/MA38 approximate degree update algorithm, the unsymmetric analog + * which forms the basis of AMD, was developed while Tim Davis was supported by + * CERFACS (Toulouse, France) in a post-doctoral position. This C version, and + * the etree postorder, were written while Tim Davis was on sabbatical at + * Stanford University and Lawrence Berkeley National Laboratory. + + * ---------------------------------------------------------------------------- + * INPUT ARGUMENTS (unaltered): + * ---------------------------------------------------------------------------- + + * n: The matrix order. Restriction: n >= 1. + * + * iwlen: The size of the Iw array. On input, the matrix is stored in + * Iw [0..pfree-1]. However, Iw [0..iwlen-1] should be slightly larger + * than what is required to hold the matrix, at least iwlen >= pfree + n. + * Otherwise, excessive compressions will take place. The recommended + * value of iwlen is 1.2 * pfree + n, which is the value used in the + * user-callable interface to this routine (amd_order.c). The algorithm + * will not run at all if iwlen < pfree. Restriction: iwlen >= pfree + n. + * Note that this is slightly more restrictive than the actual minimum + * (iwlen >= pfree), but AMD_2 will be very slow with no elbow room. + * Thus, this routine enforces a bare minimum elbow room of size n. + * + * pfree: On input the tail end of the array, Iw [pfree..iwlen-1], is empty, + * and the matrix is stored in Iw [0..pfree-1]. During execution, + * additional data is placed in Iw, and pfree is modified so that + * Iw [pfree..iwlen-1] is always the unused part of Iw. + * + * Control: A double array of size AMD_CONTROL containing input parameters + * that affect how the ordering is computed. If NULL, then default + * settings are used. + * + * Control [AMD_DENSE] is used to determine whether or not a given input + * row is "dense". A row is "dense" if the number of entries in the row + * exceeds Control [AMD_DENSE] times sqrt (n), except that rows with 16 or + * fewer entries are never considered "dense". To turn off the detection + * of dense rows, set Control [AMD_DENSE] to a negative number, or to a + * number larger than sqrt (n). The default value of Control [AMD_DENSE] + * is AMD_DEFAULT_DENSE, which is defined in amd.h as 10. + * + * Control [AMD_AGGRESSIVE] is used to determine whether or not aggressive + * absorption is to be performed. If nonzero, then aggressive absorption + * is performed (this is the default). + + * ---------------------------------------------------------------------------- + * INPUT/OUPUT ARGUMENTS: + * ---------------------------------------------------------------------------- + * + * Pe: An integer array of size n. On input, Pe [i] is the index in Iw of + * the start of row i. Pe [i] is ignored if row i has no off-diagonal + * entries. Thus Pe [i] must be in the range 0 to pfree-1 for non-empty + * rows. + * + * During execution, it is used for both supervariables and elements: + * + * Principal supervariable i: index into Iw of the description of + * supervariable i. A supervariable represents one or more rows of + * the matrix with identical nonzero pattern. In this case, + * Pe [i] >= 0. + * + * Non-principal supervariable i: if i has been absorbed into another + * supervariable j, then Pe [i] = FLIP (j), where FLIP (j) is defined + * as (-(j)-2). Row j has the same pattern as row i. Note that j + * might later be absorbed into another supervariable j2, in which + * case Pe [i] is still FLIP (j), and Pe [j] = FLIP (j2) which is + * < EMPTY, where EMPTY is defined as (-1) in amd_internal.h. + * + * Unabsorbed element e: the index into Iw of the description of element + * e, if e has not yet been absorbed by a subsequent element. Element + * e is created when the supervariable of the same name is selected as + * the pivot. In this case, Pe [i] >= 0. + * + * Absorbed element e: if element e is absorbed into element e2, then + * Pe [e] = FLIP (e2). This occurs when the pattern of e (which we + * refer to as Le) is found to be a subset of the pattern of e2 (that + * is, Le2). In this case, Pe [i] < EMPTY. If element e is "null" + * (it has no nonzeros outside its pivot block), then Pe [e] = EMPTY, + * and e is the root of an assembly subtree (or the whole tree if + * there is just one such root). + * + * Dense variable i: if i is "dense", then Pe [i] = EMPTY. + * + * On output, Pe holds the assembly tree/forest, which implicitly + * represents a pivot order with identical fill-in as the actual order + * (via a depth-first search of the tree), as follows. If Nv [i] > 0, + * then i represents a node in the assembly tree, and the parent of i is + * Pe [i], or EMPTY if i is a root. If Nv [i] = 0, then (i, Pe [i]) + * represents an edge in a subtree, the root of which is a node in the + * assembly tree. Note that i refers to a row/column in the original + * matrix, not the permuted matrix. + * + * Info: A double array of size AMD_INFO. If present, (that is, not NULL), + * then statistics about the ordering are returned in the Info array. + * See amd.h for a description. + + * ---------------------------------------------------------------------------- + * INPUT/MODIFIED (undefined on output): + * ---------------------------------------------------------------------------- + * + * Len: An integer array of size n. On input, Len [i] holds the number of + * entries in row i of the matrix, excluding the diagonal. The contents + * of Len are undefined on output. + * + * Iw: An integer array of size iwlen. On input, Iw [0..pfree-1] holds the + * description of each row i in the matrix. The matrix must be symmetric, + * and both upper and lower triangular parts must be present. The + * diagonal must not be present. Row i is held as follows: + * + * Len [i]: the length of the row i data structure in the Iw array. + * Iw [Pe [i] ... Pe [i] + Len [i] - 1]: + * the list of column indices for nonzeros in row i (simple + * supervariables), excluding the diagonal. All supervariables + * start with one row/column each (supervariable i is just row i). + * If Len [i] is zero on input, then Pe [i] is ignored on input. + * + * Note that the rows need not be in any particular order, and there + * may be empty space between the rows. + * + * During execution, the supervariable i experiences fill-in. This is + * represented by placing in i a list of the elements that cause fill-in + * in supervariable i: + * + * Len [i]: the length of supervariable i in the Iw array. + * Iw [Pe [i] ... Pe [i] + Elen [i] - 1]: + * the list of elements that contain i. This list is kept short + * by removing absorbed elements. + * Iw [Pe [i] + Elen [i] ... Pe [i] + Len [i] - 1]: + * the list of supervariables in i. This list is kept short by + * removing nonprincipal variables, and any entry j that is also + * contained in at least one of the elements (j in Le) in the list + * for i (e in row i). + * + * When supervariable i is selected as pivot, we create an element e of + * the same name (e=i): + * + * Len [e]: the length of element e in the Iw array. + * Iw [Pe [e] ... Pe [e] + Len [e] - 1]: + * the list of supervariables in element e. + * + * An element represents the fill-in that occurs when supervariable i is + * selected as pivot (which represents the selection of row i and all + * non-principal variables whose principal variable is i). We use the + * term Le to denote the set of all supervariables in element e. Absorbed + * supervariables and elements are pruned from these lists when + * computationally convenient. + * + * CAUTION: THE INPUT MATRIX IS OVERWRITTEN DURING COMPUTATION. + * The contents of Iw are undefined on output. + + * ---------------------------------------------------------------------------- + * OUTPUT (need not be set on input): + * ---------------------------------------------------------------------------- + * + * Nv: An integer array of size n. During execution, ABS (Nv [i]) is equal to + * the number of rows that are represented by the principal supervariable + * i. If i is a nonprincipal or dense variable, then Nv [i] = 0. + * Initially, Nv [i] = 1 for all i. Nv [i] < 0 signifies that i is a + * principal variable in the pattern Lme of the current pivot element me. + * After element me is constructed, Nv [i] is set back to a positive + * value. + * + * On output, Nv [i] holds the number of pivots represented by super + * row/column i of the original matrix, or Nv [i] = 0 for non-principal + * rows/columns. Note that i refers to a row/column in the original + * matrix, not the permuted matrix. + * + * Elen: An integer array of size n. See the description of Iw above. At the + * start of execution, Elen [i] is set to zero for all rows i. During + * execution, Elen [i] is the number of elements in the list for + * supervariable i. When e becomes an element, Elen [e] = FLIP (esize) is + * set, where esize is the size of the element (the number of pivots, plus + * the number of nonpivotal entries). Thus Elen [e] < EMPTY. + * Elen (i) = EMPTY set when variable i becomes nonprincipal. + * + * For variables, Elen (i) >= EMPTY holds until just before the + * postordering and permutation vectors are computed. For elements, + * Elen [e] < EMPTY holds. + * + * On output, Elen [i] is the degree of the row/column in the Cholesky + * factorization of the permuted matrix, corresponding to the original row + * i, if i is a super row/column. It is equal to EMPTY if i is + * non-principal. Note that i refers to a row/column in the original + * matrix, not the permuted matrix. + * + * Note that the contents of Elen on output differ from the Fortran + * version (Elen holds the inverse permutation in the Fortran version, + * which is instead returned in the Next array in this C version, + * described below). + * + * Last: In a degree list, Last [i] is the supervariable preceding i, or EMPTY + * if i is the head of the list. In a hash bucket, Last [i] is the hash + * key for i. + * + * Last [Head [hash]] is also used as the head of a hash bucket if + * Head [hash] contains a degree list (see the description of Head, + * below). + * + * On output, Last [0..n-1] holds the permutation. That is, if + * i = Last [k], then row i is the kth pivot row (where k ranges from 0 to + * n-1). Row Last [k] of A is the kth row in the permuted matrix, PAP'. + * + * Next: Next [i] is the supervariable following i in a link list, or EMPTY if + * i is the last in the list. Used for two kinds of lists: degree lists + * and hash buckets (a supervariable can be in only one kind of list at a + * time). + * + * On output Next [0..n-1] holds the inverse permutation. That is, if + * k = Next [i], then row i is the kth pivot row. Row i of A appears as + * the (Next[i])-th row in the permuted matrix, PAP'. + * + * Note that the contents of Next on output differ from the Fortran + * version (Next is undefined on output in the Fortran version). + + * ---------------------------------------------------------------------------- + * LOCAL WORKSPACE (not input or output - used only during execution): + * ---------------------------------------------------------------------------- + * + * Degree: An integer array of size n. If i is a supervariable, then + * Degree [i] holds the current approximation of the external degree of + * row i (an upper bound). The external degree is the number of nonzeros + * in row i, minus ABS (Nv [i]), the diagonal part. The bound is equal to + * the exact external degree if Elen [i] is less than or equal to two. + * + * We also use the term "external degree" for elements e to refer to + * |Le \ Lme|. If e is an element, then Degree [e] is |Le|, which is the + * degree of the off-diagonal part of the element e (not including the + * diagonal part). + * + * Head: An integer array of size n. Head is used for degree lists. + * Head [deg] is the first supervariable in a degree list. All + * supervariables i in a degree list Head [deg] have the same approximate + * degree, namely, deg = Degree [i]. If the list Head [deg] is empty then + * Head [deg] = EMPTY. + * + * During supervariable detection Head [hash] also serves as a pointer to + * a hash bucket. If Head [hash] >= 0, there is a degree list of degree + * hash. The hash bucket head pointer is Last [Head [hash]]. If + * Head [hash] = EMPTY, then the degree list and hash bucket are both + * empty. If Head [hash] < EMPTY, then the degree list is empty, and + * FLIP (Head [hash]) is the head of the hash bucket. After supervariable + * detection is complete, all hash buckets are empty, and the + * (Last [Head [hash]] = EMPTY) condition is restored for the non-empty + * degree lists. + * + * W: An integer array of size n. The flag array W determines the status of + * elements and variables, and the external degree of elements. + * + * for elements: + * if W [e] = 0, then the element e is absorbed. + * if W [e] >= wflg, then W [e] - wflg is the size of the set + * |Le \ Lme|, in terms of nonzeros (the sum of ABS (Nv [i]) for + * each principal variable i that is both in the pattern of + * element e and NOT in the pattern of the current pivot element, + * me). + * if wflg > W [e] > 0, then e is not absorbed and has not yet been + * seen in the scan of the element lists in the computation of + * |Le\Lme| in Scan 1 below. + * + * for variables: + * during supervariable detection, if W [j] != wflg then j is + * not in the pattern of variable i. + * + * The W array is initialized by setting W [i] = 1 for all i, and by + * setting wflg = 2. It is reinitialized if wflg becomes too large (to + * ensure that wflg+n does not cause integer overflow). + + * ---------------------------------------------------------------------------- + * LOCAL INTEGERS: + * ---------------------------------------------------------------------------- + */ + + Int deg, degme, dext, lemax, e, elenme, eln, i, ilast, inext, j, + jlast, jnext, k, knt1, knt2, knt3, lenj, ln, me, mindeg, nel, nleft, + nvi, nvj, nvpiv, slenme, wbig, we, wflg, wnvi, x, ok, ndense, ncmpa, + dense, aggressive ; + + unsigned Int hash ; /* unsigned, so that hash % n is well defined.*/ + +/* + * deg: the degree of a variable or element + * degme: size, |Lme|, of the current element, me (= Degree [me]) + * dext: external degree, |Le \ Lme|, of some element e + * lemax: largest |Le| seen so far (called dmax in Fortran version) + * e: an element + * elenme: the length, Elen [me], of element list of pivotal variable + * eln: the length, Elen [...], of an element list + * hash: the computed value of the hash function + * i: a supervariable + * ilast: the entry in a link list preceding i + * inext: the entry in a link list following i + * j: a supervariable + * jlast: the entry in a link list preceding j + * jnext: the entry in a link list, or path, following j + * k: the pivot order of an element or variable + * knt1: loop counter used during element construction + * knt2: loop counter used during element construction + * knt3: loop counter used during compression + * lenj: Len [j] + * ln: length of a supervariable list + * me: current supervariable being eliminated, and the current + * element created by eliminating that supervariable + * mindeg: current minimum degree + * nel: number of pivots selected so far + * nleft: n - nel, the number of nonpivotal rows/columns remaining + * nvi: the number of variables in a supervariable i (= Nv [i]) + * nvj: the number of variables in a supervariable j (= Nv [j]) + * nvpiv: number of pivots in current element + * slenme: number of variables in variable list of pivotal variable + * wbig: = INT_MAX - n for the "int" version, LONG_MAX - n for the + * "long" version. wflg is not allowed to be >= wbig. + * we: W [e] + * wflg: used for flagging the W array. See description of Iw. + * wnvi: wflg - Nv [i] + * x: either a supervariable or an element + * + * ok: true if supervariable j can be absorbed into i + * ndense: number of "dense" rows/columns + * dense: rows/columns with initial degree > dense are considered "dense" + * aggressive: true if aggressive absorption is being performed + * ncmpa: number of garbage collections + + * ---------------------------------------------------------------------------- + * LOCAL DOUBLES, used for statistical output only (except for alpha): + * ---------------------------------------------------------------------------- + */ + + double f, r, ndiv, s, nms_lu, nms_ldl, dmax, alpha, lnz, lnzme ; + +/* + * f: nvpiv + * r: degme + nvpiv + * ndiv: number of divisions for LU or LDL' factorizations + * s: number of multiply-subtract pairs for LU factorization, for the + * current element me + * nms_lu number of multiply-subtract pairs for LU factorization + * nms_ldl number of multiply-subtract pairs for LDL' factorization + * dmax: the largest number of entries in any column of L, including the + * diagonal + * alpha: "dense" degree ratio + * lnz: the number of nonzeros in L (excluding the diagonal) + * lnzme: the number of nonzeros in L (excl. the diagonal) for the + * current element me + + * ---------------------------------------------------------------------------- + * LOCAL "POINTERS" (indices into the Iw array) + * ---------------------------------------------------------------------------- +*/ + + Int p, p1, p2, p3, p4, pdst, pend, pj, pme, pme1, pme2, pn, psrc ; + +/* + * Any parameter (Pe [...] or pfree) or local variable starting with "p" (for + * Pointer) is an index into Iw, and all indices into Iw use variables starting + * with "p." The only exception to this rule is the iwlen input argument. + * + * p: pointer into lots of things + * p1: Pe [i] for some variable i (start of element list) + * p2: Pe [i] + Elen [i] - 1 for some variable i + * p3: index of first supervariable in clean list + * p4: + * pdst: destination pointer, for compression + * pend: end of memory to compress + * pj: pointer into an element or variable + * pme: pointer into the current element (pme1...pme2) + * pme1: the current element, me, is stored in Iw [pme1...pme2] + * pme2: the end of the current element + * pn: pointer into a "clean" variable, also used to compress + * psrc: source pointer, for compression +*/ + +/* ========================================================================= */ +/* INITIALIZATIONS */ +/* ========================================================================= */ + + /* Note that this restriction on iwlen is slightly more restrictive than + * what is actually required in AMD_2. AMD_2 can operate with no elbow + * room at all, but it will be slow. For better performance, at least + * size-n elbow room is enforced. */ + ASSERT (iwlen >= pfree + n) ; + ASSERT (n > 0) ; + + /* initialize output statistics */ + lnz = 0 ; + ndiv = 0 ; + nms_lu = 0 ; + nms_ldl = 0 ; + dmax = 1 ; + me = EMPTY ; + + wflg = 2 ; + mindeg = 0 ; + ncmpa = 0 ; + nel = 0 ; + lemax = 0 ; /* this is called dmax in the Fortran version */ + +#ifdef TEST_FOR_INTEGER_OVERFLOW + /* for testing only */ + wbig = 3*n ; +#else + /* normal operation */ + wbig = Int_MAX - n ; +#endif + + /* get control parameters */ + if (Control != (double *) NULL) + { + alpha = Control [AMD_DENSE] ; + aggressive = (Control [AMD_AGGRESSIVE] != 0) ; + } + else + { + alpha = AMD_DEFAULT_DENSE ; + aggressive = AMD_DEFAULT_AGGRESSIVE ; + } + if (alpha < 0) + { + /* no dense rows/columns */ + dense = n ; + } + else + { + dense = alpha * sqrt ((double) n) ; + } + dense = MAX (16, dense) ; + dense = MIN (n, dense) ; + AMD_DEBUG1 (("AMD (debug), alpha %g, aggr. "ID"\n", alpha, aggressive)) ; + + for (i = 0 ; i < n ; i++) + { + Last [i] = EMPTY ; + Head [i] = EMPTY ; + Next [i] = EMPTY ; + /* if seperate Hhead array is used for hash buckets: * + Hhead [i] = EMPTY ; + */ + Nv [i] = 1 ; + W [i] = 1 ; + Elen [i] = 0 ; + Degree [i] = Len [i] ; + } + +#ifndef NDEBUG + AMD_DEBUG1 (("\n======Nel "ID"\n", nel)) ; + AMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next, Last, + Head, Elen, Degree, W, -1) ; +#endif + + /* --------------------------------------------------------------------- */ + /* initialize degree lists and eliminate dense and empty rows */ + /* --------------------------------------------------------------------- */ + + ndense = 0 ; + + /* for (i = n-1 ; i >= 0 ; i--) */ + for (i = 0 ; i < n ; i++) + { + deg = Degree [i] ; + ASSERT (deg >= 0 && deg < n) ; + if (deg == 0) + { + + /* ------------------------------------------------------------- + * we have a variable that can be eliminated at once because + * there is no off-diagonal non-zero in its row. Note that + * Nv [i] = 1 for an empty variable i. It is treated just + * the same as an eliminated element i. + * ------------------------------------------------------------- */ + + Elen [i] = FLIP (1) ; + nel++ ; + Pe [i] = EMPTY ; + W [i] = 0 ; + + } + else if (deg > dense) + { + + /* ------------------------------------------------------------- + * Dense variables are not treated as elements, but as unordered, + * non-principal variables that have no parent. They do not take + * part in the postorder, since Nv [i] = 0. Note that the Fortran + * version does not have this option. + * ------------------------------------------------------------- */ + + AMD_DEBUG1 (("Dense node "ID" degree "ID"\n", i, deg)) ; + ndense++ ; + Nv [i] = 0 ; /* do not postorder this node */ + Elen [i] = EMPTY ; + nel++ ; + Pe [i] = EMPTY ; + + } + else + { + + /* ------------------------------------------------------------- + * place i in the degree list corresponding to its degree + * ------------------------------------------------------------- */ + + inext = Head [deg] ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = i ; + Next [i] = inext ; + Head [deg] = i ; + + } + } + +/* ========================================================================= */ +/* WHILE (selecting pivots) DO */ +/* ========================================================================= */ + + while (nel < n) + { + +#ifndef NDEBUG + AMD_DEBUG1 (("\n======Nel "ID"\n", nel)) ; + if (AMD_debug >= 2) AMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next, + Last, Head, Elen, Degree, W, nel) ; +#endif + +/* ========================================================================= */ +/* GET PIVOT OF MINIMUM DEGREE */ +/* ========================================================================= */ + + /* ----------------------------------------------------------------- */ + /* find next supervariable for elimination */ + /* ----------------------------------------------------------------- */ + + ASSERT (mindeg >= 0 && mindeg < n) ; + for (deg = mindeg ; deg < n ; deg++) + { + me = Head [deg] ; + if (me != EMPTY) break ; + } + mindeg = deg ; + ASSERT (me >= 0 && me < n) ; + AMD_DEBUG1 (("=================me: "ID"\n", me)) ; + + /* ----------------------------------------------------------------- */ + /* remove chosen variable from link list */ + /* ----------------------------------------------------------------- */ + + inext = Next [me] ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = EMPTY ; + Head [deg] = inext ; + + /* ----------------------------------------------------------------- */ + /* me represents the elimination of pivots nel to nel+Nv[me]-1. */ + /* place me itself as the first in this set. */ + /* ----------------------------------------------------------------- */ + + elenme = Elen [me] ; + nvpiv = Nv [me] ; + ASSERT (nvpiv > 0) ; + nel += nvpiv ; + +/* ========================================================================= */ +/* CONSTRUCT NEW ELEMENT */ +/* ========================================================================= */ + + /* ----------------------------------------------------------------- + * At this point, me is the pivotal supervariable. It will be + * converted into the current element. Scan list of the pivotal + * supervariable, me, setting tree pointers and constructing new list + * of supervariables for the new element, me. p is a pointer to the + * current position in the old list. + * ----------------------------------------------------------------- */ + + /* flag the variable "me" as being in Lme by negating Nv [me] */ + Nv [me] = -nvpiv ; + degme = 0 ; + ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ; + + if (elenme == 0) + { + + /* ------------------------------------------------------------- */ + /* construct the new element in place */ + /* ------------------------------------------------------------- */ + + pme1 = Pe [me] ; + pme2 = pme1 - 1 ; + + for (p = pme1 ; p <= pme1 + Len [me] - 1 ; p++) + { + i = Iw [p] ; + ASSERT (i >= 0 && i < n && Nv [i] >= 0) ; + nvi = Nv [i] ; + if (nvi > 0) + { + + /* ----------------------------------------------------- */ + /* i is a principal variable not yet placed in Lme. */ + /* store i in new list */ + /* ----------------------------------------------------- */ + + /* flag i as being in Lme by negating Nv [i] */ + degme += nvi ; + Nv [i] = -nvi ; + Iw [++pme2] = i ; + + /* ----------------------------------------------------- */ + /* remove variable i from degree list. */ + /* ----------------------------------------------------- */ + + ilast = Last [i] ; + inext = Next [i] ; + ASSERT (ilast >= EMPTY && ilast < n) ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = ilast ; + if (ilast != EMPTY) + { + Next [ilast] = inext ; + } + else + { + /* i is at the head of the degree list */ + ASSERT (Degree [i] >= 0 && Degree [i] < n) ; + Head [Degree [i]] = inext ; + } + } + } + } + else + { + + /* ------------------------------------------------------------- */ + /* construct the new element in empty space, Iw [pfree ...] */ + /* ------------------------------------------------------------- */ + + p = Pe [me] ; + pme1 = pfree ; + slenme = Len [me] - elenme ; + + for (knt1 = 1 ; knt1 <= elenme + 1 ; knt1++) + { + + if (knt1 > elenme) + { + /* search the supervariables in me. */ + e = me ; + pj = p ; + ln = slenme ; + AMD_DEBUG2 (("Search sv: "ID" "ID" "ID"\n", me,pj,ln)) ; + } + else + { + /* search the elements in me. */ + e = Iw [p++] ; + ASSERT (e >= 0 && e < n) ; + pj = Pe [e] ; + ln = Len [e] ; + AMD_DEBUG2 (("Search element e "ID" in me "ID"\n", e,me)) ; + ASSERT (Elen [e] < EMPTY && W [e] > 0 && pj >= 0) ; + } + ASSERT (ln >= 0 && (ln == 0 || (pj >= 0 && pj < iwlen))) ; + + /* --------------------------------------------------------- + * search for different supervariables and add them to the + * new list, compressing when necessary. this loop is + * executed once for each element in the list and once for + * all the supervariables in the list. + * --------------------------------------------------------- */ + + for (knt2 = 1 ; knt2 <= ln ; knt2++) + { + i = Iw [pj++] ; + ASSERT (i >= 0 && i < n && (i == me || Elen [i] >= EMPTY)); + nvi = Nv [i] ; + AMD_DEBUG2 ((": "ID" "ID" "ID" "ID"\n", + i, Elen [i], Nv [i], wflg)) ; + + if (nvi > 0) + { + + /* ------------------------------------------------- */ + /* compress Iw, if necessary */ + /* ------------------------------------------------- */ + + if (pfree >= iwlen) + { + + AMD_DEBUG1 (("GARBAGE COLLECTION\n")) ; + + /* prepare for compressing Iw by adjusting pointers + * and lengths so that the lists being searched in + * the inner and outer loops contain only the + * remaining entries. */ + + Pe [me] = p ; + Len [me] -= knt1 ; + /* check if nothing left of supervariable me */ + if (Len [me] == 0) Pe [me] = EMPTY ; + Pe [e] = pj ; + Len [e] = ln - knt2 ; + /* nothing left of element e */ + if (Len [e] == 0) Pe [e] = EMPTY ; + + ncmpa++ ; /* one more garbage collection */ + + /* store first entry of each object in Pe */ + /* FLIP the first entry in each object */ + for (j = 0 ; j < n ; j++) + { + pn = Pe [j] ; + if (pn >= 0) + { + ASSERT (pn >= 0 && pn < iwlen) ; + Pe [j] = Iw [pn] ; + Iw [pn] = FLIP (j) ; + } + } + + /* psrc/pdst point to source/destination */ + psrc = 0 ; + pdst = 0 ; + pend = pme1 - 1 ; + + while (psrc <= pend) + { + /* search for next FLIP'd entry */ + j = FLIP (Iw [psrc++]) ; + if (j >= 0) + { + AMD_DEBUG2 (("Got object j: "ID"\n", j)) ; + Iw [pdst] = Pe [j] ; + Pe [j] = pdst++ ; + lenj = Len [j] ; + /* copy from source to destination */ + for (knt3 = 0 ; knt3 <= lenj - 2 ; knt3++) + { + Iw [pdst++] = Iw [psrc++] ; + } + } + } + + /* move the new partially-constructed element */ + p1 = pdst ; + for (psrc = pme1 ; psrc <= pfree-1 ; psrc++) + { + Iw [pdst++] = Iw [psrc] ; + } + pme1 = p1 ; + pfree = pdst ; + pj = Pe [e] ; + p = Pe [me] ; + + } + + /* ------------------------------------------------- */ + /* i is a principal variable not yet placed in Lme */ + /* store i in new list */ + /* ------------------------------------------------- */ + + /* flag i as being in Lme by negating Nv [i] */ + degme += nvi ; + Nv [i] = -nvi ; + Iw [pfree++] = i ; + AMD_DEBUG2 ((" s: "ID" nv "ID"\n", i, Nv [i])); + + /* ------------------------------------------------- */ + /* remove variable i from degree link list */ + /* ------------------------------------------------- */ + + ilast = Last [i] ; + inext = Next [i] ; + ASSERT (ilast >= EMPTY && ilast < n) ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = ilast ; + if (ilast != EMPTY) + { + Next [ilast] = inext ; + } + else + { + /* i is at the head of the degree list */ + ASSERT (Degree [i] >= 0 && Degree [i] < n) ; + Head [Degree [i]] = inext ; + } + } + } + + if (e != me) + { + /* set tree pointer and flag to indicate element e is + * absorbed into new element me (the parent of e is me) */ + AMD_DEBUG1 ((" Element "ID" => "ID"\n", e, me)) ; + Pe [e] = FLIP (me) ; + W [e] = 0 ; + } + } + + pme2 = pfree - 1 ; + } + + /* ----------------------------------------------------------------- */ + /* me has now been converted into an element in Iw [pme1..pme2] */ + /* ----------------------------------------------------------------- */ + + /* degme holds the external degree of new element */ + Degree [me] = degme ; + Pe [me] = pme1 ; + Len [me] = pme2 - pme1 + 1 ; + ASSERT (Pe [me] >= 0 && Pe [me] < iwlen) ; + + Elen [me] = FLIP (nvpiv + degme) ; + /* FLIP (Elen (me)) is now the degree of pivot (including + * diagonal part). */ + +#ifndef NDEBUG + AMD_DEBUG2 (("New element structure: length= "ID"\n", pme2-pme1+1)) ; + for (pme = pme1 ; pme <= pme2 ; pme++) AMD_DEBUG3 ((" "ID"", Iw[pme])); + AMD_DEBUG3 (("\n")) ; +#endif + + /* ----------------------------------------------------------------- */ + /* make sure that wflg is not too large. */ + /* ----------------------------------------------------------------- */ + + /* With the current value of wflg, wflg+n must not cause integer + * overflow */ + + if (wflg >= wbig) + { + for (x = 0 ; x < n ; x++) + { + if (W [x] != 0) W [x] = 1 ; + } + wflg = 2 ; + } + +/* ========================================================================= */ +/* COMPUTE (W [e] - wflg) = |Le\Lme| FOR ALL ELEMENTS */ +/* ========================================================================= */ + + /* ----------------------------------------------------------------- + * Scan 1: compute the external degrees of previous elements with + * respect to the current element. That is: + * (W [e] - wflg) = |Le \ Lme| + * for each element e that appears in any supervariable in Lme. The + * notation Le refers to the pattern (list of supervariables) of a + * previous element e, where e is not yet absorbed, stored in + * Iw [Pe [e] + 1 ... Pe [e] + Iw [Pe [e]]]. The notation Lme + * refers to the pattern of the current element (stored in + * Iw [pme1..pme2]). If aggressive absorption is enabled, and + * (W [e] - wflg) becomes zero, then the element e will be absorbed + * in Scan 2. + * ----------------------------------------------------------------- */ + + AMD_DEBUG2 (("me: ")) ; + for (pme = pme1 ; pme <= pme2 ; pme++) + { + i = Iw [pme] ; + ASSERT (i >= 0 && i < n) ; + eln = Elen [i] ; + AMD_DEBUG3 ((""ID" Elen "ID": \n", i, eln)) ; + if (eln > 0) + { + /* note that Nv [i] has been negated to denote i in Lme: */ + nvi = -Nv [i] ; + ASSERT (nvi > 0 && Pe [i] >= 0 && Pe [i] < iwlen) ; + wnvi = wflg - nvi ; + for (p = Pe [i] ; p <= Pe [i] + eln - 1 ; p++) + { + e = Iw [p] ; + ASSERT (e >= 0 && e < n) ; + we = W [e] ; + AMD_DEBUG4 ((" e "ID" we "ID" ", e, we)) ; + if (we >= wflg) + { + /* unabsorbed element e has been seen in this loop */ + AMD_DEBUG4 ((" unabsorbed, first time seen")) ; + we -= nvi ; + } + else if (we != 0) + { + /* e is an unabsorbed element */ + /* this is the first we have seen e in all of Scan 1 */ + AMD_DEBUG4 ((" unabsorbed")) ; + we = Degree [e] + wnvi ; + } + AMD_DEBUG4 (("\n")) ; + W [e] = we ; + } + } + } + AMD_DEBUG2 (("\n")) ; + +/* ========================================================================= */ +/* DEGREE UPDATE AND ELEMENT ABSORPTION */ +/* ========================================================================= */ + + /* ----------------------------------------------------------------- + * Scan 2: for each i in Lme, sum up the degree of Lme (which is + * degme), plus the sum of the external degrees of each Le for the + * elements e appearing within i, plus the supervariables in i. + * Place i in hash list. + * ----------------------------------------------------------------- */ + + for (pme = pme1 ; pme <= pme2 ; pme++) + { + i = Iw [pme] ; + ASSERT (i >= 0 && i < n && Nv [i] < 0 && Elen [i] >= 0) ; + AMD_DEBUG2 (("Updating: i "ID" "ID" "ID"\n", i, Elen[i], Len [i])); + p1 = Pe [i] ; + p2 = p1 + Elen [i] - 1 ; + pn = p1 ; + hash = 0 ; + deg = 0 ; + ASSERT (p1 >= 0 && p1 < iwlen && p2 >= -1 && p2 < iwlen) ; + + /* ------------------------------------------------------------- */ + /* scan the element list associated with supervariable i */ + /* ------------------------------------------------------------- */ + + /* UMFPACK/MA38-style approximate degree: */ + if (aggressive) + { + for (p = p1 ; p <= p2 ; p++) + { + e = Iw [p] ; + ASSERT (e >= 0 && e < n) ; + we = W [e] ; + if (we != 0) + { + /* e is an unabsorbed element */ + /* dext = | Le \ Lme | */ + dext = we - wflg ; + if (dext > 0) + { + deg += dext ; + Iw [pn++] = e ; + hash += e ; + AMD_DEBUG4 ((" e: "ID" hash = "ID"\n",e,hash)) ; + } + else + { + /* external degree of e is zero, absorb e into me*/ + AMD_DEBUG1 ((" Element "ID" =>"ID" (aggressive)\n", + e, me)) ; + ASSERT (dext == 0) ; + Pe [e] = FLIP (me) ; + W [e] = 0 ; + } + } + } + } + else + { + for (p = p1 ; p <= p2 ; p++) + { + e = Iw [p] ; + ASSERT (e >= 0 && e < n) ; + we = W [e] ; + if (we != 0) + { + /* e is an unabsorbed element */ + dext = we - wflg ; + ASSERT (dext >= 0) ; + deg += dext ; + Iw [pn++] = e ; + hash += e ; + AMD_DEBUG4 ((" e: "ID" hash = "ID"\n",e,hash)) ; + } + } + } + + /* count the number of elements in i (including me): */ + Elen [i] = pn - p1 + 1 ; + + /* ------------------------------------------------------------- */ + /* scan the supervariables in the list associated with i */ + /* ------------------------------------------------------------- */ + + /* The bulk of the AMD run time is typically spent in this loop, + * particularly if the matrix has many dense rows that are not + * removed prior to ordering. */ + p3 = pn ; + p4 = p1 + Len [i] ; + for (p = p2 + 1 ; p < p4 ; p++) + { + j = Iw [p] ; + ASSERT (j >= 0 && j < n) ; + nvj = Nv [j] ; + if (nvj > 0) + { + /* j is unabsorbed, and not in Lme. */ + /* add to degree and add to new list */ + deg += nvj ; + Iw [pn++] = j ; + hash += j ; + AMD_DEBUG4 ((" s: "ID" hash "ID" Nv[j]= "ID"\n", + j, hash, nvj)) ; + } + } + + /* ------------------------------------------------------------- */ + /* update the degree and check for mass elimination */ + /* ------------------------------------------------------------- */ + + /* with aggressive absorption, deg==0 is identical to the + * Elen [i] == 1 && p3 == pn test, below. */ + ASSERT (IMPLIES (aggressive, (deg==0) == (Elen[i]==1 && p3==pn))) ; + + if (Elen [i] == 1 && p3 == pn) + { + + /* --------------------------------------------------------- */ + /* mass elimination */ + /* --------------------------------------------------------- */ + + /* There is nothing left of this node except for an edge to + * the current pivot element. Elen [i] is 1, and there are + * no variables adjacent to node i. Absorb i into the + * current pivot element, me. Note that if there are two or + * more mass eliminations, fillin due to mass elimination is + * possible within the nvpiv-by-nvpiv pivot block. It is this + * step that causes AMD's analysis to be an upper bound. + * + * The reason is that the selected pivot has a lower + * approximate degree than the true degree of the two mass + * eliminated nodes. There is no edge between the two mass + * eliminated nodes. They are merged with the current pivot + * anyway. + * + * No fillin occurs in the Schur complement, in any case, + * and this effect does not decrease the quality of the + * ordering itself, just the quality of the nonzero and + * flop count analysis. It also means that the post-ordering + * is not an exact elimination tree post-ordering. */ + + AMD_DEBUG1 ((" MASS i "ID" => parent e "ID"\n", i, me)) ; + Pe [i] = FLIP (me) ; + nvi = -Nv [i] ; + degme -= nvi ; + nvpiv += nvi ; + nel += nvi ; + Nv [i] = 0 ; + Elen [i] = EMPTY ; + + } + else + { + + /* --------------------------------------------------------- */ + /* update the upper-bound degree of i */ + /* --------------------------------------------------------- */ + + /* the following degree does not yet include the size + * of the current element, which is added later: */ + + Degree [i] = MIN (Degree [i], deg) ; + + /* --------------------------------------------------------- */ + /* add me to the list for i */ + /* --------------------------------------------------------- */ + + /* move first supervariable to end of list */ + Iw [pn] = Iw [p3] ; + /* move first element to end of element part of list */ + Iw [p3] = Iw [p1] ; + /* add new element, me, to front of list. */ + Iw [p1] = me ; + /* store the new length of the list in Len [i] */ + Len [i] = pn - p1 + 1 ; + + /* --------------------------------------------------------- */ + /* place in hash bucket. Save hash key of i in Last [i]. */ + /* --------------------------------------------------------- */ + + /* NOTE: this can fail if hash is negative, because the ANSI C + * standard does not define a % b when a and/or b are negative. + * That's why hash is defined as an unsigned Int, to avoid this + * problem. */ + hash = hash % n ; + ASSERT (((Int) hash) >= 0 && ((Int) hash) < n) ; + + /* if the Hhead array is not used: */ + j = Head [hash] ; + if (j <= EMPTY) + { + /* degree list is empty, hash head is FLIP (j) */ + Next [i] = FLIP (j) ; + Head [hash] = FLIP (i) ; + } + else + { + /* degree list is not empty, use Last [Head [hash]] as + * hash head. */ + Next [i] = Last [j] ; + Last [j] = i ; + } + + /* if a seperate Hhead array is used: * + Next [i] = Hhead [hash] ; + Hhead [hash] = i ; + */ + + Last [i] = hash ; + } + } + + Degree [me] = degme ; + + /* ----------------------------------------------------------------- */ + /* Clear the counter array, W [...], by incrementing wflg. */ + /* ----------------------------------------------------------------- */ + + /* make sure that wflg+n does not cause integer overflow */ + lemax = MAX (lemax, degme) ; + wflg += lemax ; + if (wflg >= wbig) + { + for (x = 0 ; x < n ; x++) + { + if (W [x] != 0) W [x] = 1 ; + } + wflg = 2 ; + } + /* at this point, W [0..n-1] < wflg holds */ + +/* ========================================================================= */ +/* SUPERVARIABLE DETECTION */ +/* ========================================================================= */ + + AMD_DEBUG1 (("Detecting supervariables:\n")) ; + for (pme = pme1 ; pme <= pme2 ; pme++) + { + i = Iw [pme] ; + ASSERT (i >= 0 && i < n) ; + AMD_DEBUG2 (("Consider i "ID" nv "ID"\n", i, Nv [i])) ; + if (Nv [i] < 0) + { + /* i is a principal variable in Lme */ + + /* --------------------------------------------------------- + * examine all hash buckets with 2 or more variables. We do + * this by examing all unique hash keys for supervariables in + * the pattern Lme of the current element, me + * --------------------------------------------------------- */ + + /* let i = head of hash bucket, and empty the hash bucket */ + ASSERT (Last [i] >= 0 && Last [i] < n) ; + hash = Last [i] ; + + /* if Hhead array is not used: */ + j = Head [hash] ; + if (j == EMPTY) + { + /* hash bucket and degree list are both empty */ + i = EMPTY ; + } + else if (j < EMPTY) + { + /* degree list is empty */ + i = FLIP (j) ; + Head [hash] = EMPTY ; + } + else + { + /* degree list is not empty, restore Last [j] of head j */ + i = Last [j] ; + Last [j] = EMPTY ; + } + + /* if seperate Hhead array is used: * + i = Hhead [hash] ; + Hhead [hash] = EMPTY ; + */ + + ASSERT (i >= EMPTY && i < n) ; + AMD_DEBUG2 (("----i "ID" hash "ID"\n", i, hash)) ; + + while (i != EMPTY && Next [i] != EMPTY) + { + + /* ----------------------------------------------------- + * this bucket has one or more variables following i. + * scan all of them to see if i can absorb any entries + * that follow i in hash bucket. Scatter i into w. + * ----------------------------------------------------- */ + + ln = Len [i] ; + eln = Elen [i] ; + ASSERT (ln >= 0 && eln >= 0) ; + ASSERT (Pe [i] >= 0 && Pe [i] < iwlen) ; + /* do not flag the first element in the list (me) */ + for (p = Pe [i] + 1 ; p <= Pe [i] + ln - 1 ; p++) + { + ASSERT (Iw [p] >= 0 && Iw [p] < n) ; + W [Iw [p]] = wflg ; + } + + /* ----------------------------------------------------- */ + /* scan every other entry j following i in bucket */ + /* ----------------------------------------------------- */ + + jlast = i ; + j = Next [i] ; + ASSERT (j >= EMPTY && j < n) ; + + while (j != EMPTY) + { + /* ------------------------------------------------- */ + /* check if j and i have identical nonzero pattern */ + /* ------------------------------------------------- */ + + AMD_DEBUG3 (("compare i "ID" and j "ID"\n", i,j)) ; + + /* check if i and j have the same Len and Elen */ + ASSERT (Len [j] >= 0 && Elen [j] >= 0) ; + ASSERT (Pe [j] >= 0 && Pe [j] < iwlen) ; + ok = (Len [j] == ln) && (Elen [j] == eln) ; + /* skop the first element in the list (me) */ + for (p = Pe [j] + 1 ; ok && p <= Pe [j] + ln - 1 ; p++) + { + ASSERT (Iw [p] >= 0 && Iw [p] < n) ; + if (W [Iw [p]] != wflg) ok = 0 ; + } + if (ok) + { + /* --------------------------------------------- */ + /* found it! j can be absorbed into i */ + /* --------------------------------------------- */ + + AMD_DEBUG1 (("found it! j "ID" => i "ID"\n", j,i)); + Pe [j] = FLIP (i) ; + /* both Nv [i] and Nv [j] are negated since they */ + /* are in Lme, and the absolute values of each */ + /* are the number of variables in i and j: */ + Nv [i] += Nv [j] ; + Nv [j] = 0 ; + Elen [j] = EMPTY ; + /* delete j from hash bucket */ + ASSERT (j != Next [j]) ; + j = Next [j] ; + Next [jlast] = j ; + + } + else + { + /* j cannot be absorbed into i */ + jlast = j ; + ASSERT (j != Next [j]) ; + j = Next [j] ; + } + ASSERT (j >= EMPTY && j < n) ; + } + + /* ----------------------------------------------------- + * no more variables can be absorbed into i + * go to next i in bucket and clear flag array + * ----------------------------------------------------- */ + + wflg++ ; + i = Next [i] ; + ASSERT (i >= EMPTY && i < n) ; + + } + } + } + AMD_DEBUG2 (("detect done\n")) ; + +/* ========================================================================= */ +/* RESTORE DEGREE LISTS AND REMOVE NONPRINCIPAL SUPERVARIABLES FROM ELEMENT */ +/* ========================================================================= */ + + p = pme1 ; + nleft = n - nel ; + for (pme = pme1 ; pme <= pme2 ; pme++) + { + i = Iw [pme] ; + ASSERT (i >= 0 && i < n) ; + nvi = -Nv [i] ; + AMD_DEBUG3 (("Restore i "ID" "ID"\n", i, nvi)) ; + if (nvi > 0) + { + /* i is a principal variable in Lme */ + /* restore Nv [i] to signify that i is principal */ + Nv [i] = nvi ; + + /* --------------------------------------------------------- */ + /* compute the external degree (add size of current element) */ + /* --------------------------------------------------------- */ + + deg = Degree [i] + degme - nvi ; + deg = MIN (deg, nleft - nvi) ; + ASSERT (IMPLIES (aggressive, deg > 0) && deg >= 0 && deg < n) ; + + /* --------------------------------------------------------- */ + /* place the supervariable at the head of the degree list */ + /* --------------------------------------------------------- */ + + inext = Head [deg] ; + ASSERT (inext >= EMPTY && inext < n) ; + if (inext != EMPTY) Last [inext] = i ; + Next [i] = inext ; + Last [i] = EMPTY ; + Head [deg] = i ; + + /* --------------------------------------------------------- */ + /* save the new degree, and find the minimum degree */ + /* --------------------------------------------------------- */ + + mindeg = MIN (mindeg, deg) ; + Degree [i] = deg ; + + /* --------------------------------------------------------- */ + /* place the supervariable in the element pattern */ + /* --------------------------------------------------------- */ + + Iw [p++] = i ; + + } + } + AMD_DEBUG2 (("restore done\n")) ; + +/* ========================================================================= */ +/* FINALIZE THE NEW ELEMENT */ +/* ========================================================================= */ + + AMD_DEBUG2 (("ME = "ID" DONE\n", me)) ; + Nv [me] = nvpiv ; + /* save the length of the list for the new element me */ + Len [me] = p - pme1 ; + if (Len [me] == 0) + { + /* there is nothing left of the current pivot element */ + /* it is a root of the assembly tree */ + Pe [me] = EMPTY ; + W [me] = 0 ; + } + if (elenme != 0) + { + /* element was not constructed in place: deallocate part of */ + /* it since newly nonprincipal variables may have been removed */ + pfree = p ; + } + + /* The new element has nvpiv pivots and the size of the contribution + * block for a multifrontal method is degme-by-degme, not including + * the "dense" rows/columns. If the "dense" rows/columns are included, + * the frontal matrix is no larger than + * (degme+ndense)-by-(degme+ndense). + */ + + if (Info != (double *) NULL) + { + f = nvpiv ; + r = degme + ndense ; + dmax = MAX (dmax, f + r) ; + + /* number of nonzeros in L (excluding the diagonal) */ + lnzme = f*r + (f-1)*f/2 ; + lnz += lnzme ; + + /* number of divide operations for LDL' and for LU */ + ndiv += lnzme ; + + /* number of multiply-subtract pairs for LU */ + s = f*r*r + r*(f-1)*f + (f-1)*f*(2*f-1)/6 ; + nms_lu += s ; + + /* number of multiply-subtract pairs for LDL' */ + nms_ldl += (s + lnzme)/2 ; + } + +#ifndef NDEBUG + AMD_DEBUG2 (("finalize done nel "ID" n "ID"\n ::::\n", nel, n)) ; + for (pme = Pe [me] ; pme <= Pe [me] + Len [me] - 1 ; pme++) + { + AMD_DEBUG3 ((" "ID"", Iw [pme])) ; + } + AMD_DEBUG3 (("\n")) ; +#endif + + } + +/* ========================================================================= */ +/* DONE SELECTING PIVOTS */ +/* ========================================================================= */ + + if (Info != (double *) NULL) + { + + /* count the work to factorize the ndense-by-ndense submatrix */ + f = ndense ; + dmax = MAX (dmax, (double) ndense) ; + + /* number of nonzeros in L (excluding the diagonal) */ + lnzme = (f-1)*f/2 ; + lnz += lnzme ; + + /* number of divide operations for LDL' and for LU */ + ndiv += lnzme ; + + /* number of multiply-subtract pairs for LU */ + s = (f-1)*f*(2*f-1)/6 ; + nms_lu += s ; + + /* number of multiply-subtract pairs for LDL' */ + nms_ldl += (s + lnzme)/2 ; + + /* number of nz's in L (excl. diagonal) */ + Info [AMD_LNZ] = lnz ; + + /* number of divide ops for LU and LDL' */ + Info [AMD_NDIV] = ndiv ; + + /* number of multiply-subtract pairs for LDL' */ + Info [AMD_NMULTSUBS_LDL] = nms_ldl ; + + /* number of multiply-subtract pairs for LU */ + Info [AMD_NMULTSUBS_LU] = nms_lu ; + + /* number of "dense" rows/columns */ + Info [AMD_NDENSE] = ndense ; + + /* largest front is dmax-by-dmax */ + Info [AMD_DMAX] = dmax ; + + /* number of garbage collections in AMD */ + Info [AMD_NCMPA] = ncmpa ; + + /* successful ordering */ + Info [AMD_STATUS] = AMD_OK ; + } + +/* ------------------------------------------------------------------------- + * Variables at this point: + * + * Pe: holds the elimination tree. The parent of j is FLIP (Pe [j]), + * or EMPTY if j is a root. The tree holds both elements and + * non-principal (unordered) variables absorbed into them. + * Dense variables are non-principal and unordered. + * + * Elen: holds the size of each element, including the diagonal part. + * FLIP (Elen [e]) > 0 if e is an element. For unordered + * variables i, Elen [i] is EMPTY. + * + * Nv: Nv [e] > 0 is the number of pivots represented by the element e. + * For unordered variables i, Nv [i] is zero. + * + * Contents no longer needed: + * W, Iw, Len, Degree, Head, Next, Last. + * + * The matrix itself has been destroyed. + * + * n: the size of the matrix. + * No other scalars needed (pfree, iwlen, etc.) + * ------------------------------------------------------------------------- */ + + for (i = 0 ; i < n ; i++) + { + Pe [i] = FLIP (Pe [i]) ; + Elen [i] = FLIP (Elen [i]) ; + } + +/* Now the parent of j is Pe [j], or EMPTY if j is a root. Elen [e] > 0 + * is the size of element e. Elen [i] is EMPTY for unordered variable i. */ + +#ifndef NDEBUG + AMD_DEBUG2 (("\nTree:\n")) ; + for (i = 0 ; i < n ; i++) + { + AMD_DEBUG2 ((" "ID" parent: "ID" ", i, Pe [i])) ; + ASSERT (Pe [i] >= EMPTY && Pe [i] < n) ; + if (Nv [i] > 0) + { + /* this is an element */ + e = i ; + AMD_DEBUG2 ((" element, size is "ID"\n", Elen [i])) ; + ASSERT (Elen [e] > 0) ; + } + AMD_DEBUG2 (("\n")) ; + } + AMD_DEBUG2 (("\nelements:\n")) ; + for (e = 0 ; e < n ; e++) + { + if (Nv [e] > 0) + { + AMD_DEBUG3 (("Element e= "ID" size "ID" nv "ID" \n", e, + Elen [e], Nv [e])) ; + } + } + AMD_DEBUG2 (("\nvariables:\n")) ; + for (i = 0 ; i < n ; i++) + { + Int cnt ; + if (Nv [i] == 0) + { + AMD_DEBUG3 (("i unordered: "ID"\n", i)) ; + j = Pe [i] ; + cnt = 0 ; + AMD_DEBUG3 ((" j: "ID"\n", j)) ; + if (j == EMPTY) + { + AMD_DEBUG3 ((" i is a dense variable\n")) ; + } + else + { + ASSERT (j >= 0 && j < n) ; + while (Nv [j] == 0) + { + AMD_DEBUG3 ((" j : "ID"\n", j)) ; + j = Pe [j] ; + AMD_DEBUG3 ((" j:: "ID"\n", j)) ; + cnt++ ; + if (cnt > n) break ; + } + e = j ; + AMD_DEBUG3 ((" got to e: "ID"\n", e)) ; + } + } + } +#endif + +/* ========================================================================= */ +/* compress the paths of the variables */ +/* ========================================================================= */ + + for (i = 0 ; i < n ; i++) + { + if (Nv [i] == 0) + { + + /* ------------------------------------------------------------- + * i is an un-ordered row. Traverse the tree from i until + * reaching an element, e. The element, e, was the principal + * supervariable of i and all nodes in the path from i to when e + * was selected as pivot. + * ------------------------------------------------------------- */ + + AMD_DEBUG1 (("Path compression, i unordered: "ID"\n", i)) ; + j = Pe [i] ; + ASSERT (j >= EMPTY && j < n) ; + AMD_DEBUG3 ((" j: "ID"\n", j)) ; + if (j == EMPTY) + { + /* Skip a dense variable. It has no parent. */ + AMD_DEBUG3 ((" i is a dense variable\n")) ; + continue ; + } + + /* while (j is a variable) */ + while (Nv [j] == 0) + { + AMD_DEBUG3 ((" j : "ID"\n", j)) ; + j = Pe [j] ; + AMD_DEBUG3 ((" j:: "ID"\n", j)) ; + ASSERT (j >= 0 && j < n) ; + } + /* got to an element e */ + e = j ; + AMD_DEBUG3 (("got to e: "ID"\n", e)) ; + + /* ------------------------------------------------------------- + * traverse the path again from i to e, and compress the path + * (all nodes point to e). Path compression allows this code to + * compute in O(n) time. + * ------------------------------------------------------------- */ + + j = i ; + /* while (j is a variable) */ + while (Nv [j] == 0) + { + jnext = Pe [j] ; + AMD_DEBUG3 (("j "ID" jnext "ID"\n", j, jnext)) ; + Pe [j] = e ; + j = jnext ; + ASSERT (j >= 0 && j < n) ; + } + } + } + +/* ========================================================================= */ +/* postorder the assembly tree */ +/* ========================================================================= */ + + AMD_postorder (n, Pe, Nv, Elen, + W, /* output order */ + Head, Next, Last) ; /* workspace */ + +/* ========================================================================= */ +/* compute output permutation and inverse permutation */ +/* ========================================================================= */ + + /* W [e] = k means that element e is the kth element in the new + * order. e is in the range 0 to n-1, and k is in the range 0 to + * the number of elements. Use Head for inverse order. */ + + for (k = 0 ; k < n ; k++) + { + Head [k] = EMPTY ; + Next [k] = EMPTY ; + } + for (e = 0 ; e < n ; e++) + { + k = W [e] ; + ASSERT ((k == EMPTY) == (Nv [e] == 0)) ; + if (k != EMPTY) + { + ASSERT (k >= 0 && k < n) ; + Head [k] = e ; + } + } + + /* construct output inverse permutation in Next, + * and permutation in Last */ + nel = 0 ; + for (k = 0 ; k < n ; k++) + { + e = Head [k] ; + if (e == EMPTY) break ; + ASSERT (e >= 0 && e < n && Nv [e] > 0) ; + Next [e] = nel ; + nel += Nv [e] ; + } + ASSERT (nel == n - ndense) ; + + /* order non-principal variables (dense, & those merged into supervar's) */ + for (i = 0 ; i < n ; i++) + { + if (Nv [i] == 0) + { + e = Pe [i] ; + ASSERT (e >= EMPTY && e < n) ; + if (e != EMPTY) + { + /* This is an unordered variable that was merged + * into element e via supernode detection or mass + * elimination of i when e became the pivot element. + * Place i in order just before e. */ + ASSERT (Next [i] == EMPTY && Nv [e] > 0) ; + Next [i] = Next [e] ; + Next [e]++ ; + } + else + { + /* This is a dense unordered variable, with no parent. + * Place it last in the output order. */ + Next [i] = nel++ ; + } + } + } + ASSERT (nel == n) ; + + AMD_DEBUG2 (("\n\nPerm:\n")) ; + for (i = 0 ; i < n ; i++) + { + k = Next [i] ; + ASSERT (k >= 0 && k < n) ; + Last [k] = i ; + AMD_DEBUG2 ((" perm ["ID"] = "ID"\n", k, i)) ; + } +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_aat.c b/deal.II/contrib/umfpack/AMD/Source/amd_aat.c new file mode 100644 index 0000000000..3de1e2446a --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_aat.c @@ -0,0 +1,180 @@ +/* ========================================================================= */ +/* === AMD_aat ============================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* AMD_aat: compute the symmetry of the pattern of A, and count the number of + * nonzeros each column of A+A' (excluding the diagonal). Assume the input + * matrix has no errors. + */ + +#include "amd_internal.h" + +GLOBAL Int AMD_aat /* returns nz in A+A' */ +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + Int Len [ ], /* Len [j]: length of column j of A+A', excl diagonal*/ + Int Tp [ ], /* workspace of size n */ + double Info [ ] +) +{ + Int p1, p2, p, i, j, pj, pj2, k, nzdiag, nzboth, nz, nzaat ; + double sym ; + +#ifndef NDEBUG + AMD_debug_init ("AMD AAT") ; + for (k = 0 ; k < n ; k++) Tp [k] = EMPTY ; + ASSERT (AMD_valid (n, n, Ap, Ai)) ; +#endif + + if (Info != (double *) NULL) + { + /* clear the Info array, if it exists */ + for (i = 0 ; i < AMD_INFO ; i++) + { + Info [i] = EMPTY ; + } + Info [AMD_STATUS] = AMD_OK ; + } + + for (k = 0 ; k < n ; k++) + { + Len [k] = 0 ; + } + + nzdiag = 0 ; + nzboth = 0 ; + nz = Ap [n] ; + + for (k = 0 ; k < n ; k++) + { + p1 = Ap [k] ; + p2 = Ap [k+1] ; + AMD_DEBUG2 (("\nAAT Column: "ID" p1: "ID" p2: "ID"\n", k, p1, p2)) ; + + /* construct A+A' */ + for (p = p1 ; p < p2 ; ) + { + /* scan the upper triangular part of A */ + j = Ai [p] ; + if (j < k) + { + /* entry A (j,k) is in the strictly upper triangular part, + * add both A (j,k) and A (k,j) to the matrix A+A' */ + Len [j]++ ; + Len [k]++ ; + AMD_DEBUG3 ((" upper ("ID","ID") ("ID","ID")\n", j,k, k,j)); + p++ ; + } + else if (j == k) + { + /* skip the diagonal */ + p++ ; + nzdiag++ ; + break ; + } + else /* j > k */ + { + /* first entry below the diagonal */ + break ; + } + /* scan lower triangular part of A, in column j until reaching + * row k. Start where last scan left off. */ + ASSERT (Tp [j] != EMPTY) ; + ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; + pj2 = Ap [j+1] ; + for (pj = Tp [j] ; pj < pj2 ; ) + { + i = Ai [pj] ; + if (i < k) + { + /* A (i,j) is only in the lower part, not in upper. + * add both A (i,j) and A (j,i) to the matrix A+A' */ + Len [i]++ ; + Len [j]++ ; + AMD_DEBUG3 ((" lower ("ID","ID") ("ID","ID")\n", + i,j, j,i)) ; + pj++ ; + } + else if (i == k) + { + /* entry A (k,j) in lower part and A (j,k) in upper */ + pj++ ; + nzboth++ ; + break ; + } + else /* i > k */ + { + /* consider this entry later, when k advances to i */ + break ; + } + } + Tp [j] = pj ; + } + /* Tp [k] points to the entry just below the diagonal in column k */ + Tp [k] = p ; + } + + /* clean up, for remaining mismatched entries */ + for (j = 0 ; j < n ; j++) + { + for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) + { + i = Ai [pj] ; + /* A (i,j) is only in the lower part, not in upper. + * add both A (i,j) and A (j,i) to the matrix A+A' */ + Len [i]++ ; + Len [j]++ ; + AMD_DEBUG3 ((" lower cleanup ("ID","ID") ("ID","ID")\n", + i,j, j,i)) ; + } + } + + /* --------------------------------------------------------------------- */ + /* compute the symmetry of the nonzero pattern of A */ + /* --------------------------------------------------------------------- */ + + /* Given a matrix A, the symmetry of A is: + * B = tril (spones (A), -1) + triu (spones (A), 1) ; + * sym = nnz (B & B') / nnz (B) ; + * or 1 if nnz (B) is zero. + */ + + if (nz == nzdiag) + { + sym = 1 ; + } + else + { + sym = ((double) (2 * nzboth)) / ((double) (nz - nzdiag)) ; + } + + nzaat = 0 ; + for (k = 0 ; k < n ; k++) + { + nzaat += Len [k] ; + } + AMD_DEBUG1 (("AMD nz in A+A', excluding diagonal (nzaat) = "ID"\n",nzaat)); + AMD_DEBUG1 ((" nzboth: "ID" nz: "ID" nzdiag: "ID" symmetry: %g\n", + nzboth, nz, nzdiag, sym)) ; + + if (Info != (double *) NULL) + { + Info [AMD_STATUS] = AMD_OK ; + Info [AMD_N] = n ; + Info [AMD_NZ] = nz ; + Info [AMD_SYMMETRY] = sym ; /* symmetry of pattern of A */ + Info [AMD_NZDIAG] = nzdiag ; /* nonzeros on diagonal of A */ + Info [AMD_NZ_A_PLUS_AT] = nzaat ; /* nonzeros in A+A' */ + } + + return (nzaat) ; +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_control.c b/deal.II/contrib/umfpack/AMD/Source/amd_control.c new file mode 100644 index 0000000000..6fc8f9f475 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_control.c @@ -0,0 +1,61 @@ +/* ========================================================================= */ +/* === AMD_control ========================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* User-callable. Prints the control parameters for AMD. See amd.h + * for details. If the Control array is not present, the defaults are + * printed instead. + */ + +#include "amd_internal.h" + +GLOBAL void AMD_control +( + double Control [ ] +) +{ + double alpha ; + Int aggressive ; + + if (Control != (double *) NULL) + { + alpha = Control [AMD_DENSE] ; + aggressive = Control [AMD_AGGRESSIVE] != 0 ; + } + else + { + alpha = AMD_DEFAULT_DENSE ; + aggressive = AMD_DEFAULT_AGGRESSIVE ; + } + + PRINTF (("\namd: approximate minimum degree ordering, parameters:\n" + " dense row parameter: %g\n", alpha)) ; + + if (alpha < 0) + { + PRINTF ((" no rows treated as dense\n")) ; + } + else + { + PRINTF (( + " (rows with more than max (%g * sqrt (n), 16) entries are\n" + " considered \"dense\", and placed last in output permutation)\n", + alpha)) ; + } + + if (aggressive) + { + PRINTF ((" aggressive absorption: yes\n\n")) ; + } + else + { + PRINTF ((" aggressive absorption: no\n\n")) ; + } +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_defaults.c b/deal.II/contrib/umfpack/AMD/Source/amd_defaults.c new file mode 100644 index 0000000000..d0737f4fab --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_defaults.c @@ -0,0 +1,33 @@ +/* ========================================================================= */ +/* === AMD_defaults ======================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* User-callable. Sets default control parameters for AMD. See amd.h + * for details. + */ + +#include "amd_internal.h" + +GLOBAL void AMD_defaults +( + double Control [ ] +) +{ + Int i ; + if (Control != (double *) NULL) + { + for (i = 0 ; i < AMD_CONTROL ; i++) + { + Control [i] = 0 ; + } + Control [AMD_DENSE] = AMD_DEFAULT_DENSE ; + Control [AMD_AGGRESSIVE] = AMD_DEFAULT_AGGRESSIVE ; + } +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_dump.c b/deal.II/contrib/umfpack/AMD/Source/amd_dump.c new file mode 100644 index 0000000000..c526007fe7 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_dump.c @@ -0,0 +1,176 @@ +/* ========================================================================= */ +/* === AMD_dump ============================================================ */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Debugging routines for AMD. Not used if NDEBUG is not defined at compile- + * time (the default). See comments in amd_internal.h on how to enable + * debugging. Not user-callable. + */ + +#include "amd_internal.h" + +#ifndef NDEBUG + +/* This global variable is present only when debugging */ +GLOBAL Int AMD_debug = -999 ; /* default is no debug printing */ + +/* ========================================================================= */ +/* === AMD_debug_init ====================================================== */ +/* ========================================================================= */ + +/* Sets the debug print level, by reading the file debug.amd (if it exists) */ + +GLOBAL void AMD_debug_init ( char *s ) +{ + FILE *f ; + f = fopen ("debug.amd", "r") ; + if (f == (FILE *) NULL) + { + AMD_debug = -999 ; + } + else + { + fscanf (f, ID, &AMD_debug) ; + fclose (f) ; + } + if (AMD_debug >= 0) printf ("%s: AMD_debug_init, D= "ID"\n", s, AMD_debug); +} + +/* ========================================================================= */ +/* === AMD_dump ============================================================ */ +/* ========================================================================= */ + +/* Dump AMD's data structure, except for the hash buckets. This routine + * cannot be called when the hash buckets are non-empty. + */ + +GLOBAL void AMD_dump ( + Int n, /* A is n-by-n */ + Int Pe [ ], /* pe [0..n-1]: index in iw of start of row i */ + Int Iw [ ], /* workspace of size iwlen, iwlen [0..pfree-1] + * holds the matrix on input */ + Int Len [ ], /* len [0..n-1]: length for row i */ + Int iwlen, /* length of iw */ + Int pfree, /* iw [pfree ... iwlen-1] is empty on input */ + Int Nv [ ], /* nv [0..n-1] */ + Int Next [ ], /* next [0..n-1] */ + Int Last [ ], /* last [0..n-1] */ + Int Head [ ], /* head [0..n-1] */ + Int Elen [ ], /* size n */ + Int Degree [ ], /* size n */ + Int W [ ], /* size n */ + Int nel +) +{ + Int i, pe, elen, nv, len, e, p, k, j, deg, w, cnt, ilast ; + + if (AMD_debug < 0) return ; + ASSERT (pfree <= iwlen) ; + for (i = 0 ; i < n ; i++) + { + pe = Pe [i] ; + elen = Elen [i] ; + nv = Nv [i] ; + len = Len [i] ; + w = W [i] ; + + if (elen >= EMPTY) + { + if (nv == 0) + { + AMD_DEBUG3 (("\nI "ID": nonprincipal: ", i)) ; + ASSERT (elen == EMPTY) ; + if (pe == EMPTY) + { + AMD_DEBUG3 ((" dense node\n")) ; + ASSERT (w == 1) ; + } + else + { + ASSERT (pe < EMPTY) ; + AMD_DEBUG3 ((" i "ID" -> parent "ID"\n", i, FLIP (Pe[i]))); + } + } + else + { + AMD_DEBUG3 (("\nI "ID": active principal supervariable:\n",i)); + AMD_DEBUG3 ((" nv(i): "ID" Flag: %d\n", nv, (nv < 0))) ; + ASSERT (elen >= 0) ; + ASSERT (nv > 0 && pe >= 0) ; + p = pe ; + AMD_DEBUG3 ((" e/s: ")) ; + if (elen == 0) AMD_DEBUG3 ((" : ")) ; + ASSERT (pe < pfree) ; + for (k = 0 ; k < len ; k++) + { + j = Iw [p] ; + AMD_DEBUG3 ((" "ID"", j)) ; + ASSERT (j >= 0 && j < n) ; + if (k == elen-1) AMD_DEBUG3 ((" : ")) ; + p++ ; + } + AMD_DEBUG3 (("\n")) ; + } + } + else + { + e = i ; + if (w == 0) + { + AMD_DEBUG3 (("\nE "ID": absorbed element: w "ID"\n", e, w)) ; + ASSERT (nv > 0 && pe < 0) ; + AMD_DEBUG3 ((" e "ID" -> parent "ID"\n", e, FLIP (Pe [e]))) ; + } + else + { + AMD_DEBUG3 (("\nE "ID": unabsorbed element: w "ID"\n", e, w)) ; + ASSERT (nv > 0 && pe >= 0) ; + p = pe ; + AMD_DEBUG3 ((" : ")) ; + ASSERT (pe < pfree) ; + for (k = 0 ; k < len ; k++) + { + j = Iw [p] ; + AMD_DEBUG3 ((" "ID"", j)) ; + ASSERT (j >= 0 && j < n) ; + p++ ; + } + AMD_DEBUG3 (("\n")) ; + } + } + } + + /* this routine cannot be called when the hash buckets are non-empty */ + AMD_DEBUG3 (("\nDegree lists:\n")) ; + if (nel >= 0) + { + cnt = 0 ; + for (deg = 0 ; deg < n ; deg++) + { + if (Head [deg] == EMPTY) continue ; + ilast = EMPTY ; + AMD_DEBUG3 ((ID": ", deg)) ; + for (i = Head [deg] ; i != EMPTY ; i = Next [i]) + { + AMD_DEBUG3 ((" "ID" : next "ID" last "ID" deg "ID"\n", + i, Next [i], Last [i], Degree [i])) ; + ASSERT (i >= 0 && i < n && ilast == Last [i] && + deg == Degree [i]) ; + cnt += Nv [i] ; + ilast = i ; + } + AMD_DEBUG3 (("\n")) ; + } + ASSERT (cnt == n - nel) ; + } + +} + +#endif diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_info.c b/deal.II/contrib/umfpack/AMD/Source/amd_info.c new file mode 100644 index 0000000000..1cc8968f98 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_info.c @@ -0,0 +1,115 @@ +/* ========================================================================= */ +/* === AMD_info ============================================================ */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* User-callable. Prints the output statistics for AMD. See amd.h + * for details. If the Info array is not present, nothing is printed. + */ + +#include "amd_internal.h" + +#define PRI(format,x) { if (x >= 0) { PRINTF ((format, x)) ; }} + +GLOBAL void AMD_info +( + double Info [ ] +) +{ + double n, ndiv, nmultsubs_ldl, nmultsubs_lu, lnz, lnzd ; + + if (!Info) + { + return ; + } + + n = Info [AMD_N] ; + ndiv = Info [AMD_NDIV] ; + nmultsubs_ldl = Info [AMD_NMULTSUBS_LDL] ; + nmultsubs_lu = Info [AMD_NMULTSUBS_LU] ; + lnz = Info [AMD_LNZ] ; + lnzd = (n >= 0 && lnz >= 0) ? (n + lnz) : (-1) ; + + /* AMD return status */ + PRINTF (( + "\namd: approximate minimum degree ordering, results:\n" + " status: ")) ; + if (Info [AMD_STATUS] == AMD_OK) + { + PRINTF (("OK\n")) ; + } + else if (Info [AMD_STATUS] == AMD_OUT_OF_MEMORY) + { + PRINTF (("out of memory\n")) ; + } + else if (Info [AMD_STATUS] == AMD_INVALID) + { + PRINTF (("invalid matrix\n")) ; + } + else + { + PRINTF (("unknown\n")) ; + } + + /* statistics about the input matrix */ + PRI (" n, dimension of A: %.20g\n", n); + PRI (" nz, number of nonzeros in A: %.20g\n", + Info [AMD_NZ]) ; + PRI (" symmetry of A: %.4f\n", + Info [AMD_SYMMETRY]) ; + PRI (" number of nonzeros on diagonal: %.20g\n", + Info [AMD_NZDIAG]) ; + PRI (" nonzeros in pattern of A+A' (excl. diagonal): %.20g\n", + Info [AMD_NZ_A_PLUS_AT]) ; + PRI (" # dense rows/columns of A+A': %.20g\n", + Info [AMD_NDENSE]) ; + + /* statistics about AMD's behavior */ + PRI (" memory used, in bytes: %.20g\n", + Info [AMD_MEMORY]) ; + PRI (" # of memory compactions: %.20g\n", + Info [AMD_NCMPA]) ; + + /* statistics about the ordering quality */ + PRINTF (("\n" + " The following approximate statistics are for a subsequent\n" + " factorization of A(P,P) + A(P,P)'. They are slight upper\n" + " bounds if there are no dense rows/columns in A+A', and become\n" + " looser if dense rows/columns exist.\n\n")) ; + + PRI (" nonzeros in L (excluding diagonal): %.20g\n", + lnz) ; + PRI (" nonzeros in L (including diagonal): %.20g\n", + lnzd) ; + PRI (" # divide operations for LDL' or LU: %.20g\n", + ndiv) ; + PRI (" # multiply-subtract operations for LDL': %.20g\n", + nmultsubs_ldl) ; + PRI (" # multiply-subtract operations for LU: %.20g\n", + nmultsubs_lu) ; + PRI (" max nz. in any column of L (incl. diagonal): %.20g\n", + Info [AMD_DMAX]) ; + + /* total flop counts for various factorizations */ + + if (n >= 0 && ndiv >= 0 && nmultsubs_ldl >= 0 && nmultsubs_lu >= 0) + { + PRINTF (("\n" + " chol flop count for real A, sqrt counted as 1 flop: %.20g\n" + " LDL' flop count for real A: %.20g\n" + " LDL' flop count for complex A: %.20g\n" + " LU flop count for real A (with no pivoting): %.20g\n" + " LU flop count for complex A (with no pivoting): %.20g\n\n", + n + ndiv + 2*nmultsubs_ldl, + ndiv + 2*nmultsubs_ldl, + 9*ndiv + 8*nmultsubs_ldl, + ndiv + 2*nmultsubs_lu, + 9*ndiv + 8*nmultsubs_lu)) ; + } +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_internal.h b/deal.II/contrib/umfpack/AMD/Source/amd_internal.h new file mode 100644 index 0000000000..e1ec500bd3 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_internal.h @@ -0,0 +1,426 @@ +/* ========================================================================= */ +/* === amd_internal.h ====================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* This file is for internal use in AMD itself, and does not normally need to + * be included in user code. Use amd.h instead. + * + * The following compile-time definitions affect how AMD is compiled. + * + * -DMATLAB_MEX_FILE + * + * This flag is turned on when compiling the amd mexFunction for + * use in MATLAB. + * + * -DMATHWORKS + * + * This flag is turned on when compiling amd as a built-in routine + * in MATLAB. Internal routines utMalloc, utFree, utRealloc, and + * utPrintf are used, and the MathWorks "util.h" file is included. + * This option is intended for use by The MathWorks, Inc., only. + * + * -DNDEBUG + * + * Debugging mode (if NDEBUG is not defined). The default, of course, + * is no debugging. Turning on debugging takes some work (see below). + * If you do not edit this file, then debugging is turned off anyway, + * regardless of whether or not -DNDEBUG is specified in your compiler + * options. + * + * -DALLOCATE=allocation_routine + * -DFREE=free_routine + * + * If you do not wish to use malloc or free, you can define the + * routines to be used here. You must specify both of them, or + * neither. + * + * -DPRINTF=printf_routine + * + * If you wish to use a routine other than printf, you can define it + * with -DPRINTF= followed by the name of the printf replacement. + */ + +/* ========================================================================= */ +/* === NDEBUG ============================================================== */ +/* ========================================================================= */ + +/* + AMD will be exceedingly slow when running in debug mode. The next three + lines ensure that debugging is turned off. +*/ +#ifndef NDEBUG +#define NDEBUG +#endif + +/* + To enable debugging, uncomment the following line: +#undef NDEBUG +*/ + +/* ------------------------------------------------------------------------- */ +/* ANSI include files */ +/* ------------------------------------------------------------------------- */ + +/* from stdlib.h: malloc, free, realloc (when not compiling for MATLAB) */ +#include + +/* from stdio.h: printf. When in debug mode: fopen, fscanf */ +#include + +/* from limits.h: INT_MAX and LONG_MAX */ +#include + +/* from math.h: sqrt */ +#include + +/* ------------------------------------------------------------------------- */ +/* MATLAB include files (only if being used in or via MATLAB) */ +/* ------------------------------------------------------------------------- */ + +#ifdef MATHWORKS +#include "util.h" +#endif + +#ifdef MATLAB_MEX_FILE +#include "matrix.h" +#include "mex.h" +#endif + +/* ------------------------------------------------------------------------- */ +/* basic definitions */ +/* ------------------------------------------------------------------------- */ + +#ifdef FLIP +#undef FLIP +#endif + +#ifdef MAX +#undef MAX +#endif + +#ifdef MIN +#undef MIN +#endif + +#ifdef EMPTY +#undef EMPTY +#endif + +#ifdef GLOBAL +#undef GLOBAL +#endif + +#ifdef PRIVATE +#undef PRIVATE +#endif + +/* FLIP is a "negation about -1", and is used to mark an integer i that is + * normally non-negative. FLIP (EMPTY) is EMPTY. FLIP of a number > EMPTY + * is negative, and FLIP of a number < EMTPY is positive. FLIP (FLIP (i)) = i + * for all integers i. UNFLIP (i) is >= EMPTY. */ +#define EMPTY (-1) +#define FLIP(i) (-(i)-2) +#define UNFLIP(i) ((i < EMPTY) ? FLIP (i) : (i)) + +/* for integer MAX/MIN, or for doubles when we don't care how NaN's behave: */ +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +/* logical expression of p implies q: */ +#define IMPLIES(p,q) (!(p) || (q)) + +/* Note that the IBM RS 6000 xlc predefines TRUE and FALSE in . */ +/* The Compaq Alpha also predefines TRUE and FALSE. */ +#ifdef TRUE +#undef TRUE +#endif +#ifdef FALSE +#undef FALSE +#endif + +#define TRUE (1) +#define FALSE (0) +#define PRIVATE static +#define GLOBAL +#define EMPTY (-1) + +/* Note that Linux's gcc 2.96 defines NULL as ((void *) 0), but other */ +/* compilers (even gcc 2.95.2 on Solaris) define NULL as 0 or (0). We */ +/* need to use the ANSI standard value of 0. */ +#ifdef NULL +#undef NULL +#endif + +#define NULL 0 + +/* ------------------------------------------------------------------------- */ +/* integer type for AMD: int or long */ +/* ------------------------------------------------------------------------- */ + +#if defined (DLONG) || defined (ZLONG) + +#define Int long +#define ID "%ld" +#define Int_MAX LONG_MAX +#define Int_MIN LONG_MIN + +#define AMD_order amd_l_order +#define AMD_defaults amd_l_defaults +#define AMD_control amd_l_control +#define AMD_info amd_l_info +#define AMD_1 amd_l1 +#define AMD_2 amd_l2 +#define AMD_valid amd_l_valid +#define AMD_aat amd_l_aat +#define AMD_postorder amd_l_postorder +#define AMD_post_tree amd_l_post_tree +#define AMD_dump amd_l_dump +#define AMD_debug amd_l_debug +#define AMD_debug_init amd_l_debug_init +#define AMD_wpreprocess amd_l_wpreprocess +#define AMD_preprocess amd_l_preprocess +#define AMD_preprocess_valid amd_l_preprocess_valid + +#else + +#define Int int +#define ID "%d" +#define Int_MAX INT_MAX +#define Int_MIN INT_MIN + +#define AMD_order amd_order +#define AMD_defaults amd_defaults +#define AMD_control amd_control +#define AMD_info amd_info +#define AMD_1 amd_1 +#define AMD_2 amd_2 +#define AMD_valid amd_valid +#define AMD_aat amd_aat +#define AMD_postorder amd_postorder +#define AMD_post_tree amd_post_tree +#define AMD_dump amd_dump +#define AMD_debug amd_debug +#define AMD_debug_init amd_debug_init +#define AMD_wpreprocess amd_wpreprocess +#define AMD_preprocess amd_preprocess +#define AMD_preprocess_valid amd_preprocess_valid + +#endif + +/* ========================================================================= */ +/* === Memory allocator ==================================================== */ +/* ========================================================================= */ + +/* The MATLAB mexFunction uses MATLAB's memory manager, while the C-callable */ +/* AMD routine uses the ANSI C malloc, free, and realloc routines. */ + +#ifndef ALLOCATE +#ifdef MATLAB_MEX_FILE +#define ALLOCATE mxMalloc +#define FREE mxFree +#else +#ifdef MATHWORKS +/* Compiling as a built-in routine. Since out-of-memory conditions are checked + * after every allocation, we can use ut* routines here. */ +#define ALLOCATE utMalloc +#define FREE utFree +#else +/* use the ANSI C memory allocation routines */ +#define ALLOCATE malloc +#define FREE free +#endif +#endif +#endif + + +/* ========================================================================= */ +/* === PRINTF macro ======================================================== */ +/* ========================================================================= */ + +/* All output goes through the PRINTF macro. */ + +#ifndef PRINTF +#ifdef MATLAB_MEX_FILE +#define PRINTF(params) { (void) mexPrintf params ; } +#else +#ifdef MATHWORKS +#define PRINTF(params) { (void) utPrintf params ; } +#else +#define PRINTF(params) { (void) printf params ; } +#endif +#endif +#endif + +/* ------------------------------------------------------------------------- */ +/* AMD routine definitions (user-callable) */ +/* ------------------------------------------------------------------------- */ + +#include "amd.h" + +/* ------------------------------------------------------------------------- */ +/* AMD routine definitions (not user-callable) */ +/* ------------------------------------------------------------------------- */ + +GLOBAL Int AMD_valid +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ] +) ; + +GLOBAL Int AMD_aat +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + Int Len [ ], + Int Tp [ ], + double Info [ ] +) ; + +GLOBAL void AMD_1 +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + Int P [ ], + Int Pinv [ ], + Int Len [ ], + Int slen, + Int S [ ], + double Control [ ], + double Info [ ] +) ; + +GLOBAL void AMD_2 ( + Int n, + Int Pe [ ], + Int Iw [ ], + Int Len [ ], + Int iwlen, + Int pfree, + Int Nv [ ], + Int Next [ ], + Int Last [ ], + Int Head [ ], + Int Elen [ ], + Int Degree [ ], + Int W [ ], + double Control [ ], + double Info [ ] +) ; + +GLOBAL void AMD_postorder +( + Int nn, + Int Parent [ ], + Int Npiv [ ], + Int Fsize [ ], + Int Order [ ], + Int Child [ ], + Int Sibling [ ], + Int Stack [ ] +) ; + +GLOBAL Int AMD_post_tree +( + Int root, + Int k, + Int Child [ ], + const Int Sibling [ ], + Int Order [ ], + Int Stack [ ] +#ifndef NDEBUG + , Int nn +#endif +) ; + +GLOBAL void AMD_wpreprocess +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + Int Rp [ ], + Int Ri [ ], + Int W [ ], + Int Flag [ ] +) ; + +GLOBAL Int AMD_preprocess_valid +( + Int n, + const Int Ap [ ], + const Int Ai [ ] +) ; + +/* ------------------------------------------------------------------------- */ +/* debugging definitions */ +/* ------------------------------------------------------------------------- */ + +/* from assert.h: assert macro */ +#if !defined (MATHWORKS) && !defined (MATLAB_MEX_FILE) +#include +#endif + +#ifndef NDEBUG + +GLOBAL Int AMD_debug ; + +GLOBAL void AMD_debug_init ( char *s ) ; + +GLOBAL void AMD_dump ( + Int n, + Int Pe [ ], + Int Iw [ ], + Int Len [ ], + Int iwlen, + Int pfree, + Int Nv [ ], + Int Next [ ], + Int Last [ ], + Int Head [ ], + Int Elen [ ], + Int Degree [ ], + Int W [ ], + Int nel +) ; + +#ifdef ASSERT +#undef ASSERT +#endif + +#ifdef MATLAB_MEX_FILE +#define ASSERT(expression) (mxAssert ((expression), "")) +#else +#ifdef MATHWORKS +#define ASSERT(expression) (utAssert (expression)) +#else +#define ASSERT(expression) (assert (expression)) +#endif +#endif /* MATLAB_MEX_FILE */ + +#define AMD_DEBUG0(params) { PRINTF (params) ; } +#define AMD_DEBUG1(params) { if (AMD_debug >= 1) PRINTF (params) ; } +#define AMD_DEBUG2(params) { if (AMD_debug >= 2) PRINTF (params) ; } +#define AMD_DEBUG3(params) { if (AMD_debug >= 3) PRINTF (params) ; } +#define AMD_DEBUG4(params) { if (AMD_debug >= 4) PRINTF (params) ; } + +#else + +#define AMD_DEBUG0(params) +#define AMD_DEBUG1(params) +#define AMD_DEBUG2(params) +#define AMD_DEBUG3(params) +#define AMD_DEBUG4(params) + +#define ASSERT(expression) + +#endif diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_order.c b/deal.II/contrib/umfpack/AMD/Source/amd_order.c new file mode 100644 index 0000000000..eac5471a0b --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_order.c @@ -0,0 +1,139 @@ +/* ========================================================================= */ +/* === AMD_order =========================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* User-callable AMD minimum degree ordering routine. See amd.h for + * documentation. + */ + +#include "amd_internal.h" + +GLOBAL Int AMD_order +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + Int P [ ], + double Control [ ], + double Info [ ] +) +{ + Int slen, *Len, *S, nz, nzaat, i, *Pinv, info ; + +#ifndef NDEBUG + AMD_debug_init ("amd") ; +#endif + + /* clear the Info array, if it exists */ + info = Info != (double *) NULL ; + if (info) + { + for (i = 0 ; i < AMD_INFO ; i++) + { + Info [i] = EMPTY ; + } + Info [AMD_N] = n ; + Info [AMD_STATUS] = AMD_OK ; + } + + /* make sure inputs exist and n is >= 0 */ + if (Ai == (Int *) NULL || Ap == (Int *) NULL || P == (Int *) NULL || n < 0) + { + if (info) Info [AMD_STATUS] = AMD_INVALID ; + return (AMD_INVALID) ; /* arguments are invalid */ + } + + if (n == 0) + { + return (AMD_OK) ; /* n is 0 so there's nothing to do */ + } + + nz = Ap [n] ; + if (info) + { + Info [AMD_NZ] = nz ; + } + if (nz < 0) + { + if (info) Info [AMD_STATUS] = AMD_INVALID ; + return (AMD_INVALID) ; + } + + /* Avoid integer overflow in memory size calculations. The space required + * by AMD is at most 2.4nz + 8n for S, and n for Len. + * Note nz - n <= nzaat <= 2*nz, below. */ + if ((2.4 * (double) nz + 8 * (double) n) > (double) Int_MAX / sizeof (Int)) + { + /* :: int overflow :: */ + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; + } + + if (!AMD_valid (n, n, Ap, Ai)) + { + if (info) Info [AMD_STATUS] = AMD_INVALID ; + return (AMD_INVALID) ; /* matrix is invalid */ + } + + /* --------------------------------------------------------------------- */ + /* determine the symmetry and count off-diagonal nonzeros in A+A' */ + /* --------------------------------------------------------------------- */ + + /* allocate size-n integer workspace */ + Len = (Int *) ALLOCATE (n * sizeof (Int)) ; + if (!Len) + { + /* :: out of memory :: */ + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; + } + nzaat = AMD_aat (n, Ap, Ai, Len, P, Info) ; + AMD_DEBUG1 (("nzaat: "ID"\n", nzaat)) ; + ASSERT (nz-n <= nzaat && nzaat <= 2*nz) ; + + /* --------------------------------------------------------------------- */ + /* allocate workspace for matrix, elbow room, and 7 size-n vectors */ + /* --------------------------------------------------------------------- */ + + slen = (nzaat + nzaat/5 + n) + 7*n ; + if (info) + { + /* memory usage (Len and S), in bytes. */ + Info [AMD_MEMORY] = ((double) slen + n) * sizeof (Int) ; + } + S = (Int *) ALLOCATE (slen * sizeof (Int)) ; + AMD_DEBUG1 ((" S "ID" Len "ID" n "ID" nzaat "ID" slen "ID"\n", + (Int) S, (Int) Len, n, nzaat, slen)) ; + if (S == (Int *) NULL) + { + /* :: out of memory :: */ + FREE (Len) ; + if (Info != (double *) NULL) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; + } + + /* allocate space from S for Pinv */ + Pinv = S + slen - n ; + slen -= n ; + + /* --------------------------------------------------------------------- */ + /* order the matrix */ + /* --------------------------------------------------------------------- */ + + AMD_1 (n, Ap, Ai, P, Pinv, Len, slen, S, Control, Info) ; + + /* --------------------------------------------------------------------- */ + /* free the workspace */ + /* --------------------------------------------------------------------- */ + + FREE (Len) ; + FREE (S) ; + return (AMD_OK) ; /* successful ordering */ +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_post_tree.c b/deal.II/contrib/umfpack/AMD/Source/amd_post_tree.c new file mode 100644 index 0000000000..3928925457 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_post_tree.c @@ -0,0 +1,121 @@ +/* ========================================================================= */ +/* === AMD_post_tree ======================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Post-ordering of a supernodal elimination tree. */ + +#include "amd_internal.h" + +GLOBAL Int AMD_post_tree +( + Int root, /* root of the tree */ + Int k, /* start numbering at k */ + Int Child [ ], /* input argument of size nn, undefined on + * output. Child [i] is the head of a link + * list of all nodes that are children of node + * i in the tree. */ + const Int Sibling [ ], /* input argument of size nn, not modified. + * If f is a node in the link list of the + * children of node i, then Sibling [f] is the + * next child of node i. + */ + Int Order [ ], /* output order, of size nn. Order [i] = k + * if node i is the kth node of the reordered + * tree. */ + Int Stack [ ] /* workspace of size nn */ +#ifndef NDEBUG + , Int nn /* nodes are in the range 0..nn-1. */ +#endif +) +{ + Int f, head, h, i ; + +#if 0 + /* --------------------------------------------------------------------- */ + /* recursive version (Stack [ ] is not used): */ + /* --------------------------------------------------------------------- */ + + /* this is simple, but can caouse stack overflow if nn is large */ + i = root ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + k = AMD_post_tree (f, k, Child, Sibling, Order, Stack, nn) ; + } + Order [i] = k++ ; + return (k) ; +#endif + + /* --------------------------------------------------------------------- */ + /* non-recursive version, using an explicit stack */ + /* --------------------------------------------------------------------- */ + + /* push root on the stack */ + head = 0 ; + Stack [0] = root ; + + while (head >= 0) + { + /* get head of stack */ + ASSERT (head < nn) ; + i = Stack [head] ; + AMD_DEBUG1 (("head of stack "ID" \n", i)) ; + ASSERT (i >= 0 && i < nn) ; + + if (Child [i] != EMPTY) + { + /* the children of i are not yet ordered */ + /* push each child onto the stack in reverse order */ + /* so that small ones at the head of the list get popped first */ + /* and the biggest one at the end of the list gets popped last */ + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + head++ ; + ASSERT (head < nn) ; + ASSERT (f >= 0 && f < nn) ; + } + h = head ; + ASSERT (head < nn) ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + ASSERT (h > 0) ; + Stack [h--] = f ; + AMD_DEBUG1 (("push "ID" on stack\n", f)) ; + ASSERT (f >= 0 && f < nn) ; + } + ASSERT (Stack [h] == i) ; + + /* delete child list so that i gets ordered next time we see it */ + Child [i] = EMPTY ; + } + else + { + /* the children of i (if there were any) are already ordered */ + /* remove i from the stack and order it. Front i is kth front */ + head-- ; + AMD_DEBUG1 (("pop "ID" order "ID"\n", i, k)) ; + Order [i] = k++ ; + ASSERT (k <= nn) ; + } + +#ifndef NDEBUG + AMD_DEBUG1 (("\nStack:")) ; + for (h = head ; h >= 0 ; h--) + { + Int j = Stack [h] ; + AMD_DEBUG1 ((" "ID, j)) ; + ASSERT (j >= 0 && j < nn) ; + } + AMD_DEBUG1 (("\n\n")) ; + ASSERT (head < nn) ; +#endif + + } + return (k) ; +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_postorder.c b/deal.II/contrib/umfpack/AMD/Source/amd_postorder.c new file mode 100644 index 0000000000..2b1bcd6893 --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_postorder.c @@ -0,0 +1,207 @@ +/* ========================================================================= */ +/* === AMD_postorder ======================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Perform a postordering (via depth-first search) of an assembly tree. */ + +#include "amd_internal.h" + +GLOBAL void AMD_postorder +( + /* inputs, not modified on output: */ + Int nn, /* nodes are in the range 0..nn-1 */ + Int Parent [ ], /* Parent [j] is the parent of j, or EMPTY if root */ + Int Nv [ ], /* Nv [j] > 0 number of pivots represented by node j, + * or zero if j is not a node. */ + Int Fsize [ ], /* Fsize [j]: size of node j */ + + /* output, not defined on input: */ + Int Order [ ], /* output post-order */ + + /* workspaces of size nn: */ + Int Child [ ], + Int Sibling [ ], + Int Stack [ ] +) +{ + Int i, j, k, parent, frsize, f, fprev, maxfrsize, bigfprev, bigf, fnext ; + + for (j = 0 ; j < nn ; j++) + { + Child [j] = EMPTY ; + Sibling [j] = EMPTY ; + } + + /* --------------------------------------------------------------------- */ + /* place the children in link lists - bigger elements tend to be last */ + /* --------------------------------------------------------------------- */ + + for (j = nn-1 ; j >= 0 ; j--) + { + if (Nv [j] > 0) + { + /* this is an element */ + parent = Parent [j] ; + if (parent != EMPTY) + { + /* place the element in link list of the children its parent */ + /* bigger elements will tend to be at the end of the list */ + Sibling [j] = Child [parent] ; + Child [parent] = j ; + } + } + } + +#ifndef NDEBUG + { + Int nels, ff, nchild ; + AMD_DEBUG1 (("\n\n================================ AMD_postorder:\n")); + nels = 0 ; + for (j = 0 ; j < nn ; j++) + { + if (Nv [j] > 0) + { + AMD_DEBUG1 (( ""ID" : nels "ID" npiv "ID" size "ID + " parent "ID" maxfr "ID"\n", j, nels, + Nv [j], Fsize [j], Parent [j], Fsize [j])) ; + /* this is an element */ + /* dump the link list of children */ + nchild = 0 ; + AMD_DEBUG1 ((" Children: ")) ; + for (ff = Child [j] ; ff != EMPTY ; ff = Sibling [ff]) + { + AMD_DEBUG1 ((ID" ", ff)) ; + ASSERT (Parent [ff] == j) ; + nchild++ ; + ASSERT (nchild < nn) ; + } + AMD_DEBUG1 (("\n")) ; + parent = Parent [j] ; + if (parent != EMPTY) + { + ASSERT (Nv [parent] > 0) ; + } + nels++ ; + } + } + } + AMD_DEBUG1 (("\n\nGo through the children of each node, and put\n" + "the biggest child last in each list:\n")) ; +#endif + + /* --------------------------------------------------------------------- */ + /* place the largest child last in the list of children for each node */ + /* --------------------------------------------------------------------- */ + + for (i = 0 ; i < nn ; i++) + { + if (Nv [i] > 0 && Child [i] != EMPTY) + { + +#ifndef NDEBUG + Int nchild ; + AMD_DEBUG1 (("Before partial sort, element "ID"\n", i)) ; + nchild = 0 ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + ASSERT (f >= 0 && f < nn) ; + AMD_DEBUG1 ((" f: "ID" size: "ID"\n", f, Fsize [f])) ; + nchild++ ; + ASSERT (nchild <= nn) ; + } +#endif + + /* find the biggest element in the child list */ + fprev = EMPTY ; + maxfrsize = EMPTY ; + bigfprev = EMPTY ; + bigf = EMPTY ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + ASSERT (f >= 0 && f < nn) ; + frsize = Fsize [f] ; + if (frsize >= maxfrsize) + { + /* this is the biggest seen so far */ + maxfrsize = frsize ; + bigfprev = fprev ; + bigf = f ; + } + fprev = f ; + } + ASSERT (bigf != EMPTY) ; + + fnext = Sibling [bigf] ; + + AMD_DEBUG1 (("bigf "ID" maxfrsize "ID" bigfprev "ID" fnext "ID + " fprev " ID"\n", bigf, maxfrsize, bigfprev, fnext, fprev)) ; + + if (fnext != EMPTY) + { + /* if fnext is EMPTY then bigf is already at the end of list */ + + if (bigfprev == EMPTY) + { + /* delete bigf from the element of the list */ + Child [i] = fnext ; + } + else + { + /* delete bigf from the middle of the list */ + Sibling [bigfprev] = fnext ; + } + + /* put bigf at the end of the list */ + Sibling [bigf] = EMPTY ; + ASSERT (Child [i] != EMPTY) ; + ASSERT (fprev != bigf) ; + ASSERT (fprev != EMPTY) ; + Sibling [fprev] = bigf ; + } + +#ifndef NDEBUG + AMD_DEBUG1 (("After partial sort, element "ID"\n", i)) ; + for (f = Child [i] ; f != EMPTY ; f = Sibling [f]) + { + ASSERT (f >= 0 && f < nn) ; + AMD_DEBUG1 ((" "ID" "ID"\n", f, Fsize [f])) ; + ASSERT (Nv [f] > 0) ; + nchild-- ; + } + ASSERT (nchild == 0) ; +#endif + + } + } + + /* --------------------------------------------------------------------- */ + /* postorder the assembly tree */ + /* --------------------------------------------------------------------- */ + + for (i = 0 ; i < nn ; i++) + { + Order [i] = EMPTY ; + } + + k = 0 ; + + for (i = 0 ; i < nn ; i++) + { + if (Parent [i] == EMPTY && Nv [i] > 0) + { + AMD_DEBUG1 (("Root of assembly tree "ID"\n", i)) ; + k = AMD_post_tree (i, k, Child, Sibling, Order, Stack +#ifndef NDEBUG + , nn +#endif + ) ; + } + } +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_preprocess.c b/deal.II/contrib/umfpack/AMD/Source/amd_preprocess.c new file mode 100644 index 0000000000..db635e2d0d --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_preprocess.c @@ -0,0 +1,225 @@ +/* ========================================================================= */ +/* === AMD_preprocess ====================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Sorts, removes duplicate entries, and transposes from the nonzero pattern of + * a column-form matrix A, to obtain the matrix R. + * See amd.h for a complete description of AMD_preprocess + */ + +#include "amd_internal.h" + +GLOBAL Int AMD_preprocess /* returns AMD_OK if input is OK, AMD_INVALID + * if the matrix is invalid, or AMD_OUT_OF_MEMORY + * if out of memory for the 2n workspace. */ +( + Int n, /* input matrix: A is n-by-n */ + const Int Ap [ ], /* size n+1 */ + const Int Ai [ ], /* size nz = Ap [n] */ + + /* output matrix R: */ + Int Rp [ ], /* size n+1 */ + Int Ri [ ] /* size nz (or less, if duplicates present) */ +) +{ + /* --------------------------------------------------------------------- */ + /* local variables */ + /* --------------------------------------------------------------------- */ + + Int *Flag, *W ; + + /* --------------------------------------------------------------------- */ + /* check inputs (note: fewer restrictions than AMD_order) */ + /* --------------------------------------------------------------------- */ + + if (!AMD_preprocess_valid (n, Ap, Ai) || !Ri || !Rp) + { + return (AMD_INVALID) ; + } + + /* --------------------------------------------------------------------- */ + /* allocate workspace */ + /* --------------------------------------------------------------------- */ + + W = (Int *) ALLOCATE (MAX (n,1) * sizeof (Int)) ; + if (!W) + { + return (AMD_OUT_OF_MEMORY) ; + } + Flag = (Int *) ALLOCATE (MAX (n,1) * sizeof (Int)) ; + if (!Flag) + { + FREE (W) ; + return (AMD_OUT_OF_MEMORY) ; + } + + /* --------------------------------------------------------------------- */ + /* preprocess the matrix: sort, remove duplicates, and transpose */ + /* --------------------------------------------------------------------- */ + + AMD_wpreprocess (n, Ap, Ai, Rp, Ri, W, Flag) ; + + /* --------------------------------------------------------------------- */ + /* free the workspace */ + /* --------------------------------------------------------------------- */ + + FREE (W) ; + FREE (Flag) ; + return (AMD_OK) ; +} + + +/* ========================================================================= */ +/* === AMD_wpreprocess ===================================================== */ +/* ========================================================================= */ + +/* The AMD_wpreprocess routine is not user-callable. It does not check its + * input for errors or allocate workspace (that is done by the user-callable + * AMD_preprocess routine). It does handle the n=0 case. */ + +GLOBAL void AMD_wpreprocess +( + Int n, /* input matrix: A is n-by-n */ + const Int Ap [ ], /* size n+1 */ + const Int Ai [ ], /* size nz = Ap [n] */ + + /* output matrix R: */ + Int Rp [ ], /* size n+1 */ + Int Ri [ ], /* size nz (or less, if duplicates present) */ + + Int W [ ], /* workspace of size n */ + Int Flag [ ] /* workspace of size n */ +) +{ + + /* --------------------------------------------------------------------- */ + /* local variables */ + /* --------------------------------------------------------------------- */ + + Int i, j, p, p2 ; + + /* --------------------------------------------------------------------- */ + /* count the entries in each row of A (excluding duplicates) */ + /* --------------------------------------------------------------------- */ + + for (i = 0 ; i < n ; i++) + { + W [i] = 0 ; /* # of nonzeros in row i (excl duplicates) */ + Flag [i] = EMPTY ; /* Flag [i] = j if i appears in column j */ + } + for (j = 0 ; j < n ; j++) + { + p2 = Ap [j+1] ; + for (p = Ap [j] ; p < p2 ; p++) + { + i = Ai [p] ; + if (Flag [i] != j) + { + /* row index i has not yet appeared in column j */ + W [i]++ ; /* one more entry in row i */ + Flag [i] = j ; /* flag row index i as appearing in col j*/ + } + } + } + + /* --------------------------------------------------------------------- */ + /* compute the row pointers for R */ + /* --------------------------------------------------------------------- */ + + Rp [0] = 0 ; + for (i = 0 ; i < n ; i++) + { + Rp [i+1] = Rp [i] + W [i] ; + } + for (i = 0 ; i < n ; i++) + { + W [i] = Rp [i] ; + Flag [i] = EMPTY ; + } + + /* --------------------------------------------------------------------- */ + /* construct the row form matrix R */ + /* --------------------------------------------------------------------- */ + + /* R = row form of pattern of A */ + for (j = 0 ; j < n ; j++) + { + p2 = Ap [j+1] ; + for (p = Ap [j] ; p < p2 ; p++) + { + i = Ai [p] ; + if (Flag [i] != j) + { + /* row index i has not yet appeared in column j */ + Ri [W [i]++] = j ; /* put col j in row i */ + Flag [i] = j ; /* flag row index i as appearing in col j*/ + } + } + } + +#ifndef NDEBUG + for (j = 0 ; j < n ; j++) + { + ASSERT (W [j] == Rp [j+1]) ; + } + ASSERT (AMD_valid (n, n, Rp, Ri)) ; +#endif +} + + +/* ========================================================================= */ +/* === AMD_preprocess_valid ================================================ */ +/* ========================================================================= */ + +/* Not user-callable. Checks a matrix and returns TRUE if it is valid as input + * to AMD_wpreprocess, FALSE otherwise. */ + +GLOBAL Int AMD_preprocess_valid +( + Int n, + const Int Ap [ ], + const Int Ai [ ] +) +{ + Int i, j, p, nz ; + + if (n < 0 || !Ai || !Ap) + { + return (FALSE) ; + } + nz = Ap [n] ; + if (Ap [0] != 0 || nz < 0) + { + /* column pointers must start at Ap [0] = 0, and Ap [n] must be >= 0 */ + AMD_DEBUG0 (("column 0 pointer bad or nz < 0\n")) ; + return (FALSE) ; + } + for (j = 0 ; j < n ; j++) + { + if (Ap [j] > Ap [j+1]) + { + /* column pointers must be ascending */ + AMD_DEBUG0 (("column "ID" pointer bad\n", j)) ; + return (FALSE) ; + } + } + for (p = 0 ; p < nz ; p++) + { + i = Ai [p] ; + AMD_DEBUG3 (("row: "ID"\n", i)) ; + if (i < 0 || i >= n) + { + /* row index out of range */ + AMD_DEBUG0 (("index out of range, col "ID" row "ID"\n", j, i)) ; + return (FALSE) ; + } + } + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amd_valid.c b/deal.II/contrib/umfpack/AMD/Source/amd_valid.c new file mode 100644 index 0000000000..77e3dea68c --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amd_valid.c @@ -0,0 +1,78 @@ +/* ========================================================================= */ +/* === AMD_valid =========================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* Check if a column-form matrix is valid or not. The matrix A is + * n_row-by-n_col. The row indices of entries in column j are in + * Ai [Ap [j] ... Ap [j+1]-1]. Required conditions are: + * + * n_row >= 0 + * n_col >= 0 + * nz = Ap [n_col] >= 0 number of entries in the matrix + * Ap [0] == 0 + * Ap [j] <= Ap [j+1] for all j in the range 0 to n_col. + * row indices in Ai [Ap [j] ... Ap [j+1]-1] must be sorted in ascending + * order, must be in the range 0 to n_row-1, and no duplicate entries + * can exist. + * + * Not user-callable. + */ + +#include "amd_internal.h" + +GLOBAL Int AMD_valid +( + /* inputs, not modified on output: */ + Int n_row, /* A is n_row-by-n_col */ + Int n_col, + const Int Ap [ ], /* column pointers of A, of size n_col+1 */ + const Int Ai [ ] /* row indices of A, of size nz = Ap [n_col] */ +) +{ + Int nz, j, p1, p2, ilast, i, p ; + if (n_row < 0 || n_col < 0) + { + AMD_DEBUG0 (("n must be >= 0: "ID" "ID"\n", n_row, n_col)) ; + return (FALSE) ; + } + nz = Ap [n_col] ; + if (Ap [0] != 0 || nz < 0) + { + /* column pointers must start at Ap [0] = 0, and Ap [n] must be >= 0 */ + AMD_DEBUG0 (("column 0 pointer bad or nz < 0\n")) ; + return (FALSE) ; + } + for (j = 0 ; j < n_col ; j++) + { + p1 = Ap [j] ; + p2 = Ap [j+1] ; + AMD_DEBUG2 (("\nColumn: "ID" p1: "ID" p2: "ID"\n", j, p1, p2)) ; + if (p1 > p2) + { + /* column pointers must be ascending */ + AMD_DEBUG0 (("column "ID" pointer bad\n", j)) ; + return (FALSE) ; + } + ilast = EMPTY ; + for (p = p1 ; p < p2 ; p++) + { + i = Ai [p] ; + AMD_DEBUG3 (("row: "ID"\n", i)) ; + if (i <= ilast || i >= n_row) + { + /* row index out of range, or unsorted */ + AMD_DEBUG0 (("index out of range, col "ID" row "ID"\n", j, i)); + return (FALSE) ; + } + ilast = i ; + } + } + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/AMD/Source/amdbar.f b/deal.II/contrib/umfpack/AMD/Source/amdbar.f new file mode 100644 index 0000000000..13843928ba --- /dev/null +++ b/deal.II/contrib/umfpack/AMD/Source/amdbar.f @@ -0,0 +1,1206 @@ +C----------------------------------------------------------------------- +C AMDBAR: approximate minimum degree, without aggressive absorption +C----------------------------------------------------------------------- + + SUBROUTINE AMDBAR + $ (N, PE, IW, LEN, IWLEN, PFREE, NV, NEXT, + $ LAST, HEAD, ELEN, DEGREE, NCMPA, W) + + INTEGER N, IWLEN, PFREE, NCMPA, IW (IWLEN), PE (N), + $ DEGREE (N), NV (N), NEXT (N), LAST (N), HEAD (N), + $ ELEN (N), W (N), LEN (N) + +C Given a representation of the nonzero pattern of a symmetric matrix, +C A, (excluding the diagonal) perform an approximate minimum +C (UMFPACK/MA38-style) degree ordering to compute a pivot order +C such that the introduction of nonzeros (fill-in) in the Cholesky +C factors A = LL^T are kept low. At each step, the pivot +C selected is the one with the minimum UMFPACK/MA38-style +C upper-bound on the external degree. +C +C This routine does not do aggresive absorption (as done by AMD). + +C ********************************************************************** +C ***** CAUTION: ARGUMENTS ARE NOT CHECKED FOR ERRORS ON INPUT. ****** +C ********************************************************************** + +C References: +C +C [1] Timothy A. Davis and Iain Duff, "An unsymmetric-pattern +C multifrontal method for sparse LU factorization", SIAM J. +C Matrix Analysis and Applications, vol. 18, no. 1, pp. +C 140-158. Discusses UMFPACK / MA38, which first introduced +C the approximate minimum degree used by this routine. +C +C [2] Patrick Amestoy, Timothy A. Davis, and Iain S. Duff, "An +C approximate degree ordering algorithm," SIAM J. Matrix +C Analysis and Applications, vol. 17, no. 4, pp. 886-905, +C 1996. Discusses AMD, AMDBAR, and MC47B. +C +C [3] Alan George and Joseph Liu, "The evolution of the minimum +C degree ordering algorithm," SIAM Review, vol. 31, no. 1, +C pp. 1-19, 1989. We list below the features mentioned in +C that paper that this code includes: +C +C mass elimination: +C Yes. MA27 relied on supervariable detection for mass +C elimination. +C indistinguishable nodes: +C Yes (we call these "supervariables"). This was also in +C the MA27 code - although we modified the method of +C detecting them (the previous hash was the true degree, +C which we no longer keep track of). A supervariable is +C a set of rows with identical nonzero pattern. All +C variables in a supervariable are eliminated together. +C Each supervariable has as its numerical name that of +C one of its variables (its principal variable). +C quotient graph representation: +C Yes. We use the term "element" for the cliques formed +C during elimination. This was also in the MA27 code. +C The algorithm can operate in place, but it will work +C more efficiently if given some "elbow room." +C element absorption: +C Yes. This was also in the MA27 code. +C external degree: +C Yes. The MA27 code was based on the true degree. +C incomplete degree update and multiple elimination: +C No. This was not in MA27, either. Our method of +C degree update within MC47B/BD is element-based, not +C variable-based. It is thus not well-suited for use +C with incomplete degree update or multiple elimination. + +C----------------------------------------------------------------------- +C Authors, and Copyright (C) 1995 by: +C Timothy A. Davis, Patrick Amestoy, Iain S. Duff, & John K. Reid. +C +C Acknowledgements: +C This work (and the UMFPACK package) was supported by the +C National Science Foundation (ASC-9111263 and DMS-9223088). +C The UMFPACK/MA38 approximate degree update algorithm, the +C unsymmetric analog which forms the basis of MC47B/BD, was +C developed while Tim Davis was supported by CERFACS (Toulouse, +C France) in a post-doctoral position. +C +C Date: September, 1995 +C----------------------------------------------------------------------- + +C----------------------------------------------------------------------- +C INPUT ARGUMENTS (unaltered): +C----------------------------------------------------------------------- + +C n: The matrix order. +C +C Restriction: 1 .le. n .lt. (iovflo/2)-2, where iovflo is +C the largest positive integer that your computer can represent. + +C iwlen: The length of iw (1..iwlen). On input, the matrix is +C stored in iw (1..pfree-1). However, iw (1..iwlen) should be +C slightly larger than what is required to hold the matrix, at +C least iwlen .ge. pfree + n is recommended. Otherwise, +C excessive compressions will take place. +C *** We do not recommend running this algorithm with *** +C *** iwlen .lt. pfree + n. *** +C *** Better performance will be obtained if *** +C *** iwlen .ge. pfree + n *** +C *** or better yet *** +C *** iwlen .gt. 1.2 * pfree *** +C *** (where pfree is its value on input). *** +C The algorithm will not run at all if iwlen .lt. pfree-1. +C +C Restriction: iwlen .ge. pfree-1 + +C----------------------------------------------------------------------- +C INPUT/OUPUT ARGUMENTS: +C----------------------------------------------------------------------- + +C pe: On input, pe (i) is the index in iw of the start of row i, or +C zero if row i has no off-diagonal non-zeros. +C +C During execution, it is used for both supervariables and +C elements: +C +C * Principal supervariable i: index into iw of the +C description of supervariable i. A supervariable +C represents one or more rows of the matrix +C with identical nonzero pattern. +C * Non-principal supervariable i: if i has been absorbed +C into another supervariable j, then pe (i) = -j. +C That is, j has the same pattern as i. +C Note that j might later be absorbed into another +C supervariable j2, in which case pe (i) is still -j, +C and pe (j) = -j2. +C * Unabsorbed element e: the index into iw of the description +C of element e, if e has not yet been absorbed by a +C subsequent element. Element e is created when +C the supervariable of the same name is selected as +C the pivot. +C * Absorbed element e: if element e is absorbed into element +C e2, then pe (e) = -e2. This occurs when the pattern of +C e (that is, Le) is found to be a subset of the pattern +C of e2 (that is, Le2). If element e is "null" (it has +C no nonzeros outside its pivot block), then pe (e) = 0. +C +C On output, pe holds the assembly tree/forest, which implicitly +C represents a pivot order with identical fill-in as the actual +C order (via a depth-first search of the tree). +C +C On output: +C If nv (i) .gt. 0, then i represents a node in the assembly tree, +C and the parent of i is -pe (i), or zero if i is a root. +C If nv (i) = 0, then (i,-pe (i)) represents an edge in a +C subtree, the root of which is a node in the assembly tree. + +C pfree: On input the tail end of the array, iw (pfree..iwlen), +C is empty, and the matrix is stored in iw (1..pfree-1). +C During execution, additional data is placed in iw, and pfree +C is modified so that iw (pfree..iwlen) is always the unused part +C of iw. On output, pfree is set equal to the size of iw that +C would have been needed for no compressions to occur. If +C ncmpa is zero, then pfree (on output) is less than or equal to +C iwlen, and the space iw (pfree+1 ... iwlen) was not used. +C Otherwise, pfree (on output) is greater than iwlen, and all the +C memory in iw was used. + +C----------------------------------------------------------------------- +C INPUT/MODIFIED (undefined on output): +C----------------------------------------------------------------------- + +C len: On input, len (i) holds the number of entries in row i of the +C matrix, excluding the diagonal. The contents of len (1..n) +C are undefined on output. + +C iw: On input, iw (1..pfree-1) holds the description of each row i +C in the matrix. The matrix must be symmetric, and both upper +C and lower triangular parts must be present. The diagonal must +C not be present. Row i is held as follows: +C +C len (i): the length of the row i data structure +C iw (pe (i) ... pe (i) + len (i) - 1): +C the list of column indices for nonzeros +C in row i (simple supervariables), excluding +C the diagonal. All supervariables start with +C one row/column each (supervariable i is just +C row i). +C if len (i) is zero on input, then pe (i) is ignored +C on input. +C +C Note that the rows need not be in any particular order, +C and there may be empty space between the rows. +C +C During execution, the supervariable i experiences fill-in. +C This is represented by placing in i a list of the elements +C that cause fill-in in supervariable i: +C +C len (i): the length of supervariable i +C iw (pe (i) ... pe (i) + elen (i) - 1): +C the list of elements that contain i. This list +C is kept short by removing absorbed elements. +C iw (pe (i) + elen (i) ... pe (i) + len (i) - 1): +C the list of supervariables in i. This list +C is kept short by removing nonprincipal +C variables, and any entry j that is also +C contained in at least one of the elements +C (j in Le) in the list for i (e in row i). +C +C When supervariable i is selected as pivot, we create an +C element e of the same name (e=i): +C +C len (e): the length of element e +C iw (pe (e) ... pe (e) + len (e) - 1): +C the list of supervariables in element e. +C +C An element represents the fill-in that occurs when supervariable +C i is selected as pivot (which represents the selection of row i +C and all non-principal variables whose principal variable is i). +C We use the term Le to denote the set of all supervariables +C in element e. Absorbed supervariables and elements are pruned +C from these lists when computationally convenient. +C +C CAUTION: THE INPUT MATRIX IS OVERWRITTEN DURING COMPUTATION. +C The contents of iw are undefined on output. + +C----------------------------------------------------------------------- +C OUTPUT (need not be set on input): +C----------------------------------------------------------------------- + +C nv: During execution, abs (nv (i)) is equal to the number of rows +C that are represented by the principal supervariable i. If i is +C a nonprincipal variable, then nv (i) = 0. Initially, +C nv (i) = 1 for all i. nv (i) .lt. 0 signifies that i is a +C principal variable in the pattern Lme of the current pivot +C element me. On output, nv (e) holds the true degree of element +C e at the time it was created (including the diagonal part). + +C ncmpa: The number of times iw was compressed. If this is +C excessive, then the execution took longer than what could have +C been. To reduce ncmpa, try increasing iwlen to be 10% or 20% +C larger than the value of pfree on input (or at least +C iwlen .ge. pfree + n). The fastest performance will be +C obtained when ncmpa is returned as zero. If iwlen is set to +C the value returned by pfree on *output*, then no compressions +C will occur. + +C elen: See the description of iw above. At the start of execution, +C elen (i) is set to zero. During execution, elen (i) is the +C number of elements in the list for supervariable i. When e +C becomes an element, elen (e) = -nel is set, where nel is the +C current step of factorization. elen (i) = 0 is done when i +C becomes nonprincipal. +C +C For variables, elen (i) .ge. 0 holds until just before the +C permutation vectors are computed. For elements, +C elen (e) .lt. 0 holds. +C +C On output elen (1..n) holds the inverse permutation (the same +C as the 'INVP' argument in Sparspak). That is, if k = elen (i), +C then row i is the kth pivot row. Row i of A appears as the +C (elen(i))-th row in the permuted matrix, PAP^T. + +C last: In a degree list, last (i) is the supervariable preceding i, +C or zero if i is the head of the list. In a hash bucket, +C last (i) is the hash key for i. last (head (hash)) is also +C used as the head of a hash bucket if head (hash) contains a +C degree list (see head, below). +C +C On output, last (1..n) holds the permutation (the same as the +C 'PERM' argument in Sparspak). That is, if i = last (k), then +C row i is the kth pivot row. Row last (k) of A is the k-th row +C in the permuted matrix, PAP^T. + +C----------------------------------------------------------------------- +C LOCAL (not input or output - used only during execution): +C----------------------------------------------------------------------- + +C degree: If i is a supervariable, then degree (i) holds the +C current approximation of the external degree of row i (an upper +C bound). The external degree is the number of nonzeros in row i, +C minus abs (nv (i)) (the diagonal part). The bound is equal to +C the external degree if elen (i) is less than or equal to two. +C +C We also use the term "external degree" for elements e to refer +C to |Le \ Lme|. If e is an element, then degree (e) holds |Le|, +C which is the degree of the off-diagonal part of the element e +C (not including the diagonal part). + +C head: head is used for degree lists. head (deg) is the first +C supervariable in a degree list (all supervariables i in a +C degree list deg have the same approximate degree, namely, +C deg = degree (i)). If the list deg is empty then +C head (deg) = 0. +C +C During supervariable detection head (hash) also serves as a +C pointer to a hash bucket. +C If head (hash) .gt. 0, there is a degree list of degree hash. +C The hash bucket head pointer is last (head (hash)). +C If head (hash) = 0, then the degree list and hash bucket are +C both empty. +C If head (hash) .lt. 0, then the degree list is empty, and +C -head (hash) is the head of the hash bucket. +C After supervariable detection is complete, all hash buckets +C are empty, and the (last (head (hash)) = 0) condition is +C restored for the non-empty degree lists. + +C next: next (i) is the supervariable following i in a link list, or +C zero if i is the last in the list. Used for two kinds of +C lists: degree lists and hash buckets (a supervariable can be +C in only one kind of list at a time). + +C w: The flag array w determines the status of elements and +C variables, and the external degree of elements. +C +C for elements: +C if w (e) = 0, then the element e is absorbed +C if w (e) .ge. wflg, then w (e) - wflg is the size of +C the set |Le \ Lme|, in terms of nonzeros (the +C sum of abs (nv (i)) for each principal variable i that +C is both in the pattern of element e and NOT in the +C pattern of the current pivot element, me). +C if wflg .gt. w (e) .gt. 0, then e is not absorbed and has +C not yet been seen in the scan of the element lists in +C the computation of |Le\Lme| in loop 150 below. +C +C for variables: +C during supervariable detection, if w (j) .ne. wflg then j is +C not in the pattern of variable i +C +C The w array is initialized by setting w (i) = 1 for all i, +C and by setting wflg = 2. It is reinitialized if wflg becomes +C too large (to ensure that wflg+n does not cause integer +C overflow). + +C----------------------------------------------------------------------- +C LOCAL INTEGERS: +C----------------------------------------------------------------------- + + INTEGER DEG, DEGME, DMAX, E, ELENME, ELN, HASH, HMOD, I, + $ ILAST, INEXT, J, JLAST, JNEXT, K, KNT1, KNT2, KNT3, + $ LENJ, LN, MAXMEM, ME, MEM, MINDEG, NEL, NEWMEM, + $ NLEFT, NVI, NVJ, NVPIV, SLENME, WE, WFLG, WNVI, X + +C deg: the degree of a variable or element +C degme: size, |Lme|, of the current element, me (= degree (me)) +C dext: external degree, |Le \ Lme|, of some element e +C dmax: largest |Le| seen so far +C e: an element +C elenme: the length, elen (me), of element list of pivotal var. +C eln: the length, elen (...), of an element list +C hash: the computed value of the hash function +C hmod: the hash function is computed modulo hmod = max (1,n-1) +C i: a supervariable +C ilast: the entry in a link list preceding i +C inext: the entry in a link list following i +C j: a supervariable +C jlast: the entry in a link list preceding j +C jnext: the entry in a link list, or path, following j +C k: the pivot order of an element or variable +C knt1: loop counter used during element construction +C knt2: loop counter used during element construction +C knt3: loop counter used during compression +C lenj: len (j) +C ln: length of a supervariable list +C maxmem: amount of memory needed for no compressions +C me: current supervariable being eliminated, and the +C current element created by eliminating that +C supervariable +C mem: memory in use assuming no compressions have occurred +C mindeg: current minimum degree +C nel: number of pivots selected so far +C newmem: amount of new memory needed for current pivot element +C nleft: n - nel, the number of nonpivotal rows/columns remaining +C nvi: the number of variables in a supervariable i (= nv (i)) +C nvj: the number of variables in a supervariable j (= nv (j)) +C nvpiv: number of pivots in current element +C slenme: number of variables in variable list of pivotal variable +C we: w (e) +C wflg: used for flagging the w array. See description of iw. +C wnvi: wflg - nv (i) +C x: either a supervariable or an element + +C----------------------------------------------------------------------- +C LOCAL POINTERS: +C----------------------------------------------------------------------- + + INTEGER P, P1, P2, P3, PDST, PEND, PJ, PME, PME1, PME2, PN, PSRC + +C Any parameter (pe (...) or pfree) or local variable +C starting with "p" (for Pointer) is an index into iw, +C and all indices into iw use variables starting with +C "p." The only exception to this rule is the iwlen +C input argument. + +C p: pointer into lots of things +C p1: pe (i) for some variable i (start of element list) +C p2: pe (i) + elen (i) - 1 for some var. i (end of el. list) +C p3: index of first supervariable in clean list +C pdst: destination pointer, for compression +C pend: end of memory to compress +C pj: pointer into an element or variable +C pme: pointer into the current element (pme1...pme2) +C pme1: the current element, me, is stored in iw (pme1...pme2) +C pme2: the end of the current element +C pn: pointer into a "clean" variable, also used to compress +C psrc: source pointer, for compression + +C----------------------------------------------------------------------- +C FUNCTIONS CALLED: +C----------------------------------------------------------------------- + + INTRINSIC MAX, MIN, MOD + +C======================================================================= +C INITIALIZATIONS +C======================================================================= + + WFLG = 2 + MINDEG = 1 + NCMPA = 0 + NEL = 0 + HMOD = MAX (1, N-1) + DMAX = 0 + MEM = PFREE - 1 + MAXMEM = MEM + ME = 0 + + DO 10 I = 1, N + LAST (I) = 0 + HEAD (I) = 0 + NV (I) = 1 + W (I) = 1 + ELEN (I) = 0 + DEGREE (I) = LEN (I) +10 CONTINUE + +C ---------------------------------------------------------------- +C initialize degree lists and eliminate rows with no off-diag. nz. +C ---------------------------------------------------------------- + + DO 20 I = 1, N + + DEG = DEGREE (I) + + IF (DEG .GT. 0) THEN + +C ---------------------------------------------------------- +C place i in the degree list corresponding to its degree +C ---------------------------------------------------------- + + INEXT = HEAD (DEG) + IF (INEXT .NE. 0) LAST (INEXT) = I + NEXT (I) = INEXT + HEAD (DEG) = I + + ELSE + +C ---------------------------------------------------------- +C we have a variable that can be eliminated at once because +C there is no off-diagonal non-zero in its row. +C ---------------------------------------------------------- + + NEL = NEL + 1 + ELEN (I) = -NEL + PE (I) = 0 + W (I) = 0 + + ENDIF + +20 CONTINUE + +C======================================================================= +C WHILE (selecting pivots) DO +C======================================================================= + +30 CONTINUE + IF (NEL .LT. N) THEN + +C======================================================================= +C GET PIVOT OF MINIMUM DEGREE +C======================================================================= + +C ------------------------------------------------------------- +C find next supervariable for elimination +C ------------------------------------------------------------- + + DO 40 DEG = MINDEG, N + ME = HEAD (DEG) + IF (ME .GT. 0) GOTO 50 +40 CONTINUE +50 CONTINUE + MINDEG = DEG + +C ------------------------------------------------------------- +C remove chosen variable from link list +C ------------------------------------------------------------- + + INEXT = NEXT (ME) + IF (INEXT .NE. 0) LAST (INEXT) = 0 + HEAD (DEG) = INEXT + +C ------------------------------------------------------------- +C me represents the elimination of pivots nel+1 to nel+nv(me). +C place me itself as the first in this set. It will be moved +C to the nel+nv(me) position when the permutation vectors are +C computed. +C ------------------------------------------------------------- + + ELENME = ELEN (ME) + ELEN (ME) = - (NEL + 1) + NVPIV = NV (ME) + NEL = NEL + NVPIV + +C======================================================================= +C CONSTRUCT NEW ELEMENT +C======================================================================= + +C ------------------------------------------------------------- +C At this point, me is the pivotal supervariable. It will be +C converted into the current element. Scan list of the +C pivotal supervariable, me, setting tree pointers and +C constructing new list of supervariables for the new element, +C me. p is a pointer to the current position in the old list. +C ------------------------------------------------------------- + +C flag the variable "me" as being in Lme by negating nv (me) + NV (ME) = -NVPIV + DEGME = 0 + + IF (ELENME .EQ. 0) THEN + +C ---------------------------------------------------------- +C construct the new element in place +C ---------------------------------------------------------- + + PME1 = PE (ME) + PME2 = PME1 - 1 + + DO 60 P = PME1, PME1 + LEN (ME) - 1 + I = IW (P) + NVI = NV (I) + IF (NVI .GT. 0) THEN + +C ---------------------------------------------------- +C i is a principal variable not yet placed in Lme. +C store i in new list +C ---------------------------------------------------- + + DEGME = DEGME + NVI +C flag i as being in Lme by negating nv (i) + NV (I) = -NVI + PME2 = PME2 + 1 + IW (PME2) = I + +C ---------------------------------------------------- +C remove variable i from degree list. +C ---------------------------------------------------- + + ILAST = LAST (I) + INEXT = NEXT (I) + IF (INEXT .NE. 0) LAST (INEXT) = ILAST + IF (ILAST .NE. 0) THEN + NEXT (ILAST) = INEXT + ELSE +C i is at the head of the degree list + HEAD (DEGREE (I)) = INEXT + ENDIF + + ENDIF +60 CONTINUE +C this element takes no new memory in iw: + NEWMEM = 0 + + ELSE + +C ---------------------------------------------------------- +C construct the new element in empty space, iw (pfree ...) +C ---------------------------------------------------------- + + P = PE (ME) + PME1 = PFREE + SLENME = LEN (ME) - ELENME + + DO 120 KNT1 = 1, ELENME + 1 + + IF (KNT1 .GT. ELENME) THEN +C search the supervariables in me. + E = ME + PJ = P + LN = SLENME + ELSE +C search the elements in me. + E = IW (P) + P = P + 1 + PJ = PE (E) + LN = LEN (E) + ENDIF + +C ------------------------------------------------------- +C search for different supervariables and add them to the +C new list, compressing when necessary. this loop is +C executed once for each element in the list and once for +C all the supervariables in the list. +C ------------------------------------------------------- + + DO 110 KNT2 = 1, LN + I = IW (PJ) + PJ = PJ + 1 + NVI = NV (I) + IF (NVI .GT. 0) THEN + +C ------------------------------------------------- +C compress iw, if necessary +C ------------------------------------------------- + + IF (PFREE .GT. IWLEN) THEN +C prepare for compressing iw by adjusting +C pointers and lengths so that the lists being +C searched in the inner and outer loops contain +C only the remaining entries. + + PE (ME) = P + LEN (ME) = LEN (ME) - KNT1 + IF (LEN (ME) .EQ. 0) THEN +C nothing left of supervariable me + PE (ME) = 0 + ENDIF + PE (E) = PJ + LEN (E) = LN - KNT2 + IF (LEN (E) .EQ. 0) THEN +C nothing left of element e + PE (E) = 0 + ENDIF + + NCMPA = NCMPA + 1 +C store first item in pe +C set first entry to -item + DO 70 J = 1, N + PN = PE (J) + IF (PN .GT. 0) THEN + PE (J) = IW (PN) + IW (PN) = -J + ENDIF +70 CONTINUE + +C psrc/pdst point to source/destination + PDST = 1 + PSRC = 1 + PEND = PME1 - 1 + +C while loop: +80 CONTINUE + IF (PSRC .LE. PEND) THEN +C search for next negative entry + J = -IW (PSRC) + PSRC = PSRC + 1 + IF (J .GT. 0) THEN + IW (PDST) = PE (J) + PE (J) = PDST + PDST = PDST + 1 +C copy from source to destination + LENJ = LEN (J) + DO 90 KNT3 = 0, LENJ - 2 + IW (PDST + KNT3) = IW (PSRC + KNT3) +90 CONTINUE + PDST = PDST + LENJ - 1 + PSRC = PSRC + LENJ - 1 + ENDIF + GOTO 80 + ENDIF + +C move the new partially-constructed element + P1 = PDST + DO 100 PSRC = PME1, PFREE - 1 + IW (PDST) = IW (PSRC) + PDST = PDST + 1 +100 CONTINUE + PME1 = P1 + PFREE = PDST + PJ = PE (E) + P = PE (ME) + ENDIF + +C ------------------------------------------------- +C i is a principal variable not yet placed in Lme +C store i in new list +C ------------------------------------------------- + + DEGME = DEGME + NVI +C flag i as being in Lme by negating nv (i) + NV (I) = -NVI + IW (PFREE) = I + PFREE = PFREE + 1 + +C ------------------------------------------------- +C remove variable i from degree link list +C ------------------------------------------------- + + ILAST = LAST (I) + INEXT = NEXT (I) + IF (INEXT .NE. 0) LAST (INEXT) = ILAST + IF (ILAST .NE. 0) THEN + NEXT (ILAST) = INEXT + ELSE +C i is at the head of the degree list + HEAD (DEGREE (I)) = INEXT + ENDIF + + ENDIF +110 CONTINUE + + IF (E .NE. ME) THEN +C set tree pointer and flag to indicate element e is +C absorbed into new element me (the parent of e is me) + PE (E) = -ME + W (E) = 0 + ENDIF +120 CONTINUE + + PME2 = PFREE - 1 +C this element takes newmem new memory in iw (possibly zero) + NEWMEM = PFREE - PME1 + MEM = MEM + NEWMEM + MAXMEM = MAX (MAXMEM, MEM) + ENDIF + +C ------------------------------------------------------------- +C me has now been converted into an element in iw (pme1..pme2) +C ------------------------------------------------------------- + +C degme holds the external degree of new element + DEGREE (ME) = DEGME + PE (ME) = PME1 + LEN (ME) = PME2 - PME1 + 1 + +C ------------------------------------------------------------- +C make sure that wflg is not too large. With the current +C value of wflg, wflg+n must not cause integer overflow +C ------------------------------------------------------------- + + IF (WFLG + N .LE. WFLG) THEN + DO 130 X = 1, N + IF (W (X) .NE. 0) W (X) = 1 +130 CONTINUE + WFLG = 2 + ENDIF + +C======================================================================= +C COMPUTE (w (e) - wflg) = |Le\Lme| FOR ALL ELEMENTS +C======================================================================= + +C ------------------------------------------------------------- +C Scan 1: compute the external degrees of previous elements +C with respect to the current element. That is: +C (w (e) - wflg) = |Le \ Lme| +C for each element e that appears in any supervariable in Lme. +C The notation Le refers to the pattern (list of +C supervariables) of a previous element e, where e is not yet +C absorbed, stored in iw (pe (e) + 1 ... pe (e) + iw (pe (e))). +C The notation Lme refers to the pattern of the current element +C (stored in iw (pme1..pme2)). If (w (e) - wflg) becomes +C zero, then the element e will be absorbed in scan 2. +C ------------------------------------------------------------- + + DO 150 PME = PME1, PME2 + I = IW (PME) + ELN = ELEN (I) + IF (ELN .GT. 0) THEN +C note that nv (i) has been negated to denote i in Lme: + NVI = -NV (I) + WNVI = WFLG - NVI + DO 140 P = PE (I), PE (I) + ELN - 1 + E = IW (P) + WE = W (E) + IF (WE .GE. WFLG) THEN +C unabsorbed element e has been seen in this loop + WE = WE - NVI + ELSE IF (WE .NE. 0) THEN +C e is an unabsorbed element +C this is the first we have seen e in all of Scan 1 + WE = DEGREE (E) + WNVI + ENDIF + W (E) = WE +140 CONTINUE + ENDIF +150 CONTINUE + +C======================================================================= +C DEGREE UPDATE AND ELEMENT ABSORPTION +C======================================================================= + +C ------------------------------------------------------------- +C Scan 2: for each i in Lme, sum up the degree of Lme (which +C is degme), plus the sum of the external degrees of each Le +C for the elements e appearing within i, plus the +C supervariables in i. Place i in hash list. +C ------------------------------------------------------------- + + DO 180 PME = PME1, PME2 + I = IW (PME) + P1 = PE (I) + P2 = P1 + ELEN (I) - 1 + PN = P1 + HASH = 0 + DEG = 0 + +C ---------------------------------------------------------- +C scan the element list associated with supervariable i +C ---------------------------------------------------------- + +C UMFPACK/MA38-style approximate degree: + DO 160 P = P1, P2 + E = IW (P) + WE = W (E) + IF (WE .NE. 0) THEN +C e is an unabsorbed element + DEG = DEG + WE - WFLG + IW (PN) = E + PN = PN + 1 + HASH = HASH + E + ENDIF +160 CONTINUE + +C count the number of elements in i (including me): + ELEN (I) = PN - P1 + 1 + +C ---------------------------------------------------------- +C scan the supervariables in the list associated with i +C ---------------------------------------------------------- + + P3 = PN + DO 170 P = P2 + 1, P1 + LEN (I) - 1 + J = IW (P) + NVJ = NV (J) + IF (NVJ .GT. 0) THEN +C j is unabsorbed, and not in Lme. +C add to degree and add to new list + DEG = DEG + NVJ + IW (PN) = J + PN = PN + 1 + HASH = HASH + J + ENDIF +170 CONTINUE + +C ---------------------------------------------------------- +C update the degree and check for mass elimination +C ---------------------------------------------------------- + + IF (ELEN (I) .EQ. 1 .AND. P3 .EQ. PN) THEN + +C ------------------------------------------------------- +C mass elimination +C ------------------------------------------------------- + +C There is nothing left of this node except for an +C edge to the current pivot element. elen (i) is 1, +C and there are no variables adjacent to node i. +C Absorb i into the current pivot element, me. + + PE (I) = -ME + NVI = -NV (I) + DEGME = DEGME - NVI + NVPIV = NVPIV + NVI + NEL = NEL + NVI + NV (I) = 0 + ELEN (I) = 0 + + ELSE + +C ------------------------------------------------------- +C update the upper-bound degree of i +C ------------------------------------------------------- + +C the following degree does not yet include the size +C of the current element, which is added later: + DEGREE (I) = MIN (DEGREE (I), DEG) + +C ------------------------------------------------------- +C add me to the list for i +C ------------------------------------------------------- + +C move first supervariable to end of list + IW (PN) = IW (P3) +C move first element to end of element part of list + IW (P3) = IW (P1) +C add new element to front of list. + IW (P1) = ME +C store the new length of the list in len (i) + LEN (I) = PN - P1 + 1 + +C ------------------------------------------------------- +C place in hash bucket. Save hash key of i in last (i). +C ------------------------------------------------------- + + HASH = MOD (HASH, HMOD) + 1 + J = HEAD (HASH) + IF (J .LE. 0) THEN +C the degree list is empty, hash head is -j + NEXT (I) = -J + HEAD (HASH) = -I + ELSE +C degree list is not empty +C use last (head (hash)) as hash head + NEXT (I) = LAST (J) + LAST (J) = I + ENDIF + LAST (I) = HASH + ENDIF +180 CONTINUE + + DEGREE (ME) = DEGME + +C ------------------------------------------------------------- +C Clear the counter array, w (...), by incrementing wflg. +C ------------------------------------------------------------- + + DMAX = MAX (DMAX, DEGME) + WFLG = WFLG + DMAX + +C make sure that wflg+n does not cause integer overflow + IF (WFLG + N .LE. WFLG) THEN + DO 190 X = 1, N + IF (W (X) .NE. 0) W (X) = 1 +190 CONTINUE + WFLG = 2 + ENDIF +C at this point, w (1..n) .lt. wflg holds + +C======================================================================= +C SUPERVARIABLE DETECTION +C======================================================================= + + DO 250 PME = PME1, PME2 + I = IW (PME) + IF (NV (I) .LT. 0) THEN +C i is a principal variable in Lme + +C ------------------------------------------------------- +C examine all hash buckets with 2 or more variables. We +C do this by examing all unique hash keys for super- +C variables in the pattern Lme of the current element, me +C ------------------------------------------------------- + + HASH = LAST (I) +C let i = head of hash bucket, and empty the hash bucket + J = HEAD (HASH) + IF (J .EQ. 0) GOTO 250 + IF (J .LT. 0) THEN +C degree list is empty + I = -J + HEAD (HASH) = 0 + ELSE +C degree list is not empty, restore last () of head + I = LAST (J) + LAST (J) = 0 + ENDIF + IF (I .EQ. 0) GOTO 250 + +C while loop: +200 CONTINUE + IF (NEXT (I) .NE. 0) THEN + +C ---------------------------------------------------- +C this bucket has one or more variables following i. +C scan all of them to see if i can absorb any entries +C that follow i in hash bucket. Scatter i into w. +C ---------------------------------------------------- + + LN = LEN (I) + ELN = ELEN (I) +C do not flag the first element in the list (me) + DO 210 P = PE (I) + 1, PE (I) + LN - 1 + W (IW (P)) = WFLG +210 CONTINUE + +C ---------------------------------------------------- +C scan every other entry j following i in bucket +C ---------------------------------------------------- + + JLAST = I + J = NEXT (I) + +C while loop: +220 CONTINUE + IF (J .NE. 0) THEN + +C ------------------------------------------------- +C check if j and i have identical nonzero pattern +C ------------------------------------------------- + + IF (LEN (J) .NE. LN) THEN +C i and j do not have same size data structure + GOTO 240 + ENDIF + IF (ELEN (J) .NE. ELN) THEN +C i and j do not have same number of adjacent el + GOTO 240 + ENDIF +C do not flag the first element in the list (me) + DO 230 P = PE (J) + 1, PE (J) + LN - 1 + IF (W (IW (P)) .NE. WFLG) THEN +C an entry (iw(p)) is in j but not in i + GOTO 240 + ENDIF +230 CONTINUE + +C ------------------------------------------------- +C found it! j can be absorbed into i +C ------------------------------------------------- + + PE (J) = -I +C both nv (i) and nv (j) are negated since they +C are in Lme, and the absolute values of each +C are the number of variables in i and j: + NV (I) = NV (I) + NV (J) + NV (J) = 0 + ELEN (J) = 0 +C delete j from hash bucket + J = NEXT (J) + NEXT (JLAST) = J + GOTO 220 + +C ------------------------------------------------- +240 CONTINUE +C j cannot be absorbed into i +C ------------------------------------------------- + + JLAST = J + J = NEXT (J) + GOTO 220 + ENDIF + +C ---------------------------------------------------- +C no more variables can be absorbed into i +C go to next i in bucket and clear flag array +C ---------------------------------------------------- + + WFLG = WFLG + 1 + I = NEXT (I) + IF (I .NE. 0) GOTO 200 + ENDIF + ENDIF +250 CONTINUE + +C======================================================================= +C RESTORE DEGREE LISTS AND REMOVE NONPRINCIPAL SUPERVAR. FROM ELEMENT +C======================================================================= + + P = PME1 + NLEFT = N - NEL + DO 260 PME = PME1, PME2 + I = IW (PME) + NVI = -NV (I) + IF (NVI .GT. 0) THEN +C i is a principal variable in Lme +C restore nv (i) to signify that i is principal + NV (I) = NVI + +C ------------------------------------------------------- +C compute the external degree (add size of current elem) +C ------------------------------------------------------- + + DEG = MAX (1, MIN (DEGREE (I) + DEGME-NVI, NLEFT-NVI)) + +C ------------------------------------------------------- +C place the supervariable at the head of the degree list +C ------------------------------------------------------- + + INEXT = HEAD (DEG) + IF (INEXT .NE. 0) LAST (INEXT) = I + NEXT (I) = INEXT + LAST (I) = 0 + HEAD (DEG) = I + +C ------------------------------------------------------- +C save the new degree, and find the minimum degree +C ------------------------------------------------------- + + MINDEG = MIN (MINDEG, DEG) + DEGREE (I) = DEG + +C ------------------------------------------------------- +C place the supervariable in the element pattern +C ------------------------------------------------------- + + IW (P) = I + P = P + 1 + ENDIF +260 CONTINUE + +C======================================================================= +C FINALIZE THE NEW ELEMENT +C======================================================================= + + NV (ME) = NVPIV + DEGME +C nv (me) is now the degree of pivot (including diagonal part) +C save the length of the list for the new element me + LEN (ME) = P - PME1 + IF (LEN (ME) .EQ. 0) THEN +C there is nothing left of the current pivot element + PE (ME) = 0 + W (ME) = 0 + ENDIF + IF (NEWMEM .NE. 0) THEN +C element was not constructed in place: deallocate part +C of it (final size is less than or equal to newmem, +C since newly nonprincipal variables have been removed). + PFREE = P + MEM = MEM - NEWMEM + LEN (ME) + ENDIF + +C======================================================================= +C END WHILE (selecting pivots) + GOTO 30 + ENDIF +C======================================================================= + +C======================================================================= +C COMPUTE THE PERMUTATION VECTORS +C======================================================================= + +C ---------------------------------------------------------------- +C The time taken by the following code is O(n). At this +C point, elen (e) = -k has been done for all elements e, +C and elen (i) = 0 has been done for all nonprincipal +C variables i. At this point, there are no principal +C supervariables left, and all elements are absorbed. +C ---------------------------------------------------------------- + +C ---------------------------------------------------------------- +C compute the ordering of unordered nonprincipal variables +C ---------------------------------------------------------------- + + DO 290 I = 1, N + IF (ELEN (I) .EQ. 0) THEN + +C ---------------------------------------------------------- +C i is an un-ordered row. Traverse the tree from i until +C reaching an element, e. The element, e, was the +C principal supervariable of i and all nodes in the path +C from i to when e was selected as pivot. +C ---------------------------------------------------------- + + J = -PE (I) +C while (j is a variable) do: +270 CONTINUE + IF (ELEN (J) .GE. 0) THEN + J = -PE (J) + GOTO 270 + ENDIF + E = J + +C ---------------------------------------------------------- +C get the current pivot ordering of e +C ---------------------------------------------------------- + + K = -ELEN (E) + +C ---------------------------------------------------------- +C traverse the path again from i to e, and compress the +C path (all nodes point to e). Path compression allows +C this code to compute in O(n) time. Order the unordered +C nodes in the path, and place the element e at the end. +C ---------------------------------------------------------- + + J = I +C while (j is a variable) do: +280 CONTINUE + IF (ELEN (J) .GE. 0) THEN + JNEXT = -PE (J) + PE (J) = -E + IF (ELEN (J) .EQ. 0) THEN +C j is an unordered row + ELEN (J) = K + K = K + 1 + ENDIF + J = JNEXT + GOTO 280 + ENDIF +C leave elen (e) negative, so we know it is an element + ELEN (E) = -K + ENDIF +290 CONTINUE + +C ---------------------------------------------------------------- +C reset the inverse permutation (elen (1..n)) to be positive, +C and compute the permutation (last (1..n)). +C ---------------------------------------------------------------- + + DO 300 I = 1, N + K = ABS (ELEN (I)) + LAST (K) = I + ELEN (I) = K +300 CONTINUE + +C======================================================================= +C RETURN THE MEMORY USAGE IN IW +C======================================================================= + +C If maxmem is less than or equal to iwlen, then no compressions +C occurred, and iw (maxmem+1 ... iwlen) was unused. Otherwise +C compressions did occur, and iwlen would have had to have been +C greater than or equal to maxmem for no compressions to occur. +C Return the value of maxmem in the pfree argument. + + PFREE = MAXMEM + + RETURN + END + diff --git a/deal.II/contrib/umfpack/Makefile b/deal.II/contrib/umfpack/Makefile new file mode 100644 index 0000000000..1da96a2a71 --- /dev/null +++ b/deal.II/contrib/umfpack/Makefile @@ -0,0 +1,32 @@ +include ../../common/Make.global_options + +# rules +ifeq ($(enable-shared),yes) + lib: $(LIBDIR)/liblac_umfpack.so +else + lib: $(LIBDIR)/liblac_umfpack.a +endif + + +AMD: + cd AMD/Source ; $(MAKE) all + +UMFPACK: + cd UMFPACK/Source ; $(MAKE) all + + +$(LIBDIR)/liblac_umfpack.so: AMD UMFPACK + @echo "=====umfpack==========optimized==$(MT)== Linking library: $(@F)" + @$(SHLIBLD) $(LDFLAGS) -shared -o $@ $D/lib/umfpack/*.o + + +$(LIBDIR)/liblac_umfpack.a: AMD UMFPACK + @echo "=====umfpack==========optimized==$(MT)== Linking library: $(@F)" + @$(AR) ru $@ $D/lib/umfpack/*.o + @$(RANLIB) $@ + + +clean: + + +.PHONY: AMD UMFPACK clean lib diff --git a/deal.II/contrib/umfpack/README b/deal.II/contrib/umfpack/README new file mode 100644 index 0000000000..adf016bb70 --- /dev/null +++ b/deal.II/contrib/umfpack/README @@ -0,0 +1,5 @@ +UMFPACK v4.3 and AMD v1.1. + +See UMFPACK/README and AMD/README for details. Documention is in +UMFPACK/Doc and AMD/Doc. + diff --git a/deal.II/contrib/umfpack/README.deal.II b/deal.II/contrib/umfpack/README.deal.II new file mode 100644 index 0000000000..56c3c4133e --- /dev/null +++ b/deal.II/contrib/umfpack/README.deal.II @@ -0,0 +1,14 @@ +This directory is an almost identical copy of the UMFPACK 4.3 +release. The only things I have changed are the Makefiles: they build +shared libraries instead of static ones if deal.II is so configured, +and they put the object files somewhere else. This also allowed us to +get rid of the AMD/Make and UMFPACK/Make subdirectories, since they +did by hand what we do more elegantly using autoconf and friends. +Finally, I removed the UMFPACK/Demo directory, since it is rather +big. I assume that people using UMFPACK from inside deal.II are not +interested in this, but they can of course download the full UMFPACK +distribution if they want these files. + +UMFPACK is copyrighted by others than the deal.II authors, but +included by permission. See the AMD/Doc/License and +UMFPACK/Doc/License files for licensing information. \ No newline at end of file diff --git a/deal.II/contrib/umfpack/UMFPACK/Doc/ChangeLog b/deal.II/contrib/umfpack/UMFPACK/Doc/ChangeLog new file mode 100644 index 0000000000..165d5efa72 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Doc/ChangeLog @@ -0,0 +1,334 @@ +Jan. 16, 2004: v4.3 released. + + * user interface of v4.3 is upwardly-compatible with v4.2 and v4.1. + No bugs found in v4.1 (except for one workaround for an old compiler). + These changes add features only. + + * Note that v4.0 has a bug in umf_scale_column.c. The bug was patched + in that version on Jan. 12, 2004. The bug does not appear in v4.1 + and later. The bug is thus present in MATLAB 6.5, but it occurs + very rarely, fortunately. It can occur when dividing a nonzero entry + in the pivot column by the pivot value results in an underflow. + + * added to umfpackmex.c, for DBL_EPSILON. Some non-standard + compilers (Microsoft Visual C++) require this. + + * #pragma added to umf_analyze.c, as a workaround around a bug in an + old Intel compiler. + + * mexFunction interface to MATLAB modified. Call to mexCallMATLAB removed, + which can be slow. In V4.1 it was used only to get MATLAB's + spparms ('spumoni') value. + + * The AMD mexFunction was also modified in the same way (v1.1), with + the call to mexCallMATLAB removed. Note that UMFPACK v4.1 through + v4.3 can use either AMD v1.0 or AMD v1.1. + + * -DNO_DIVIDE_BY_ZERO option added. If this non-default option is enabled + at compile time, and if the pivot value is zero, then no division + occurs (zeros on the diagonal of U are treated as if they were equal + to one). By default, the division by zero does occur. + + * -DNO_TIMER option added. If this non-default option is enabled at + compile time, then no timers (times ( ), clock ( ), getrusage ( )) + are used. + +V4.2: A special release for COMSOL, Inc., only (FEMLAB) + + * drop tolerance added. A few new parameters in the Control array are used, + and a few new Info entries. + +May 6, 2003: V4.1 released. + + * No bugs were found in the prior version, Version 4.0. New features + added only. Major changes throughout the code. User interface + nearly unchanged, however. + + * Version 4.1 is upward-compatible with Version 4.0. The calling + sequence of some user-callable routines in Version 4.0 have changed + in this version. The routines umfpack_*_symbolic, umfpack_*_qsymbolic, + umfpack_*_get_symbolic, and umfpack_*_get_numeric have new arguments + added to them. The new arguments are optional. If you want to use + a calling sequence similar to v4.0, simply pass NULL pointers in + place of the new arguments. There are two new timing routines, + umfpack_tic and umfpack_toc. A new user-callable routine, + umfpack_*_scale, has been added. + + * "auto", "unsymmetric", "symmetric", and "2-by-2" strategies added. + The symmetric strategy uses AMD on A+A' as the column preordering, + followed by a postorder of the assembly tree of A+A'. Column ordering + refinement is turned off, and diagonal entries are prefered as pivots. + V4.0 only had the unsymmetric strategy. The 2-by-2 strategy does row + permutations and attempts to find a zero-free diagonal while at the + same time maintaining structural symmetry, and then uses the + symmetric strategy on the permuted matrix. + + * row-scaling added. The default is to divide each row by the sum of + the absolute values of each row. Other options are no scaling, + and to divide each row by the max abs value in each row. + + * Matrices with upper bound memory usage greater than the maximum integer + (2GB for 32-bit int's) can now be factorized (assuming the actual + memory usage is still less than the maximum integer). With this change, + the UMFPACK_ERROR_problem_too_large error code is no longer returned. + + * The current frontal matrix (Work->Fx) is no longer allocated as a + static size, via malloc. It can grow and shrink, and is allocated + from Numeric->Memory. + + * The AMD (Version 1.0) package is now required. It is available + separately. To compile UMFPACK, it must appear as ../AMD if you are + in the main UMFPACK directory. + + * The UMFPACK mexFunction now uses the internal utMalloc, utRealloc, + and utFree routines, by default (except on Windows). + + * Three control parameters for modifying relaxed amalgamation removed. + These values are now fixed at compile-time. + + * Many new statistics added to Info, and new control parameters added. + + * The umfpack mexFunction now returns permutation matrices for P and Q, + not permutation vectors. It also returns the scale factors as a + diagonal matrix. The factorization is now L*U = P*(R\A)*Q. + + * Option added for controlling the initial allocation of the workspace for + the current frontal matrix. + + * pivot tolerance of zero treated differently. symmetric pivot tolerance + added. + + * Makefile and GNUmakefile changed. umf_* routines with no double or + complex values are now compiled just twice (int and long versions) + rather than 4 times. + + * New routines added to save and load the Numeric and Symbolic objects + to/from binary files. + + * Simple Fortran interface added. + +Apr 11, 2002: + + * Version 4.0 released. + + * bug fix: the Microsoft compiler doesn't handle NaN's properly. + utIsNaN, and other ut* routines, added for MathWorks version + to handle this properly. + +Apr 1, 2002: + + * bug fix: if a column was all NaN's, then UMFPACK would fail + to find a pivot row. umf_row_search.c and umf_internal.h + modified to fix this problem. + +Mar 9, 2002: V4.0beta released + + * Map argument added to umfpack_*_triplet_to_col. New files + (umf_triplet.[ch]) added. + * minor changes made so that UMFPACK can be compiled with g++ + * additional error checking added to umfpack_*_numeric, for + detecting more changes in pattern (Ap, Ai) since last + call to umfpack_*_symbolic + +Feb 21, 2002: + + * User Guide explains the Makefile vs. GNUmakefile + + * umf_config.h modified, so that the complex SCSL C-BLAS uses + (void *) arguments instead of (scsl_zomplex *). gcc generates + some spurious warnings (cc doesn't complain). Affects the SGI + IRIX only. + + * ported to Compaq Alpha + +Feb 20, 2002: V4.0 (alpha) released. + + * V4.0 not yet ported to the Compaq Alpha (V3.2 was ported). + +Feb 6 to Feb 19, 2002: + + * Relaxed restrictions on sizes of arrays for umfpack_*_transpose and + umfpack_*_triplet_to_col. Size of "max(n,nz)" now just size nz. + + * workspace for umfpack_*_wsolve increased in size. + + * two user arrays for umfpack_*_get_symbolic increased in size, + by 1 (Chain_maxrows, Chain_maxcols). + + * lu_normest.m added. + +Jan 18 to Feb 5, 2002: + + * The matrix A can be complex, singular, and/or rectangular. + The solve step that uses the LU factors can only handle + matrices that are complex or real, singuluar or non-singular, + and *** square ***, however. + + * Estimate of the condition number computed: + (min (abs (diag (U))) / (max (abs (diag (U))))) + + * Forward/backsolves can solve with A.' as well as A'. + + * char * arguments removed from user-callable routines to make it + easier for Fortran to call UMFPACK. No Fortran interface is (yet) + provided, however. + + The solve codes for umfpack_*_*solve changed to #define'd + integers: + + UMFPACK_A Ax=b + UMFPACK_At A'x=b + UMFPACK_Aat A.'x=b + UMFPACK_Pt_L P'Lx=b + UMFPACK_L Lx=b + UMFPACK_Lt_P L'Px=b + UMFPACK_Lat_P L.'Px=b + UMFPACK_Lt L'x=b + UMFPACK_U_Qt UQ'x=b + UMFPACK_U Ux=b + UMFPACK_Q_Ut QU'x=b + UMFPACK_Q_Uat QU.'x=b + UMFPACK_Ut U'x=b + UMFPACK_Uat U.'x=b + + All arguments are now either int, long scalars (pass by value), + or int, long, double arrays (pass by reference), or void * pointers + (pass by value or reference). A void * pointer is of size 32 or 64 + bits on most machines. There is no need for the caller (C or Fortran) + to dereference the void * pointers, so these can be treated as + integer*4 or integer*8 in Fortran. A Fortran interface would have to + have all arguments passed by reference. + + * All user-callable routine names changed. The four sets are now: + umfpack_di_* real (double precision), int's as integers + umfpack_dl_* real (double precision), longs's as integers + umfpack_zi_* real (double precision), int's as integers + umfpack_zl_* real (double precision), longs's as integers + + * Ptree (row preordering) and info on pivotal rows for each front + added to Symbolic object (extracted by umfpack_*_get_symbolic). + Ptree added as output argument to "umfpack (A, 'symbolic')" + mexFunction. + + * umfpack_*_transpose can do A' or A.' + + * umfpack_wsolve.c file removed (now generated from umfpack_solve.c). + + * Can now extract just the diagonal of U with umfpack_*_get_numeric, + without having to extract the entire matrix U. + + * UMFPACK_ERROR_singular_matrix (-2) removed. + + * UMFPACK_WARNING_singular_matrix (1) added. + + * Control [UMFPACK_PIVOT_OPTION] removed. No longer any symmetric + pivot option (conflicts with the handling of singular and + rectangular matrices). + + * Iterative refinement can do Ax=b, A'x=b, or A.'x=b. + + * Most floating-point operations done in macros, to support the complex + versions. + + * Info [UMFPACK_N] is now Info [UMFPACK_NROW] + + * Info [UMFPACK_NCOL], Info [UMFPACK_UDIAG_NZ], Info [UMFPACK_UDIAG_NZ] + added. + + * umfpack_* routines with "n" as input now use two arguments, + n_row and n_col. + + * umfpack mexFunction now explicitly transposes A for b/A. It computes + it using the array transpose as (A.'\b.').' + +January 1, 2002: UMFPACK Version 3.2 released. Submitted to ACM Trans. + on Mathematical Software. + + * The umfpack mexFunction now returns the Info array when the matrix + is singular. Returned an empty array prior to this change. + + * Renamed variable that conflicted with system library routines + (system and j1). + + * Added a #ifdef MATHWORKS definition, so the built-in UMFPACK routine + (in a future release of MATLAB) can use the internal ut* memory + allocation routines, ut* assertion routine, and utPrintf. + + * MAX and MIN are not defined if they are already defined. + + * A bug fix in umf_kernel_init (a variable was not properly initialized). + + * Removed unused variables. + +October 8, 2001: UMFPACK Version 3.1 released. + +August-October, 2001: + + * added umfpack_btf M-file. + + * modified the BLAS update in the frontal matrix. If there are only + a few pivots in remaining in the current front, then the BLAS3 update + is delayed to include pivots in the next front. + + * Removed the special-case handling of dense columns from the numerical + factorization (kept it in the colamd preordering). This improves the + performance of UMFPACK on dense matrices by a factor of 5 or so, and + simplifies the code. + + * Added a symmetric-preference pivoting option. The option slightly + (but uniformly) improves the ordering when factorizing matrices with + symmetric nonzero pattern. That class of matrix is better handled by + the symmetric-pattern multifrontal method (MA41 in the Harwell + Subroutine Library), however. + + * Fixed the detection of integer overflow. The 32-bit version cannot + make use of more than 2GB of main memory (use the 64-bit version + in that case, instead). The 32-bit version did not correctly detect + when it was trying to factorize too large of a matrix. + +May 4, 2001: + + * SGI port extended. It can now call the SCSL Scientific Library, with + 64-bit BLAS. Make.sgi and umf_config.h modified. + +April 30, 2001: UMFPACK Version 3.0 released. Changes since 3.0Beta release: + + * Long integer version added (umfpack_l_* user-callable routines). + + * Peak memory usage in the numerical factorization reduced by a total of + 12n integers (8n temporary workspace used during numerical + factorization, and 4n for the permanent LU factors which was allocated + at the beginning of factorization). + + * Ported to the IBM RS 6000 and Compaq Alpha, with help from Anshul Gupta + and Friedrich Grund, respectively. + + * 64-bit version added. Uses dgemm_64, dgemv_64, and dger_64 in the Sun + Performance Library. 64-bit versions with the BLAS might not work on + any other platform, because they take int's as their integer input + arguments instead of long's. Unfortunately, the proposed ANSI + definition of the C-BLAS also uses int's as input integer arguments. + It ought to use long's, or include a version that uses long's, just + like the Sun Performance Library BLAS. + + * Additional statistics returned in Info: + Info [UMFPACK_SIZE_OF_INT] sizeof (int) + Info [UMFPACK_SIZE_OF_LONG] sizeof (long) + Info [UMFPACK_SIZE_OF_POINTER] sizeof (void *) + Info [UMFPACK_SIZE_OF_ENTRY] (was Info [UMFPACK_WORD]) + Info [UMFPACK_MAX_FRONT_SIZE_ESTIMATE] est. front matrix size + Info [UMFPACK_MAX_FRONT_SIZE] actual max frontal matrix size. + Contents of Info rearranged. + + * UMFPACK_ERROR_bad_configurution error code replaced with + UMFPACK_ERROR_problem_too_large error code. The "bad configuration" + error occured when sizeof (int) < sizeof (size_t). Now, the int + version of UMFPACK can use 32-bit int's and 64-bit pointers, and the + long version can use 64-bit long's and 64-bit pointers. Both versions + check to see if the array sizes allocated are larger than what can be + accessed by an integer index variable (int or long, depending on the + version), and returns UMFPACK_ERROR_problem_too_large if they become + too large. + +March 15, 2001: UMFPACK Version 3.0Beta released. + diff --git a/deal.II/contrib/umfpack/UMFPACK/Doc/License b/deal.II/contrib/umfpack/UMFPACK/Doc/License new file mode 100644 index 0000000000..20fdc97a62 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Doc/License @@ -0,0 +1,53 @@ +UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +Davis. All Rights Reserved. + +UMFPACK License: + + Your use or distribution of UMFPACK or any modified version of + UMFPACK implies that you agree to this License. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY + EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to use or copy this program, provided + that the Copyright, this License, and the Availability of the original + version is retained on all copies. User documentation of any code that + uses UMFPACK or any modified version of UMFPACK code must cite the + Copyright, this License, the Availability note, and "Used by permission." + Permission to modify the code and to distribute modified code is granted, + provided the Copyright, this License, and the Availability note are + retained, and a notice that the code was modified is included. This + software was developed with support from the National Science Foundation, + and is provided to you free of charge. + +Availability: + + http://www.cise.ufl.edu/research/sparse/umfpack + +-------------------------------------------------------------------------------- + +AMD Version 1.1 (Jan. 10, 2004), Copyright (c) 2004 by Timothy A. +Davis, Patrick R. Amestoy, and Iain S. Duff. All Rights Reserved. + +AMD License: + + Your use or distribution of AMD or any modified version of + AMD implies that you agree to this License. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY + EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to use or copy this program, provided + that the Copyright, this License, and the Availability of the original + version is retained on all copies. User documentation of any code that + uses AMD or any modified version of AMD code must cite the + Copyright, this License, the Availability note, and "Used by permission." + Permission to modify the code and to distribute modified code is granted, + provided the Copyright, this License, and the Availability note are + retained, and a notice that the code was modified is included. This + software was developed with support from the National Science Foundation, + and is provided to you free of charge. + +Availability: + + http://www.cise.ufl.edu/research/sparse/amd diff --git a/deal.II/contrib/umfpack/UMFPACK/Doc/Makefile b/deal.II/contrib/umfpack/UMFPACK/Doc/Makefile new file mode 100644 index 0000000000..a7458bbcf6 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Doc/Makefile @@ -0,0 +1,52 @@ +#------------------------------------------------------------------------------- +# UMFPACK Makefile for compiling on Unix systems (for GNU or original make) +#------------------------------------------------------------------------------- + +all: dist + +include ../Make/Make.include + +#------------------------------------------------------------------------------- +# Remove all but the files in the original distribution +#------------------------------------------------------------------------------- + +# Note that UserGuide.tex is created from UserGuide.stex, the files in +# the ../Include directory, and the ../Demo/umfpack_simple.c file. +purge: clean + - $(RM) *.aux *.bbl *.blg *.log *.toc + - $(RM) UserGuide.tex + +#------------------------------------------------------------------------------- +# Create the User Guide and Quick Start Guide +#------------------------------------------------------------------------------- + +UMFPACK = umfpack_col_to_triplet umfpack_defaults umfpack_free_numeric \ + umfpack_free_symbolic umfpack_get_numeric umfpack_get_lunz \ + umfpack_get_symbolic umfpack_numeric umfpack_qsymbolic \ + umfpack_report_control umfpack_report_info umfpack_report_matrix \ + umfpack_report_numeric umfpack_report_perm umfpack_report_status \ + umfpack_report_symbolic umfpack_report_triplet \ + umfpack_report_vector umfpack_solve umfpack_symbolic \ + umfpack_transpose umfpack_triplet_to_col umfpack_scale + +UMFPACKW = umfpack_wsolve + +USER = $(UMFPACKW) $(UMFPACK) + +SRC = $(addprefix ../Include/, $(addsuffix .h,$(USER))) ../Demo/umfpack_simple.c + +UserGuide.pdf: UserGuide.stex UserGuide.sed1 UserGuide.sed2 $(SRC) + sed -f UserGuide.sed1 < UserGuide.stex | sed -f UserGuide.sed2 \ + | expand -8 > UserGuide.tex + pdflatex UserGuide + bibtex UserGuide + pdflatex UserGuide + pdflatex UserGuide + +QuickStart.pdf: QuickStart.tex + pdflatex QuickStart + pdflatex QuickStart + +dist: QuickStart.pdf UserGuide.pdf + - $(RM) *.aux *.bbl *.blg *.log *.toc + - $(RM) UserGuide.tex diff --git a/deal.II/contrib/umfpack/UMFPACK/Doc/QuickStart.pdf b/deal.II/contrib/umfpack/UMFPACK/Doc/QuickStart.pdf new file mode 100644 index 0000000000000000000000000000000000000000..43c785cfdcbf8812d11c12207d35470573685f98 GIT binary patch literal 125745 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 QZA%;-1X@&=BmExJ>x%#?-C9ACqIU& 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

^>2+A3COlawf9tR%J&efb96%=}%1Wm%}g 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 Z37wox9Gy@0cc|Wkg$X%0 + #include "umfpack.h" + + int n = 5 ; + int Ap [ ] = {0, 2, 5, 9, 10, 12} ; + int Ai [ ] = { 0, 1, 0, 2, 4, 1, 2, 3, 4, 2, 1, 4} ; + double Ax [ ] = {2., 3., 3., -1., 4., 4., -3., 1., 2., 2., 6., 1.} ; + double b [ ] = {8., 45., -3., 3., 19.} ; + double x [5] ; + + int main (void) + { + double *null = (double *) NULL ; + int i ; + void *Symbolic, *Numeric ; + (void) umfpack_di_symbolic (n, n, Ap, Ai, Ax, &Symbolic, null, null) ; + (void) umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, null, null) ; + umfpack_di_free_symbolic (&Symbolic) ; + (void) umfpack_di_solve (UMFPACK_A, Ap, Ai, Ax, x, b, Numeric, null, null) ; + umfpack_di_free_numeric (&Numeric) ; + for (i = 0 ; i < n ; i++) printf ("x [%d] = %g\n", i, x [i]) ; + return (0) ; + } +\end{verbatim} +} + +The {\tt Ap}, {\tt Ai}, and {\tt Ax} arrays represent the matrix +\[ +\m{A} = \left[ +\begin{array}{rrrrr} + 2 & 3 & 0 & 0 & 0 \\ + 3 & 0 & 4 & 0 & 6 \\ + 0 & -1 & -3 & 2 & 0 \\ + 0 & 0 & 1 & 0 & 0 \\ + 0 & 4 & 2 & 0 & 1 \\ +\end{array} +\right]. +\] +and the solution is $\m{x} = [1 \, 2 \, 3 \, 4 \, 5]\tr$. The program uses +default control settings and does not return any statistics about the ordering, +factorization, or solution ({\tt Control} and {\tt Info} are both +{\tt (double *) NULL}). + +For routines to manipulate a simpler ``triplet-form'' data structure for your +sparse matrix $\m{A}$, refer to the UMFPACK User Guide. + +%------------------------------------------------------------------------------- +\section{Synopsis of primary C-callable routines} +\label{Synopsis} +%------------------------------------------------------------------------------- + +The matrix $\m{A}$ is {\tt m}-by-{\tt n} with {\tt nz} entries. +The optional {\tt umfpack\_di\_defaults} routine loads the default control +parameters into the {\tt Control} array. The settings can then be modified +before passing the array to the other routines. Refer to Section~\ref{Primary} +for more details. + +{\footnotesize +\begin{verbatim} + #include "umfpack.h" + int status, sys, n, m, nz, Ap [n+1], Ai [nz] ; + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], Ax [nz], X [n], B [n] ; + void *Symbolic, *Numeric ; + + status = umfpack_di_symbolic (m, n, Ap, Ai, Ax, &Symbolic, Control, Info) ; + status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, Control, Info) ; + status = umfpack_di_solve (sys, Ap, Ai, Ax, X, B, Numeric, Control, Info) ; + umfpack_di_free_symbolic (&Symbolic) ; + umfpack_di_free_numeric (&Numeric) ; + umfpack_di_defaults (Control) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\section{Installation} +\label{Install} +%------------------------------------------------------------------------------- + +You will need to install both UMFPACK v4.3 and AMD v1.1 (or AMD v1.0) to use UMFPACK. +The {\tt UMFPACK} and {\tt AMD} subdirectories must be placed side-by-side +within the same parent directory. AMD is a stand-alone package that +is required by UMFPACK. UMFPACK can be compiled without the +BLAS but your performance will be much less than what it should be. + +System-dependent configurations are in the {\tt AMD/Make} +and {\tt UMFPACK/Make} directories. You can edit the {\tt Make.include} +files in those directories to customize the compilation. The default +settings will work on most systems, except that UMFPACK will be compiled so +that it does not use the BLAS. Sample configuration files are provided +for Linux, Sun Solaris, SGI IRIX, IBM AIX, and the DEC/Compaq Alpha. + +To compile and install both packages, +go to the UMFPACK directory and type {\tt make}. This will compile the +libraries ({\tt AMD/Lib/libamd.a} and {\tt UMFPACK/Lib/libumfpack.a}). +A demo of the AMD ordering routine will be compiled and tested in +the {\tt AMD/Demo} directory, and five demo programs will then be +compiled and tested in the {\tt UMFPACK/Demo} directory. +The outputs of these demo programs will then be compared with output +files in the distribution. Expect to see a few differences, such as +residual norms, compile-time control settings, and perhaps memory usage +differences. The AMD and MATLAB mexFunctions for +use in MATLAB will also be compiled. If you do not have MATLAB, +type {\tt make lib} instead. + +If you compile UMFPACK and AMD and then later change the {\tt Make.include} +file or your system-specific configuration file such as {\tt Make.linux}, +then you should type {\tt make purge} and then {\tt make} to recompile. + +Here are the various parameters that you can control in your +{\tt Make.include} file: + +\begin{itemize} +\item {\tt CC = } your C compiler, such as {\tt cc}. +\item {\tt RANLIB = } your system's {\tt ranlib} program, if needed. +\item {\tt CFLAGS = } optimization flags, such as {\tt -O}. +\item {\tt CONFIG = } configuration settings, for the BLAS, memory allocation + routines, and timing routines. +\item {\tt LIB = } your libraries, such as {\tt -lm} or {\tt -lblas}. +\item {\tt RM =} the command to delete a file. +\item {\tt MV =} the command to rename a file. +\item {\tt MEX =} the command to compile a MATLAB mexFunction. +\item {\tt F77 =} the command to compile a Fortran program (optional). +\item {\tt F77FLAGS =} the Fortran compiler flags (optional). +\item {\tt F77LIB =} the Fortran libraries (optional). +\end{itemize} + +The {\tt CONFIG} string can include combinations of the following; +most deal with how the BLAS are called: +\begin{itemize} +\item {\tt -DNBLAS} if you do not have any BLAS at all. +\item {\tt -DCBLAS} if you have the C-BLAS. +\item {\tt -DNSUNPERF} if you are on Solaris but do not have the Sun + Performance Library. +\item {\tt -DNSCSL} if you on SGI IRIX but do not have the SCSL BLAS library. +\item {\tt -DLONGBLAS} if your BLAS can take {\tt long} integer input + arguments. +\item Options for controlling how C calls the Fortran BLAS: + {\tt -DBLAS\_BY\_VALUE}, {\tt -DBLAS\_NO\_UNDERSCORE}, + and {\tt -DBLAS\_CHAR\_ARG}. These are set automatically for Windows, + Sun Solaris, SGI Irix, Red Hat Linux, Compaq Alpha, and + AIX (the IBM RS 6000). +\item {\tt -DGETRUSAGE} if you have the {\tt getrusage} function. +\item {\tt -DLP64} if you are compiling in the LP64 model (32 bit {\tt int}'s, + 64 bit {\tt long}'s, and 64 bit pointers). +\item {\tt -DNUTIL} if you wish to compile the MATLAB-callable + UMFPACK mexFunction with the {\tt mxMalloc}, {\tt mxRealloc} + and {\tt mxFree} routines, instead of the undocumented (but + superior) {\tt utMalloc}, {\tt utRealloc}, and {\tt utFree} + routines. +\item {\tt -DNPOSIX} if you do not have the POSIX-compliant + {\tt sysconf} and {\tt times} routines. +\item {\tt -DNRECIPROCAL} controls a trade-off between speed and accuracy. + This is normally off by default, except when the {\tt gcc} + compiler is used. +\end{itemize} + +When you compile your program that uses the C-callable UMFPACK library, +you need to add the both {\tt UMFPACK/Lib/libumfpack.a} and +{\tt AMD/Lib/libamd.a} +libraries, and you need to tell your compiler to look in the +directories {\tt UMFPACK/Include} and {\tt AMD/Include} for include +files. See {\tt UMFPACK/Demo/Makefile} for an example. +You do not need to directly include any AMD include files in your +program, unless you directly call AMD routines. You only need the +\begin{verbatim} +#include "umfpack.h" +\end{verbatim} +statement, as described in Section~\ref{Synopsis}. + +%------------------------------------------------------------------------------- +\newpage +\section{The primary UMFPACK routines} +\label{Primary} +%------------------------------------------------------------------------------- + +\subsection{umfpack\_di\_symbolic} + +{\footnotesize +\begin{verbatim} +int umfpack_di_symbolic +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +Purpose: + + Given nonzero pattern of a sparse matrix A in column-oriented form, + umfpack_di_symbolic performs a column pre-ordering to reduce fill-in + (using COLAMD or AMD) and a symbolic factorization. This is required + before the matrix can be numerically factorized with umfpack_di_numeric. + + For the following discussion, let S be the submatrix of A obtained after + eliminating all pivots of zero Markowitz cost. S has dimension + (n_row-n1-nempty_row) -by- (n_col-n1-nempty_col), where + n1 = Info [UMFPACK_COL_SINGLETONS] + Info [UMFPACK_ROW_SINGLETONS], + nempty_row = Info [UMFPACK_NEMPTY_ROW] and + nempty_col = Info [UMFPACK_NEMPTY_COL]. + +Returns: + + The status code is returned. See Info [UMFPACK_STATUS], below. + +Arguments: + + Int n_row ; Input argument, not modified. + Int n_col ; Input argument, not modified. + + A is an n_row-by-n_col matrix. Restriction: n_row > 0 and n_col > 0. + + Int Ap [n_col+1] ; Input argument, not modified. + + Ap is an integer array of size n_col+1. On input, it holds the + "pointers" for the column form of the sparse matrix A. Column j of + the matrix A is held in Ai [(Ap [j]) ... (Ap [j+1]-1)]. The first + entry, Ap [0], must be zero, and Ap [j] <= Ap [j+1] must hold for all + j in the range 0 to n_col-1. The value nz = Ap [n_col] is thus the + total number of entries in the pattern of the matrix A. nz must be + greater than or equal to zero. + + Int Ai [nz] ; Input argument, not modified, of size nz = Ap [n_col]. + + The nonzero pattern (row indices) for column j is stored in + Ai [(Ap [j]) ... (Ap [j+1]-1)]. The row indices in a given column j + must be in ascending order, and no duplicate row indices may be present. + Row indices must be in the range 0 to n_row-1 (the matrix is 0-based). + + double Ax [nz] ; Optional input argument, not modified. + + The numerical values of the sparse matrix A. The nonzero pattern (row + indices) for column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and + the corresponding numerical values are stored in + Ax [(Ap [j]) ... (Ap [j+1]-1)]. Used only by the 2-by-2 strategy to + determine whether entries are "large" or "small". You do not have to + pass the same numerical values to umfpack_di_numeric. If Ax is not + present (a (double *) NULL pointer), then any entry in A is assumed to + be "large". + + void **Symbolic ; Output argument. + + **Symbolic is the address of a (void *) pointer variable in the user's + calling routine (see Syntax, above). On input, the contents of this + variable are not defined. On output, this variable holds a (void *) + pointer to the Symbolic object (if successful), or (void *) NULL if + a failure occurred. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Only the primary parameters are listed below: + + Control [UMFPACK_STRATEGY]: This is the most important control + parameter. It determines what kind of ordering and pivoting + strategy that UMFPACK should use. It is new to Version 4.1 + There are 4 options: + + UMFPACK_STRATEGY_AUTO: This is the default. The input matrix is + analyzed to determine how symmetric the nonzero pattern is, and + how many entries there are on the diagonal. It then selects one + of the following strategies. Refer to the User Guide for a + description of how the strategy is automatically selected. + + UMFPACK_STRATEGY_UNSYMMETRIC: Use the unsymmetric strategy. COLAMD + is used to order the columns of A, followed by a postorder of + the column elimination tree. No attempt is made to perform + diagonal pivoting. The column ordering is refined during + factorization. This strategy was the only one provided with + UMFPACK V4.0. + + In the numerical factorization, the + Control [UMFPACK_SYM_PIVOT_TOLERANCE] parameter is ignored. A + pivot is selected if its magnitude is >= + Control [UMFPACK_PIVOT_TOLERANCE] (default 0.1) times the + largest entry in its column. + + UMFPACK_STRATEGY_SYMMETRIC: Use the symmetric strategy (new to + Version 4.1). In this method, the approximate minimum degree + ordering (AMD) is applied to A+A', followed by a postorder of + the elimination tree of A+A'. UMFPACK attempts to perform + diagonal pivoting during numerical factorization. No refinement + of the column preordering is performed during factorization. + + In the numerical factorization, a nonzero entry on the diagonal + is selected as the pivot if its magnitude is >= Control + [UMFPACK_SYM_PIVOT_TOLERANCE] (default 0.001) times the largest + entry in its column. If this is not acceptable, then an + off-diagonal pivot is selected with magnitude >= Control + [UMFPACK_PIVOT_TOLERANCE] (default 0.1) times the largest entry + in its column. + + UMFPACK_STRATEGY_2BY2: a row permutation P2 is found that places + large entries on the diagonal. The matrix P2*A is then + factorized using the symmetric strategy, described above. + Refer to the User Guide for more information. + + Control [UMFPACK_2BY2_TOLERANCE]: a diagonal entry S (k,k) is + considered "small" if it is < tol * max (abs (S (:,k))), where S a + submatrix of the scaled input matrix, with pivots of zero Markowitz + cost removed. + + Control [UMFPACK_SCALE]: This parameter is new to V4.1. See + umfpack_numeric.h for a description. Only affects the 2-by-2 + strategy. Default: UMFPACK_SCALE_SUM. + + double Info [UMFPACK_INFO] ; Output argument, not defined on input. + + Contains statistics about the symbolic analysis. If a (double *) NULL + pointer is passed, then no statistics are returned in Info (this is not + an error condition). The entire Info array is cleared (all entries set + to -1) and then the following statistics are computed (only the + primary statistics are listed): + + Info [UMFPACK_STATUS]: status code. This is also the return value, + whether or not Info is present. + + UMFPACK_OK + + Each column of the input matrix contained row indices + in increasing order, with no duplicates. Only in this case + does umfpack_di_symbolic compute a valid symbolic factorization. + For the other cases below, no Symbolic object is created + (*Symbolic is (void *) NULL). + + UMFPACK_ERROR_n_nonpositive + + n is less than or equal to zero. + + UMFPACK_ERROR_invalid_matrix + + Number of entries in the matrix is negative, Ap [0] is nonzero, + a column has a negative number of entries, a row index is out of + bounds, or the columns of input matrix were jumbled (unsorted + columns or duplicate entries). + + UMFPACK_ERROR_out_of_memory + + Insufficient memory to perform the symbolic analysis. If the + analysis requires more than 2GB of memory and you are using + the 32-bit ("int") version of UMFPACK, then you are guaranteed + to run out of memory. Try using the 64-bit version of UMFPACK. + + UMFPACK_ERROR_argument_missing + + One or more required arguments is missing. + + UMFPACK_ERROR_internal_error + + Something very serious went wrong. This is a bug. + Please contact the author (davis@cise.ufl.edu). + + Info [UMFPACK_SIZE_OF_UNIT]: the number of bytes in a Unit, + for memory usage statistics below. + + Info [UMFPACK_SYMBOLIC_PEAK_MEMORY]: the amount of memory (in Units) + required for umfpack_di_symbolic to complete. This count includes + the size of the Symbolic object itself, which is also reported in + Info [UMFPACK_SYMBOLIC_SIZE]. + + Info [UMFPACK_NUMERIC_SIZE_ESTIMATE]: an estimate of the final size (in + Units) of the entire Numeric object (both fixed-size and variable- + sized parts), which holds the LU factorization (including the L, U, + P and Q matrices). + + Info [UMFPACK_PEAK_MEMORY_ESTIMATE]: an estimate of the total amount of + memory (in Units) required by umfpack_di_symbolic and + umfpack_di_numeric to perform both the symbolic and numeric + factorization. This is the larger of the amount of memory needed + in umfpack_di_numeric itself, and the amount of memory needed in + umfpack_di_symbolic (Info [UMFPACK_SYMBOLIC_PEAK_MEMORY]). The + count includes the size of both the Symbolic and Numeric objects + themselves. It can be a very loose upper bound, particularly when + the symmetric or 2-by-2 strategies are used. + + Info [UMFPACK_FLOPS_ESTIMATE]: an estimate of the total floating-point + operations required to factorize the matrix. This is a "true" + theoretical estimate of the number of flops that would be performed + by a flop-parsimonious sparse LU algorithm. It assumes that no + extra flops are performed except for what is strictly required to + compute the LU factorization. It ignores, for example, the flops + performed by umfpack_di_numeric to add contribution blocks of + frontal matrices together. If L and U are the upper bound on the + pattern of the factors, then this flop count estimate can be + represented in MATLAB (for real matrices, not complex) as: + + Lnz = full (sum (spones (L))) - 1 ; % nz in each col of L + Unz = full (sum (spones (U')))' - 1 ; % nz in each row of U + flops = 2*Lnz*Unz + sum (Lnz) ; + + The actual "true flop" count found by umfpack_di_numeric will be + less than this estimate. + + Info [UMFPACK_LNZ_ESTIMATE]: an estimate of the number of nonzeros in + L, including the diagonal. Since L is unit-diagonal, the diagonal + of L is not stored. This estimate is a strict upper bound on the + actual nonzeros in L to be computed by umfpack_di_numeric. + + Info [UMFPACK_UNZ_ESTIMATE]: an estimate of the number of nonzeros in + U, including the diagonal. This estimate is a strict upper bound on + the actual nonzeros in U to be computed by umfpack_di_numeric. + + Info [UMFPACK_SYMBOLIC_TIME]: The CPU time taken, in seconds. + + Info [UMFPACK_STRATEGY_USED]: The ordering strategy used: + UMFPACK_STRATEGY_SYMMETRIC, UMFPACK_STRATEGY_UNSYMMETRIC, or + UMFPACK_STRATEGY_2BY2. +\end{verbatim} +} + + +%------------------------------------------------------------------------------- +\newpage +\subsection{umfpack\_di\_numeric} + +{\footnotesize +\begin{verbatim} +int umfpack_di_numeric +( + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + void *Symbolic, + void **Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +Purpose: + + Given a sparse matrix A in column-oriented form, and a symbolic analysis + computed by umfpack_di_symbolic, the umfpack_di_numeric routine performs the + numerical factorization, PAQ=LU, PRAQ=LU, or P(R\A)Q=LU, where P and Q are + permutation matrices (represented as permutation vectors), R is the row + scaling, L is unit-lower triangular, and U is upper triangular. This is + required before the system Ax=b (or other related linear systems) can be + solved. umfpack_di_numeric can be called multiple times for each call to + umfpack_di_symbolic, to factorize a sequence of matrices with identical + nonzero pattern. Simply compute the Symbolic object once, with + umfpack_di_symbolic, and reuse it for subsequent matrices. + umfpack_di_numeric safely detects if the pattern changes, and sets an + appropriate error code. + +Returns: + + The status code is returned. See Info [UMFPACK_STATUS], below. + +Arguments: + + Int Ap [n_col+1] ; Input argument, not modified. + + This must be identical to the Ap array passed to umfpack_di_symbolic. + The value of n_col is what was passed to umfpack_di_symbolic (this is + held in the Symbolic object). + + Int Ai [nz] ; Input argument, not modified, of size nz = Ap [n_col]. + + This must be identical to the Ai array passed to umfpack_di_symbolic. + + double Ax [nz] ; Input argument, not modified, of size nz = Ap [n_col]. + + The numerical values of the sparse matrix A. The nonzero pattern (row + indices) for column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and + the corresponding numerical values are stored in + Ax [(Ap [j]) ... (Ap [j+1]-1)]. + + void *Symbolic ; Input argument, not modified. + + The Symbolic object, which holds the symbolic factorization computed by + umfpack_di_symbolic. The Symbolic object is not modified by + umfpack_di_numeric. + + void **Numeric ; Output argument. + + **Numeric is the address of a (void *) pointer variable in the user's + calling routine (see Syntax, above). On input, the contents of this + variable are not defined. On output, this variable holds a (void *) + pointer to the Numeric object (if successful), or (void *) NULL if + a failure occurred. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Only the primary parameters are listed below: + + Control [UMFPACK_PIVOT_TOLERANCE]: relative pivot tolerance for + threshold partial pivoting with row interchanges. In any given + column, an entry is numerically acceptable if its absolute value is + greater than or equal to Control [UMFPACK_PIVOT_TOLERANCE] times + the largest absolute value in the column. A value of 1.0 gives true + partial pivoting. If less than or equal to zero, then any nonzero + entry is numerically acceptable as a pivot (this is changed from + Version 4.0). Default: 0.1. + + Smaller values tend to lead to sparser LU factors, but the solution + to the linear system can become inaccurate. Larger values can lead + to a more accurate solution (but not always), and usually an + increase in the total work. + + Control [UMFPACK_SYM_PIVOT_TOLERANCE]: This parameter is new to V4.1. + If diagonal pivoting is attempted (the symmetric or symmetric-2by2 + strategies are used) then this parameter is used to control when the + diagonal entry is selected in a given pivot column. The absolute + value of the entry must be >= Control [UMFPACK_SYM_PIVOT_TOLERANCE] + times the largest absolute value in the column. A value of zero + will ensure that no off-diagonal pivoting is performed, except that + zero diagonal entries are not selected if there are any off-diagonal + nonzero entries. + + If an off-diagonal pivot is selected, an attempt is made to restore + symmetry later on. Suppose A (i,j) is selected, where i != j. + If column i has not yet been selected as a pivot column, then + the entry A (j,i) is redefined as a "diagonal" entry, except that + the tighter tolerance (Control [UMFPACK_PIVOT_TOLERANCE]) is + applied. This strategy has an effect similar to 2-by-2 pivoting + for symmetric indefinite matrices. If a 2-by-2 block pivot with + nonzero structure + + i j + i: 0 x + j: x 0 + + is selected in a symmetric indefinite factorization method, the + 2-by-2 block is inverted and a rank-2 update is applied. In + UMFPACK, this 2-by-2 block would be reordered as + + j i + i: x 0 + j: 0 x + + In both cases, the symmetry of the Schur complement is preserved. + + Control [UMFPACK_SCALE]: This parameter is new to V4.1. Version 4.0 + did not scale the matrix. Note that the user's input matrix is + never modified, only an internal copy is scaled. + + There are three valid settings for this parameter. If any other + value is provided, the default is used. + + UMFPACK_SCALE_NONE: no scaling is performed. + + UMFPACK_SCALE_SUM: each row of the input matrix A is divided by + the sum of the absolute values of the entries in that row. + The scaled matrix has an infinity norm of 1. + + UMFPACK_SCALE_MAX: each row of the input matrix A is divided by + the maximum the absolute values of the entries in that row. + In the scaled matrix the largest entry in each row has + a magnitude exactly equal to 1. + + Scaling is very important for the "symmetric" strategy when + diagonal pivoting is attempted. It also improves the performance + of the "unsymmetric" strategy. + + Default: UMFPACK_SCALE_SUM. + + double Info [UMFPACK_INFO] ; Output argument. + + Contains statistics about the numeric factorization. If a + (double *) NULL pointer is passed, then no statistics are returned in + Info (this is not an error condition). The following statistics are + computed in umfpack_di_numeric (only the primary statistics are listed): + + Info [UMFPACK_STATUS]: status code. This is also the return value, + whether or not Info is present. + + UMFPACK_OK + + Numeric factorization was successful. umfpack_di_numeric + computed a valid numeric factorization. + + UMFPACK_WARNING_singular_matrix + + Numeric factorization was successful, but the matrix is + singular. umfpack_di_numeric computed a valid numeric + factorization, but you will get a divide by zero in + umfpack_di_solve. For the other cases below, no Numeric object + is created (*Numeric is (void *) NULL). + + UMFPACK_ERROR_out_of_memory + + Insufficient memory to complete the numeric factorization. + + UMFPACK_ERROR_argument_missing + + One or more required arguments are missing. + + UMFPACK_ERROR_invalid_Symbolic_object + + Symbolic object provided as input is invalid. + + UMFPACK_ERROR_different_pattern + + The pattern (Ap and/or Ai) has changed since the call to + umfpack_di_symbolic which produced the Symbolic object. + + Info [UMFPACK_NUMERIC_SIZE]: the actual final size (in Units) of the + entire Numeric object, including the final size of the variable + part of the object. Info [UMFPACK_NUMERIC_SIZE_ESTIMATE], + an estimate, was computed by umfpack_di_symbolic. The estimate is + normally an upper bound on the actual final size, but this is not + guaranteed. + + Info [UMFPACK_PEAK_MEMORY]: the actual peak memory usage (in Units) of + both umfpack_di_symbolic and umfpack_di_numeric. An estimate, + Info [UMFPACK_PEAK_MEMORY_ESTIMATE], was computed by + umfpack_di_symbolic. The estimate is normally an upper bound on the + actual peak usage, but this is not guaranteed. With testing on + hundreds of matrix arising in real applications, I have never + observed a matrix where this estimate or the Numeric size estimate + was less than the actual result, but this is theoretically possible. + Please send me one if you find such a matrix. + + Info [UMFPACK_FLOPS]: the actual count of the (useful) floating-point + operations performed. An estimate, Info [UMFPACK_FLOPS_ESTIMATE], + was computed by umfpack_di_symbolic. The estimate is guaranteed to + be an upper bound on this flop count. The flop count excludes + "useless" flops on zero values, flops performed during the pivot + search (for tentative updates and assembly of candidate columns), + and flops performed to add frontal matrices together. + + Info [UMFPACK_LNZ]: the actual nonzero entries in final factor L, + including the diagonal. This excludes any zero entries in L, + although some of these are stored in the Numeric object. The + Info [UMFPACK_LU_ENTRIES] statistic does account for all + explicitly stored zeros, however. Info [UMFPACK_LNZ_ESTIMATE], + an estimate, was computed by umfpack_di_symbolic. The estimate is + guaranteed to be an upper bound on Info [UMFPACK_LNZ]. + + Info [UMFPACK_UNZ]: the actual nonzero entries in final factor U, + including the diagonal. This excludes any zero entries in U, + although some of these are stored in the Numeric object. The + Info [UMFPACK_LU_ENTRIES] statistic does account for all + explicitly stored zeros, however. Info [UMFPACK_UNZ_ESTIMATE], + an estimate, was computed by umfpack_di_symbolic. The estimate is + guaranteed to be an upper bound on Info [UMFPACK_UNZ]. + + Info [UMFPACK_NUMERIC_TIME]: The CPU time taken, in seconds. +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\newpage +\subsection{umfpack\_di\_solve} + +{\footnotesize +\begin{verbatim} +int umfpack_di_solve +( + int sys, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + double X [ ], + const double B [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +Purpose: + + Given LU factors computed by umfpack_di_numeric (PAQ=LU, PRAQ=LU, or + P(R\A)Q=LU) and the right-hand-side, B, solve a linear system for the + solution X. Iterative refinement is optionally performed. Only square + systems are handled. Singular matrices result in a divide-by-zero for all + systems except those involving just the matrix L. Iterative refinement is + not performed for singular matrices. + + In the discussion below, n is equal to n_row and n_col, because only + square systems are handled. + +Returns: + + The status code is returned. See Info [UMFPACK_STATUS], below. + +Arguments: + + Int sys ; Input argument, not modified. + + Defines which system to solve. (') is the linear algebraic transpose. + + sys value system solved + + UMFPACK_A Ax=b + UMFPACK_At A'x=b + UMFPACK_Pt_L P'Lx=b + UMFPACK_L Lx=b + UMFPACK_Lt_P L'Px=b + UMFPACK_Lt L'x=b + UMFPACK_U_Qt UQ'x=b + UMFPACK_U Ux=b + UMFPACK_Q_Ut QU'x=b + UMFPACK_Ut U'x=b + + Iterative refinement can be optionally performed when sys is any of + the following: + + UMFPACK_A Ax=b + UMFPACK_At A'x=b + + For the other values of the sys argument, iterative refinement is not + performed (Control [UMFPACK_IRSTEP], Ap, Ai, and Ax are ignored). + + Int Ap [n+1] ; Input argument, not modified. + Int Ai [nz] ; Input argument, not modified. + double Ax [nz] ; Input argument, not modified. + + If iterative refinement is requested (Control [UMFPACK_IRSTEP] >= 1, + Ax=b or A'x=b is being solved, and A is nonsingular), then + these arrays must be identical to the same ones passed to + umfpack_di_numeric. The umfpack_di_solve routine does not check the + contents of these arguments, so the results are undefined if Ap, Ai, Ax, + are modified between the calls the umfpack_di_numeric and + umfpack_di_solve. These three arrays do not need to be present (NULL + pointers can be passed) if Control [UMFPACK_IRSTEP] is zero, or if a + system other than Ax=b or A'x=b is being solved, or if A is + singular, since in each of these cases A is not accessed. + + double X [n] ; Output argument. + + The solution to the linear system, where n = n_row = n_col is the + dimension of the matrices A, L, and U. + + double B [n] ; Input argument, not modified. + + The right-hand side vector, b, stored as a conventional array of size n + (or two arrays of size n for complex versions). This routine does not + solve for multiple right-hand-sides, nor does it allow b to be stored in + a sparse-column form. + + void *Numeric ; Input argument, not modified. + + Numeric must point to a valid Numeric object, computed by + umfpack_di_numeric. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. + + Control [UMFPACK_IRSTEP]: The maximum number of iterative refinement + steps to attempt. A value less than zero is treated as zero. If + less than 1, or if Ax=b or A'x=b is not being solved, or + if A is singular, then the Ap, Ai, and Ax arguments are not + accessed. Default: 2. + + double Info [UMFPACK_INFO] ; Output argument. + + Contains statistics about the solution factorization. If a + (double *) NULL pointer is passed, then no statistics are returned in + Info (this is not an error condition). The following statistics are + computed in umfpack_di_solve (only the primary statistics are listed): + + Info [UMFPACK_STATUS]: status code. This is also the return value, + whether or not Info is present. + + UMFPACK_OK + + The linear system was successfully solved. + + UMFPACK_WARNING_singular_matrix + + A divide-by-zero occured. Your solution will contain Inf's + and/or NaN's. Some parts of the solution may be valid. For + example, solving Ax=b with + + A = [2 0] b = [ 1 ] returns x = [ 0.5 ] + [0 0] [ 0 ] [ Inf ] + + UMFPACK_ERROR_out_of_memory + + Insufficient memory to solve the linear system. + + UMFPACK_ERROR_argument_missing + + One or more required arguments are missing. The B and X + arguments are always required. Info and Control are not + required. Ap, Ai and Ax are required if Ax=b or + A'x=b is to be solved, the (default) iterative + refinement is requested, and the matrix A is nonsingular. + + UMFPACK_ERROR_invalid_system + + The sys argument is not valid, or the matrix A is not square. + + UMFPACK_ERROR_invalid_Numeric_object + + The Numeric object is not valid. + + Info [UMFPACK_SOLVE_FLOPS]: the number of floating point operations + performed to solve the linear system. This includes the work + taken for all iterative refinement steps, including the backtrack + (if any). + + Info [UMFPACK_SOLVE_TIME]: The time taken, in seconds. +\end{verbatim} +} + + +%------------------------------------------------------------------------------- +\newpage + +\subsection{umfpack\_di\_free\_symbolic} +{\footnotesize +\begin{verbatim} +void umfpack_di_free_symbolic +( + void **Symbolic +) ; + +Purpose: + + Deallocates the Symbolic object and sets the Symbolic handle to NULL. + +Arguments: + + void **Symbolic ; Input argument, deallocated and Symbolic is + set to (void *) NULL on output. +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{umfpack\_di\_free\_numeric} + +{\footnotesize +\begin{verbatim} +void umfpack_di_free_numeric +( + void **Numeric +) ; + +Purpose: + + Deallocates the Numeric object and sets the Numeric handle to NULL. + +Arguments: + + void **Numeric ; Input argument, deallocated and Numeric is + set to (void *) NULL on output. +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{umfpack\_di\_defaults} + +{\footnotesize +\begin{verbatim} +void umfpack_di_defaults +( + double Control [UMFPACK_CONTROL] +) ; + +Purpose: + + Sets the default control parameter settings. + +Arguments: + + double Control [UMFPACK_CONTROL] ; Output argument. + + Control is set to the default control parameter settings. +\end{verbatim} +} +\end{document} diff --git a/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.bib b/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.bib new file mode 100644 index 0000000000..691ba73e45 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.bib @@ -0,0 +1,324 @@ +@string{TOMS = "ACM Trans. Math. Softw."} +@string{SIMAX = "SIAM J. Matrix Anal. Applic."} +@string{SINUM = "SIAM J. Numer. Anal."} +@string{SIAMJSC = "SIAM J. Sci. Comput."} +@string{SIAMJSSC = "SIAM J. Sci. Statist. Comput."} +@string{IJNME = "Internat. J. Numer. Methods Eng."} +@string{SIAMJADM = "SIAM J. Alg. Disc. Meth."} + +@article{AmestoyDavisDuff96, + author={Amestoy, P. R. and Davis, T. A. and Duff, I. S.}, + title={An approximate minimum degree ordering algorithm}, + journal=SIMAX, + year={1996} + ,volume={17} + ,number={4} + ,pages={886-905}} + +@article{AmestoyDavisDuff03, + author={Amestoy, P. R. and Davis, T. A. and Duff, I. S.}, + title={Algorithm 8xx: {AMD}, an approximate minimum degree ordering algorithm}, + journal=TOMS, + year={2003 (under submission)}, + note={Also TR-03-010 at www.cise.ufl.edu/tech-reports.} + } + +@techreport{AmestoyDavisDuff03_user, + author={Amestoy, P. R. and Davis, T. A. and Duff, I. S.}, + title={{AMD} Version 1.0 User Guide}, + institution={CISE Dept., Univ. of Florida}, + year={2003} + ,number={TR-03-011} + ,address={Gainesville, FL} + ,note={www.cise.ufl.edu/tech-reports.} + } + +@article{Davis03, + author={Davis, T. A.}, + title={A column pre-ordering strategy for the unsymmetric-pattern multifrontal method}, + journal=TOMS, + year={2003 (under submission)} + ,note={Also TR-03-006 at www.cise.ufl.edu/tech-reports.} + } + +@article{Davis03_algo, + author={Davis, T. A.}, + title={Algorithm 8xx: {UMFPACK V4.3}, an unsymmetric-pattern multifrontal method}, + journal=TOMS, + year={2004 (under submission)} + ,note={Also TR-04-004 at www.cise.ufl.edu/tech-reports.} + } + +@techreport{Davis03_umf, + author={Davis, T. A.}, + title={{UMFPACK} Version 4.3 User Guide}, + institution={Univ. of Florida, CISE Dept.}, + year={2004} + ,number={TR-04-003} + ,address={Gainesville, FL} + ,note={(www.cise.ufl.edu/tech-reports)} + } + +@techreport{Davis03_umfquick, + author={Davis, T. A.}, + title={{UMFPACK} Version 4.3 Quick Start Guide}, + institution={Univ. of Florida, CISE Dept.}, + year={2004} + ,number={TR-04-005} + ,address={Gainesville, FL} + ,note={(www.cise.ufl.edu/tech-reports)} + } + +@article{DavisDuff97, + author={Davis, T. A. and Duff, I. S.}, + title={An unsymmetric-pattern multifrontal method for sparse {LU} factorization}, + journal=SIMAX, + year={1997} + ,volume={18} + ,number={1} + ,pages={140-158}} + +@article{DavisDuff99, + author={Davis, T. A. and Duff, I. S.}, + title={A combined unifrontal/multifrontal method for unsymmetric sparse matrices}, + journal=TOMS, + volume={25}, + number={1}, + pages={1-19}, + year={1999}} + +@article{SuperLU99, + author={Demmel, J. W. and Eisenstat, S. C. and Gilbert, J. R. and Li, X. S. and Liu, J. W. H.}, + title={A supernodal approach to sparse partial pivoting}, + journal=SIMAX, + year={1999} + ,volume={20} + ,number={3} + ,pages={720-755} + ,note={www.netlib.org} + } + +@article{ACM679a, + author={Dongarra, J. J. and Du Croz, J. and Duff, I. S. and Hammarling, S.}, + title={A set of level-3 basic linear algebra subprograms}, + journal=TOMS, + year={1990} + ,volume={16} + ,number={1} + ,pages={1--17}} + +@article{netlib, + author={Dongarra, J. J. and Grosse, E.}, + title={Distribution of mathematical software via electronic mail}, + journal={Comm. ACM}, + year={1987} + ,volume={30} + ,pages={403-407} + ,note={www.netlib.org} + } + +@article{Duff78b, + author={Duff, I. S. and Reid, J. K.}, + year={1978}, + title={Algorithm 529: Permutations to Block Triangular Form}, + journal=TOMS, + volume={4}, + annote={f}, + number={2}, + pages={189-192}, + keywords={102 ordering block triangular form}} + +@article{Duff81b, + author={Duff, I. S.}, + year={1981}, + title={Algorithm 575: Permutations for a Zero-Free Diagonal}, + journal=TOMS, + annote={f}, + volume={7}, + pages={387-390}, + keywords={ordering, zero-free diagonal}} + +@techreport{GotoVandeGeijn02, + author = {Goto, K. and van de Geijn, R.}, + title = {On Reducing {TLB} Misses in Matrix Multiplication, {FLAME} Working Note 9}, + institution={The University of Texas at Austin, Department of Computer Sciences}, + number={TR-2002-55}, + month={Nov.}, + year={2002}} + +@article{GeorgeNg85, + author={George, A. and Ng, E. G.}, + year={1985}, + title={An Implementation of {G}aussian Elimination with + Partial Pivoting for Sparse Systems}, + journal=SIAMJSSC, + volume={6}, + number={2}, + pages={390-409}} + +@article{GeorgeNg87, + author={George, A. and Ng, E. G.}, + year={1987}, + title={Symbolic Factorization for Sparse {G}aussian Elimination + with Partial Pivoting}, + journal={SIAM J. Sci. Statist. Comput.}, + volume={8}, + number={6}, + pages={877-898}} + +@article{GilbertMolerSchreiber, + author={Gilbert, J. R. and Moler, C. and Schreiber, R.}, + title={Sparse matrices in {MATLAB}: design and implementation}, + journal=SIMAX, + year={1992} + ,volume={13} + ,number={1} + ,pages={333-356}} + +@article{GilbertPeierls88, + author={Gilbert, J. R. and Peierls, T.}, + year={1988}, + title={Sparse Partial Pivoting in Time Proportional to Arithmetic Operations}, + journal={SIAM J. Sci. Statist. Comput.}, + volume={9}, + pages={862-874}} + +@article{Gustavson78, + author={Gustavson, F. G.}, + year={1978}, + title={Two Fast Algorithms for Sparse Matrices: Multiplication and Permuted Transposition}, + journal=TOMS, + volume={4}, + number={3}, + pages={250-269}} + +@techreport{Larimore98, + author={Larimore, S. I.}, + title={An approximate minimum degree column ordering algorithm}, + institution={Univ. of Florida, CISE Dept.}, + year={1998} + ,number={TR-98-016} + ,address={Gainesville, FL} + ,note={www.cise.ufl.edu/tech-reports}} + +@techreport{DavisGilbertLarimoreNg00, + author={Davis, T. A. and Gilbert, J. R. and Larimore, S. I. and Ng, E. G.}, + title={A column approximate minimum degree ordering algorithm}, + institution={Univ. of Florida, CISE Dept.}, + year={2000} + ,number={TR-00-005} + ,address={Gainesville, FL} + ,month={October} + ,note={(www.cise.ufl.edu/tech-reports. Submitted to ACM Trans. Math. Softw.)} + } + +@techreport{DavisGilbertLarimoreNg00_algo, + author={Davis, T. A. and Gilbert, J. R. and Larimore, S. I. and Ng, E. G.}, + title={Algorithm 8xx: {COLAMD}, a column approximate minimum degree ordering algorithm}, + institution={Univ. of Florida, CISE Dept.}, + year={2000} + ,number={TR-00-006} + ,address={Gainesville, FL} + ,month={October} + ,note={(www.cise.ufl.edu/tech-reports. Submitted to ACM Trans. Math. Softw.)} + } + + +@INCOLLECTION{GilbertNg93, + author = {J. R. Gilbert and E. G. Ng}, + editor = {A. George and J. R. Gilbert and J. W.H. Liu}, + year = 1993, + title = {Predicting Structure in Nonsymmetric Sparse Matrix Factorizations}, + booktitle = {Graph Theory and Sparse Matrix Computation}, + series = {Volume 56 of the {IMA} Volumes in Mathematics and its Applications}, + pages = {107-139}, + publisher = {Springer-Verlag} +} + + +@techreport{ATLAS, + author={Whaley, R. C and Petitet, A. and Dongarra, J. J.}, + title={Automated Emperical Optimization of Software and the {ATLAS} Project}, + institution={Computer Science Department, The University of Tennessee}, + year={2000} + ,number={LAPACK Working Note 147} + ,month={September} + ,note={www.netlib.org/atlas} + } + +@article{DaydeDuff99, + author = "M. J. Dayd\'{e} and I. S. Duff", + title = "The {RISC} {BLAS}: A Blocked Implementation of Level 3 {BLAS} for {RISC} Processors", + journal = TOMS, + volume = "25", + number = "3", + month = {Sept.}, + year ="1999" + } + + +@article{ardd:89, + author = {M. Arioli and J. W. Demmel and I. S. Duff}, + year = "1989", + title = {Solving sparse linear systems with sparse backward error}, + journal = SIMAX, + volume = {10}, + pages = {165-190} +} + + +@article{DavisHager99, + author={Davis, T. A. and Hager, W. W.}, + title={Modifying a sparse {C}holesky factorization}, + journal=SIMAX, + year={1999} + ,volume={20} + ,number={3} + ,pages={606-627} + } + + +@article{dusc:96, + author = {I. S. Duff and J. A. Scott}, + title = {The design of a new frontal code for solving sparse unsymmetric systems}, + journal = TOMS, + year = "1996", + volume = "22", + number = "1", + pages = "30-45" + } + + +@article{Duff78a, + author={Duff, I. S. and Reid, J. K.}, + year={1978}, + title={An Implementation of {T}arjan's Algorithm for the Block Triangularization of a Matrix}, + journal=TOMS, + volume={4}, + number={2}, + pages={137-147} + } + +@book{GeorgeLiu, + author={George, A. and Liu, J. W. H.}, + year={1981}, + title={Computer Solution of Large Sparse Positive Definite Systems}, + publisher={Englewood Cliffs, New Jersey: Prentice-Hall} + } + +@article{GilbertNgPeyton94, + author={Gilbert, J. R. and Ng, E. G. and Peyton, B. W.}, + title={An efficient algorithm to compute row and column counts for sparse {C}holesky factorization}, + journal=SIMAX, + year={1994} + ,volume={15} + ,number={4} + ,pages={1075-1091} + } + +@techreport{DuffGrimesLewis87b, + author={Duff, I. S. and Grimes, R. G. and Lewis, J. G.}, + year={1987}, + title={Users' Guide for the Harwell-Boeing Sparse Matrix Test Collection}, + institution={AERE Harwell Laboratory, United Kingdom Atomic Energy Authority}} + diff --git a/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.pdf b/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3aab8d85667d85d9488222b1907b61fecca52ff3 GIT binary patch literal 391768 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 B>?NG5GN_rloB*b&UhhbYmzy6P@m! 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 i!Az0_agui8KW{bYmj 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

7sK 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 literal 0 HcmV?d00001 diff --git a/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.sed1 b/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.sed1 new file mode 100644 index 0000000000..c44a6408b1 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.sed1 @@ -0,0 +1,32 @@ +/INCLUDE umfpack_col_to_triplet.h/r ../Include/umfpack_col_to_triplet.h +/INCLUDE umfpack_defaults.h/r ../Include/umfpack_defaults.h +/INCLUDE umfpack_free_numeric.h/r ../Include/umfpack_free_numeric.h +/INCLUDE umfpack_free_symbolic.h/r ../Include/umfpack_free_symbolic.h +/INCLUDE umfpack_get_lunz.h/r ../Include/umfpack_get_lunz.h +/INCLUDE umfpack_get_numeric.h/r ../Include/umfpack_get_numeric.h +/INCLUDE umfpack_get_symbolic.h/r ../Include/umfpack_get_symbolic.h +/INCLUDE umfpack_get_scale.h/r ../Include/umfpack_get_scale.h +/INCLUDE umfpack_numeric.h/r ../Include/umfpack_numeric.h +/INCLUDE umfpack_qsymbolic.h/r ../Include/umfpack_qsymbolic.h +/INCLUDE umfpack_report_control.h/r ../Include/umfpack_report_control.h +/INCLUDE umfpack_report_info.h/r ../Include/umfpack_report_info.h +/INCLUDE umfpack_report_matrix.h/r ../Include/umfpack_report_matrix.h +/INCLUDE umfpack_report_numeric.h/r ../Include/umfpack_report_numeric.h +/INCLUDE umfpack_report_perm.h/r ../Include/umfpack_report_perm.h +/INCLUDE umfpack_report_status.h/r ../Include/umfpack_report_status.h +/INCLUDE umfpack_report_symbolic.h/r ../Include/umfpack_report_symbolic.h +/INCLUDE umfpack_report_triplet.h/r ../Include/umfpack_report_triplet.h +/INCLUDE umfpack_report_vector.h/r ../Include/umfpack_report_vector.h +/INCLUDE umfpack_simple.c/r ../Demo/umfpack_simple.c +/INCLUDE umfpack_solve.h/r ../Include/umfpack_solve.h +/INCLUDE umfpack_scale.h/r ../Include/umfpack_scale.h +/INCLUDE umfpack_symbolic.h/r ../Include/umfpack_symbolic.h +/INCLUDE umfpack_timer.h/r ../Include/umfpack_timer.h +/INCLUDE umfpack_tictoc.h/r ../Include/umfpack_tictoc.h +/INCLUDE umfpack_transpose.h/r ../Include/umfpack_transpose.h +/INCLUDE umfpack_triplet_to_col.h/r ../Include/umfpack_triplet_to_col.h +/INCLUDE umfpack_wsolve.h/r ../Include/umfpack_wsolve.h +/INCLUDE umfpack_load_numeric.h/r ../Include/umfpack_load_numeric.h +/INCLUDE umfpack_load_symbolic.h/r ../Include/umfpack_load_symbolic.h +/INCLUDE umfpack_save_numeric.h/r ../Include/umfpack_save_numeric.h +/INCLUDE umfpack_save_symbolic.h/r ../Include/umfpack_save_symbolic.h diff --git a/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.sed2 b/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.sed2 new file mode 100644 index 0000000000..0df47d9fcb --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.sed2 @@ -0,0 +1,3 @@ +/[/][*]/d +/[*][/]/d +/INCLUDE umfpack/d diff --git a/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.stex b/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.stex new file mode 100644 index 0000000000..99dc586642 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Doc/UserGuide.stex @@ -0,0 +1,2554 @@ +%------------------------------------------------------------------------------- +% The UserGuide.stex file. Processed into UserGuide.tex via sed. +%------------------------------------------------------------------------------- + +\documentclass[11pt]{article} + +\newcommand{\m}[1]{{\bf{#1}}} % for matrices and vectors +\newcommand{\tr}{^{\sf T}} % transpose +\newcommand{\he}{^{\sf H}} % complex conjugate transpose +\newcommand{\implies}{\rightarrow} + +\topmargin 0in +\textheight 9in +\oddsidemargin 0pt +\evensidemargin 0pt +\textwidth 6.5in + +\begin{document} + +\author{Timothy A. Davis \\ +Dept. of Computer and Information Science and Engineering \\ +Univ. of Florida, Gainesville, FL} +\title{UMFPACK Version 4.3 User Guide} +\date{Jan. 16, 2004} +\maketitle + +%------------------------------------------------------------------------------- +\begin{abstract} + UMFPACK is a set of routines for solving unsymmetric sparse linear + systems, $\m{Ax}=\m{b}$, using the Unsymmetric MultiFrontal method + and direct sparse LU factorization. It is written in ANSI/ISO C, with a + MATLAB interface. UMFPACK relies on the Level-3 Basic + Linear Algebra Subprograms (dense matrix multiply) for its performance. + This code works on Windows and many versions of Unix (Sun Solaris, + Red Hat Linux, IBM AIX, SGI IRIX, and Compaq Alpha). +\end{abstract} +%------------------------------------------------------------------------------- + +Technical Report TR-04-003. + +UMFPACK Version 4.3 (Jan. 16, 2004), Copyright\copyright 2004 by Timothy A. +Davis. All Rights Reserved. + +{\bf UMFPACK License:} + Your use or distribution of UMFPACK or any modified version of + UMFPACK implies that you agree to this License. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY + EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to use or copy this program, provided + that the Copyright, this License, and the Availability of the original + version is retained on all copies. User documentation of any code that + uses UMFPACK or any modified version of UMFPACK code must cite the + Copyright, this License, the Availability note, and ``Used by permission.'' + Permission to modify the code and to distribute modified code is granted, + provided the Copyright, this License, and the Availability note are + retained, and a notice that the code was modified is included. This + software was developed with support from the National Science Foundation, + and is provided to you free of charge. + +{\bf Availability:} + http://www.cise.ufl.edu/research/sparse/umfpack + +{\bf Acknowledgments:} + + This work was supported by the National Science Foundation, under + grants DMS-9504974, DMS-9803599, and CCR-0203270. + The upgrade to Version 4.1 and the inclusion of the + symmetric and 2-by-2 pivoting strategies + were done while the author was on sabbatical at + Stanford University and Lawrence Berkeley National Laboratory. + +%------------------------------------------------------------------------------- +\newpage +%------------------------------------------------------------------------------- + +\tableofcontents + +%------------------------------------------------------------------------------- +\newpage +\section{Overview} +%------------------------------------------------------------------------------- + +UMFPACK\footnote{Pronounced with two syllables: umph-pack} +Version 4.3 is a set of routines for solving systems of linear +equations, $\m{Ax}=\m{b}$, when $\m{A}$ is sparse and unsymmetric. It is based +on the Unsymmetric-pattern MultiFrontal method \cite{DavisDuff97,DavisDuff99}. +UMFPACK factorizes +$\m{PAQ}$, $\m{PRAQ}$, or $\m{PR}^{-1}\m{AQ}$ into the product $\m{LU}$, +where $\m{L}$ and $\m{U}$ +are lower and upper triangular, respectively, $\m{P}$ and $\m{Q}$ are +permutation matrices, and $\m{R}$ is a diagonal matrix of row scaling factors +(or $\m{R}=\m{I}$ if row-scaling is not used). Both $\m{P}$ and $\m{Q}$ are +chosen to reduce fill-in (new nonzeros in $\m{L}$ and $\m{U}$ that are not +present in $\m{A}$). The permutation $\m{P}$ has the dual role of reducing +fill-in and maintaining numerical accuracy (via relaxed partial pivoting +and row interchanges). + +The sparse matrix $\m{A}$ can be square or rectangular, singular +or non-singular, and real or complex (or any combination). Only square +matrices $\m{A}$ can be used to solve $\m{Ax}=\m{b}$ or related systems. +Rectangular matrices can only be factorized. + +UMFPACK first finds a column pre-ordering that reduces fill-in, without regard +to numerical values. It scales and analyzes the matrix, and then automatically +selects one of three strategies for pre-ordering the rows and columns: +{\em unsymmetric}, +{\em 2-by-2}, and +{\em symmetric}. These strategies are described below. + +First, all pivots with zero Markowitz cost are eliminated and placed in the +LU factors. The remaining submatrix $\m{S}$ is then analyzed. +The following rules are applied, and the first one that matches defines +the strategy. + +\begin{itemize} +\item Rule 1: $\m{A}$ rectangular $\implies$ unsymmetric. +\item Rule 2: + If the zero-Markowitz elimination results in a rectangular $\m{S}$, + or an $\m{S}$ whose diagonal has not been preserved, the + unsymmetric strategy is used. +\item The symmetry $\sigma_1$ of $\m{S}$ is computed. It is defined as + the number of {\em matched} off-diagonal entries, divided by the + total number of off-diagonal entries. An entry $s_{ij}$ is matched + if $s_{ji}$ is also an entry. They need not be numerically equal. + An {\em entry} is a value in $\m{A}$ which is present + in the input data structure. All nonzeros are entries, but some entries + may be numerically zero. + Rule 3: $\sigma_1 < 0.1 \implies$ unsymmetric. + The matrix is very unsymmetric. +\item Let $d$ be the number of nonzero entries on the diagonal of $\m{S}$. + Let $\m{S}$ be $\nu$-by-$\nu$. + Rule 4: $(\sigma_1 \ge 0.7) \:\wedge\: (d = \nu) \implies$ symmetric. + The matrix has a nearly symmetric nonzero pattern, and a zero-free + diagonal. +\end{itemize} + +If the strategy has not yet been determined, +the 2-by-2 strategy is attempted. A row permutation $\m{P}_2$ +is found which attempts to reduce the number of small +diagonal entries of $\m{P}_2 \m{S}$. +An entry $s_{ij}$ is determined to be small if +$|s_{ij}| < 0.01 \max |s_{*j}|$, or large otherwise. +If $s_{ii}$ is numerically small, the method attempts to swap +two rows $i$ and $j$, such that both $s_{ij}$ and $s_{ji}$ are large. +Once these rows are swapped, +they remain in place. Let $\sigma_2$ be the symmetry of $\m{P}_2 \m{S}$, +and let $d_2$ be the number of nonzero entries (either small or large) +on the diagonal of $\m{P}_2 \m{S}$. + +\begin{itemize} +\item Rule 5: + ($\sigma_2 > 1.1 \sigma_1) \:\wedge\: (d_2 > 0.9 \nu) \implies$ 2-by-2. + The 2-by-2 permutation has made the matrix significantly more symmetric. +\item Rule 6: $\sigma_2 < 0.7 \sigma_1 \implies$ unsymmetric. + The 2-by-2 strategy has significantly deteriorated the symmetry, +\item Rule 7: $\sigma_2 < 0.25 \implies$ unsymmetric. + The matrix is still very unsymmetric. +\item Rule 8: $\sigma_2 \ge 0.51 \implies$ 2-by-2. + The matrix is roughly symmetric. +\item Rule 9: $\sigma_2 \ge 0.999 \sigma_1 \implies$ 2-by-2. + The 2-by-2 permutation has preserved symmetry, or made it only + slightly worse. +\item Rule 10: if no rule has yet triggered, use the unsymmetric strategy. +\end{itemize} + +Each strategy is described below: +\begin{itemize} +\item {\em unsymmetric}: +The column pre-ordering of $\m{S}$ is computed by a modified version of COLAMD +\cite{DavisGilbertLarimoreNg00_algo,DavisGilbertLarimoreNg00,Larimore98}. +The method finds a symmetric permutation $\m{Q}$ of the matrix $\m{S}\tr\m{S}$ +(without forming $\m{S}\tr\m{S}$ explicitly). This is a good choice for +$\m{Q}$, since the Cholesky factors of $\m{(SQ)\tr(SQ)}$ are an upper bound (in +terms of nonzero pattern) of the factor $\m{U}$ for the unsymmetric LU +factorization ($\m{PSQ}=\m{LU}$) regardless of the choice of $\m{P}$ +\cite{GeorgeNg85,GeorgeNg87,GilbertNg93}. This modified version of +COLAMD also computes the column elimination tree and post-orders the +tree. It finds the upper bound on the number of nonzeros in L and U. +It also has a different threshold for determining dense rows and columns. +During factorization, the column pre-ordering can be modified. +Columns within a single super-column can be reshuffled, to reduce fill-in. +Threshold partial pivoting is used with no preference given to the diagonal +entry. Within a given pivot column $j$, an entry $a_{ij}$ can be chosen if +$|a_{ij}| \ge 0.1 \max |a_{*j}|$. Among those numerically acceptable +entries, the sparsest row $i$ is chosen as the pivot row. + +\item {\em 2-by-2}: +The symmetric strategy (see below) is applied to the matrix $\m{P}_2 \m{S}$, +rather than $\m{S}$. + +\item {\em symmetric}: +The column ordering is computed from AMD +\cite{AmestoyDavisDuff96,AmestoyDavisDuff03}, +applied to the pattern of $\m{S}+\m{S}\tr$ +followed by a post-ordering of the supernodal elimination +tree of $\m{S}+\m{S}\tr$. +No modification of the column pre-ordering is made during numerical +factorization. Threshold partial pivoting is used, with a strong +preference given to the diagonal entry. The diagonal entry is chosen if +$a_{jj} \ge 0.001 \max |a_{*j}|$. Otherwise, a sparse row is selected, +using the same method used by the unsymmetric strategy. + +\end{itemize} + +The symmetric and 2-by-2 strategies, and their automatic selection, +are new to Version 4.1. Version 4.0 only used the unsymmetric strategy. + +Once the strategy is selected, +the factorization of the matrix $\m{A}$ is broken down into the factorization +of a sequence of dense rectangular frontal matrices. The frontal matrices are +related to each other by a supernodal column elimination tree, in which each +node in the tree represents one frontal matrix. This analysis phase also +determines upper bounds on the memory usage, the floating-point operation count, +and the number of nonzeros in the LU factors. + +UMFPACK factorizes each {\em chain} of frontal matrices in a single working +array, similar to how the unifrontal method \cite{dusc:96} factorizes the whole +matrix. A chain of frontal matrices is a sequence of fronts where the parent +of front $i$ is $i$+1 in the supernodal column elimination tree. For the +nonsingular matrices factorized with the unsymmetric strategy, there are +exactly the same number of chains as there are leaves in the supernodal +column elimination tree. UMFPACK is an +outer-product based, right-looking method. At the $k$-th step of Gaussian +elimination, it represents the updated submatrix $\m{A}_k$ as an implicit +summation of a set of dense sub-matrices (referred to as {\em elements}, +borrowing a phrase from finite-element methods) that arise when the frontal +matrices are factorized and their pivot rows and columns eliminated. + +Each frontal matrix represents the elimination of one or more columns; +each column of $\m{A}$ will be eliminated in a specific frontal matrix, +and which frontal matrix will be used for which column is determined by +the pre-analysis phase. The pre-analysis phase also determines the worst-case +size of each frontal matrix so that they can hold any candidate pivot column +and any candidate pivot row. From the perspective of the analysis phase, any +candidate pivot column in the frontal matrix is identical (in terms of nonzero +pattern), and so is any row. However, the numeric factorization phase has +more information than the analysis phase. It uses this information to reorder +the columns within each frontal matrix to reduce fill-in. Similarly, since +the number of nonzeros in each row and column are maintained (more precisely, +COLMMD-style approximate degrees \cite{GilbertMolerSchreiber}), a pivot row can +be selected based on sparsity-preserving criteria (low degree) as well as +numerical considerations (relaxed threshold partial pivoting). + +When the symmetric or 2-by-2 strategies are used, +the column preordering is not refined during numeric factorization. +Row pivoting for sparsity and numerical accuracy is performed if the +diagonal entry is too small. + +More details of the method, including experimental results, are +described in \cite{Davis03,Davis03}, available at +http://www.cise.ufl.edu/tech-reports. + +%------------------------------------------------------------------------------- +\section{Availability} +%------------------------------------------------------------------------------- + +In addition to appearing as a Collected Algorithm of the ACM, +UMFPACK Version 4.3 is available at http://www.cise.ufl.edu/research/sparse. +An earlier version (Version 4.0) is included as a built-in routine in MATLAB +6.5. Version 4.0 does not have the symmetric or 2-by-2 strategies and it takes +less advantage of the level-3 +BLAS \cite{DaydeDuff99,ACM679a,ATLAS,GotoVandeGeijn02}. +Version 4.3 (and v4.2 and v4.1) tend to be much faster than Version 4.0, +particularly on unsymmetric matrices with mostly symmetric +nonzero pattern (such as finite element and circuit simulation matrices). +Version 3.0 and following make +use of a modified version of COLAMD V2.0 by Timothy A.~Davis, Stefan +Larimore, John Gilbert, and Esmond Ng. The original COLAMD V2.1 is available in +as a built-in routine in MATLAB V6.0 (or later), and at +http://www.cise.ufl.edu/research/sparse. +These codes are also available in Netlib \cite{netlib} at +http://www.netlib.org. +UMFPACK Versions 2.2.1 and earlier, co-authored with Iain Duff, +are available at http://www.cise.ufl.edu/research/sparse and as +MA38 (functionally equivalent to Version 2.2.1) in the Harwell +Subroutine Library. + +%------------------------------------------------------------------------------- +\section{Primary changes since Version 4.1} +%------------------------------------------------------------------------------- + +No changes are visible to the C or MATLAB user, except the presence of +one new control parameter in the {\tt Control} array, +and three new statistics in the {\tt Info} array. +The primary change is the addition of an (optional) drop tolerance. + +%------------------------------------------------------------------------------- +\section{Primary changes in Version 4.1 (since Version 4.0)} +%------------------------------------------------------------------------------- + +A detailed list of changes is in the {\tt ChangeLog} file. The following +is a summary of the main changes that are visible to the C or MATLAB user: + +\begin{enumerate} + +\item New ordering strategies added. No changes are required in user code + (either C or MATLAB) to use the new default strategy, which is an automatic + selection of the unsymmetric, symmetric, or 2-by-2 strategies. + +\item Row scaling added. This is only visible to the MATLAB caller when using + the form {\tt [L,U,P,Q,R] = umfpack (A)}, to retrieve the LU factors. + Likewise, it is only visible to the C caller when the LU factors are + retrieved, or when solving systems with just $\m{L}$ or $\m{U}$. + New C-callable and MATLAB-callable routines are included to get and to + apply the scale factors computed by UMFPACK. Row scaling is enabled by + default, but can be disabled. Row scaling usually leads to a better + factorization, particularly when the symmetric strategy is used. + +\item Error code {\tt UMFPACK\_ERROR\_problem\_to\_large} removed. + Version 4.0 would generate this error when the upper bound memory usage + exceeded 2GB (for the {\tt int} version), even when the actual memory + usage was less than this. The new version properly handles this case, + and can successfully factorize the matrix if sufficient memory is + available. + +\item New control parameters and statistics provided. + +\item The AMD symmetric approximate minimum degree ordering routine added + \cite{AmestoyDavisDuff96,AmestoyDavisDuff03}. + It is used by UMFPACK, and can also be called independently from C or + MATLAB. + +\item The {\tt umfpack} mexFunction now returns permutation matrices, not + permutation vectors, when using the form {\tt [L,U,P,Q] = umfpack (A)} + or the new form {\tt [L,U,P,Q,R] = umfpack (A)}. + +\item New arguments added to the user-callable routines + {\tt umfpack\_*\_symbolic}, + {\tt umfpack\_*\_qsymbolic}, + {\tt umfpack\_*\_get\_numeric}, and + {\tt umfpack\_*\_get\_symbolic}. + The symbolic analysis now makes use of the numerical values of the matrix + $\m{A}$, to guide the 2-by-2 strategy. The subsequent matrix passed to + the numeric factorization step does not have to have the same numerical + values. All of the new arguments are optional. If you do not wish to + include them, simply pass {\tt NULL} pointers instead. The 2-by-2 strategy + will assume all entries are numerically large, for example. + +\item New routines added to save and load the {\tt Numeric} and {\tt Symbolic} + objects to and from a binary file. + +\item A Fortran interface added. It provides access to a subset of + UMFPACK's features. + +\item You can compute an incomplete LU factorization, by dropping small + entries from $\m{L}$ and $\m{U}$. By default, no nonzero entry is + dropped, no matter how small in absolute value. This feature is new + to Version 4.3. + +\end{enumerate} + +%------------------------------------------------------------------------------- +\section{Using UMFPACK in MATLAB} +%------------------------------------------------------------------------------- + +The easiest way to use UMFPACK is within MATLAB. Version 4.0 is a built-in +routine in MATLAB 6.5, and is used in {\tt x = A}$\backslash${\tt b} when +{\tt A} is sparse, square, unsymmetric (or symmetric but not positive definite), +and with nonzero entries that are not confined in a narrow band. +It is also used for the {\tt [L,U,P,Q] = lu (A)} usage of {\tt lu}. +Type {\tt help lu} in MATLAB 6.5 or later for more details. + +To use the UMFPACK mexFunction, you must download and compile it, +since the mexFunction itself is not part of MATLAB. +The following discussion assumes that +you have MATLAB Version 6.0 or later (which includes the BLAS, and the +{\tt colamd} ordering routine). To compile both the UMFPACK and AMD +mexFunctions, just type {\tt make} in the Unix system shell, +while in the {\tt UMFPACK} directory. +You can also type {\tt umfpack\_make} in MATLAB, if you are in the +{\tt UMFPACK/MATLAB} directory, or if that directory is in your MATLAB path. +This works on any system with MATLAB, including Windows. +See Section~\ref{Install} for more details on how to install UMFPACK. +Once installed, the UMFPACK mexFunction can analyze, factor, and solve linear +systems. Table~\ref{matlab} summarizes some of the more common uses +of the UMFPACK mexFunction within MATLAB. + +An optional input argument can be used to modify the control parameters for +UMFPACK, and an optional output argument provides statistics on the +factorization. UMFPACK will print these statistics if you turn +on the sparse matrix monitor flag with {\tt spparms ('spumoni', 1)}. +The UMFPACK printing level parameter described in Section~\ref{Reporting} +is set to {\tt spumoni} plus 1. Type {\tt help umfpack} for more information. + +Refer to the AMD User Guide for more details about the AMD mexFunction. + +\begin{table} +\caption{Using UMFPACK's MATLAB interface} +\label{matlab} +\vspace{0.1in} +{\footnotesize +\begin{tabular}{l|l|l} +\hline +Function & Using UMFPACK & MATLAB 6.0 equivalent \\ +\hline + & & \\ +\begin{minipage}[t]{1.5in} +Solve $\m{Ax}=\m{b}$. +\end{minipage} +& +\begin{minipage}[t]{2.2in} +\begin{verbatim} +x = umfpack (A,'\',b) ; +\end{verbatim} +\end{minipage} +& +\begin{minipage}[t]{2.2in} +\begin{verbatim} +x = A \ b ; +\end{verbatim} +\end{minipage} + \\ + & & \\ +\hline + & & \\ +\begin{minipage}[t]{1.5in} +Solve $\m{Ax}=\m{b}$ using a different column pre-ordering. +\end{minipage} +& +\begin{minipage}[t]{2.2in} +\begin{verbatim} +S = spones (A) ; +Q = symamd (S+S') ; +x = umfpack (A,Q,'\',b) ; +\end{verbatim} +\end{minipage} +& +\begin{minipage}[t]{2.2in} +\begin{verbatim} +spparms ('autommd',0) ; +S = spones (A) ; +Q = symamd (S+S') ; +x = A (:,Q) \ b ; +x (Q) = x ; +spparms ('autommd',1) ; +\end{verbatim} +\end{minipage} + \\ + & & \\ +\hline + & & \\ +\begin{minipage}[t]{1.5in} +Solve $\m{A}\tr\m{x}\tr = \m{b}\tr$. +\end{minipage} +& +\begin{minipage}[t]{2.2in} +\begin{verbatim} +x = umfpack (b,'/',A) ; +\end{verbatim} +\end{minipage} +& +\begin{minipage}[t]{2.2in} +\begin{verbatim} +x = b / A ; +\end{verbatim} +\end{minipage} + \\ + & & \\ +\hline + & & \\ +\begin{minipage}[t]{1.5in} +Scale and factorize $\m{A}$, then solve $\m{Ax}=\m{b}$. +\end{minipage} +& +\begin{minipage}[t]{2.2in} +\begin{verbatim} +[L,U,P,Q,R] = umfpack (A) ; +c = P * (R \ b) ; +x = Q * (U \ (L \ c)) ; +\end{verbatim} +\end{minipage} +& +\begin{minipage}[t]{2.2in} +\begin{verbatim} +[m n] = size (A) ; +r = full (sum (abs (A), 2)) ; +r (find (r == 0)) = 1 ; +R = spdiags (r, 0, m, m) ; +I = speye (n) ; +Q = I (:, colamd (A)) ; +[L,U,P] = lu ((R\A)*Q) ; +c = P * (R \ b) ; +x = Q * (U \ (L \ c)) ; +\end{verbatim} +\end{minipage} + \\ + & & \\ +\hline +\end{tabular} +} +\end{table} + +Note: in MATLAB 6.5, use {\tt spparms ('autoamd',0)} in addition to +{\tt spparms ('autommd',0)}, in Table~\ref{matlab}, to turn off MATLAB's +default reordering. + +UMFPACK requires +{\tt b} to be a dense vector (real or complex) of the appropriate dimension. +This is more restrictive than what you can do with MATLAB's +backslash or forward slash. See {\tt umfpack\_solve} for an M-file that +removes this restriction. +This restriction does not apply to the built-in backslash operator +in MATLAB 6.5, which uses UMFPACK v4.0 to factorize the matrix. +You can do this yourself in MATLAB: + +{\footnotesize +\begin{verbatim} + [L,U,P,Q,R] = umfpack (A) ; + x = Q * (U \ (L \ (P * (R \ b)))) ; +\end{verbatim} +} + +or, with no row scaling: + +{\footnotesize +\begin{verbatim} + [L,U,P,Q] = umfpack (A) ; + x = Q * (U \ (L \ (P * b))) ; +\end{verbatim} +} + +The above examples do not make use of the iterative refinement +that is built into +{\tt x = }{\tt umfpack (A,'}$\backslash${\tt ',b)} +however. + +MATLAB's {\tt [L,U,P] = lu(A)} returns a lower triangular {\tt L}, an upper +triangular {\tt U}, and a permutation matrix {\tt P} such that {\tt P*A} is +equal to {\tt L*U}. UMFPACK behaves differently. By default, it scales +the rows of {\tt A} and reorders the columns of {\tt A} prior to +factorization, so that {\tt L*U} is equal to {\tt P*(R}$\backslash${\tt A)*Q}, +where {\tt R} is a diagonal sparse matrix of scale factors for the rows +of {\tt A}. The scale factors {\tt R} are applied to {\tt A} via the MATLAB +expression {\tt R}$\backslash${\tt A} to avoid multiplying by +the reciprocal, which can be numerically inaccurate. + +There are more options; you can provide your own column pre-ordering (in which +case UMFPACK does not call COLAMD or AMD), you can modify other control settings +(similar to the {\tt spparms} in MATLAB), and you can get various statistics on +the analysis, factorization, and solution of the linear system. Type +{\tt umfpack\_details} and {\tt umfpack\_report} in MATLAB for more +information. Two demo M-files are provided. Just type {\tt umfpack\_simple} +and {\tt umfpack\_demo} to run them. +The output of these two programs should be about the same +as the files {\tt umfpack\_simple.m.out} and {\tt umfpack\_demo.m.out} +that are provided. + +Factorizing {\tt A'} (or {\tt A.'}) and using the transposed factors can +sometimes be faster than factorizing {\tt A}. It can also be preferable to +factorize {\tt A'} if {\tt A} is rectangular. UMFPACK pre-orders the columns +to maintain sparsity; the row ordering is not determined until the matrix +is factorized. Thus, if {\tt A} is {\tt m} by {\tt n} with rank {\tt m} +and {\tt m} $<$ {\tt n}, then {\tt umfpack} might not find a factor +{\tt U} with a zero-free diagonal. Unless the matrix ill-conditioned or +poorly scaled, factorizing {\tt A'} in this case will guarantee that both +factors will have zero-free diagonals. Here's how you can factorize {\tt A'} +and get the factors of {\tt A} instead: + +\begin{verbatim} + [l,u,p,q] = umfpack (A') ; + L = u' ; + U = l' ; + P = q ; + Q = p ; + clear l u p q +\end{verbatim} + +This is an alternative to {\tt [L,U,P,Q]=umfpack(A)}. + +A simple M-file ({\tt umfpack\_btf}) is provided that first permutes the matrix +to upper block triangular form, using MATLAB's {\tt dmperm} routine, and then +solves each block. The LU factors are not returned. Its usage is simple: +{\tt x = umfpack\_btf(A,b)}. Type {\tt help umfpack\_btf} for more options. +An estimate of the 1-norm of {\tt L*U-P*A*Q} can be computed in MATLAB +as {\tt lu\_normest(P*A*Q,L,U)}, using the {\tt lu\_normest.m} M-file +by Hager and Davis \cite{DavisHager99} that is included with the +UMFPACK distribution. With row scaling enabled, use +{\tt lu\_normest(P*(R}$\backslash${\tt A)*Q,L,U)} instead. + +One issue you may encounter is how UMFPACK allocates its memory when being used +in a mexFunction. One part of its working space is of variable size. The +symbolic analysis phase determines an upper bound on the size of this memory, +but not all of this memory will typically be used in the numerical +factorization. UMFPACK tries to allocate a decent amount of working space. +This is 70\% of the upper bound, by default, for the unsymmetric strategy. +For the symmetric strategy, the fraction of the upper bound is computed +automatically (assuming a best-case scenario with no numerical pivoting +required during numeric factorization). +If this initial allocation fails, it reduces its request +and uses less memory. If the space is not large enough during factorization, +it is increased via {\tt mxRealloc}. + +However, {\tt mxMalloc} and {\tt mxRealloc} abort the {\tt umfpack} mexFunction +if they fail, so this strategy does not work in MATLAB. The strategy works fine +when {\tt malloc} or the internal memory allocator {\tt utMalloc} are used +instead, since those routines return {\tt NULL} on failure, and do not terminate +the mexFunction. The {\tt umfpack} mexFunction can be compiled to use +{\tt utMalloc}, but this is an internal undocumented utility routine in MATLAB, +and thus using {\tt utMalloc} might not always be successful. +To use the documented {\tt mxMalloc} routine instead, compile the +mexFunction with the {\tt -DNUTIL} flag enabled. + +%------------------------------------------------------------------------------- +\section{Using UMFPACK in a C program} +\label{C} +%------------------------------------------------------------------------------- + +The C-callable UMFPACK library consists of 31 user-callable routines and one +include file. Twenty-seven of the routines come in four versions, with +different sizes of integers and for real or complex floating-point numbers: +\begin{enumerate} +\item {\tt umfpack\_di\_*}: real double precision, {\tt int} integers. +\item {\tt umfpack\_dl\_*}: real double precision, {\tt long} integers. +\item {\tt umfpack\_zi\_*}: complex double precision, {\tt int} integers. +\item {\tt umfpack\_zl\_*}: complex double precision, {\tt long} integers. +\end{enumerate} +where {\tt *} denotes the specific name of one of the 27 routines. +Routine names beginning with {\tt umf\_} are internal to the package, +and should not be called by the user. The include file {\tt umfpack.h} +must be included in any C program that uses UMFPACK. +The other three routines are the same for all four versions. + +In addition, the C-callable AMD library distributed with UMFPACK +includes 4 user-callable routines (in two versions with {\tt int} and +{\tt long} integers) and one include file. Refer to the AMD documentation +for more details. + +Use only one version for any one problem; do not attempt to use one version +to analyze the matrix and another version to factorize the matrix, for example. + +The notation {\tt umfpack\_di\_*} refers to all 27 user-callable routines +for the real double precision and {\tt int} integer case. The notation +{\tt umfpack\_*\_numeric}, for example, refers all four versions +(real/complex, int/long) of a single operation +(in this case numeric factorization). + +%------------------------------------------------------------------------------- +\subsection{The size of an integer} +%------------------------------------------------------------------------------- + +The {\tt umfpack\_di\_*} and {\tt umfpack\_zi\_*} routines use {\tt int} integer +arguments; those starting with {\tt umfpack\_dl\_} or {\tt umfpack\_zl\_} +use {\tt long} integer arguments. If you compile UMFPACK in the standard +ILP32 mode (32-bit {\tt int}'s, {\tt long}'s, and pointers) then the versions +are essentially identical. You will be able to solve problems using up to 2GB +of memory. If you compile UMFPACK in the standard LP64 mode, the size of an +{\tt int} remains 32-bits, but the size of a {\tt long} and a pointer both get +promoted to 64-bits. In the LP64 mode, the {\tt umfpack\_dl\_*} +and {\tt umfpack\_zl\_*} routines can solve huge +problems (not limited to 2GB), limited of course by the amount of available +memory. The only drawback to the 64-bit mode is that not all BLAS libraries +support 64-bit integers. This limits the performance you will obtain. +Those that do support 64-bit integers are specific to particular +architectures, and are not portable. UMFPACK and AMD should be compiled +in the same mode. +If you compile UMFPACK and AMD in the LP64 mode, +be sure to add {\tt -DLP64} to the compilation command. See the examples in +{\tt Make.alpha}, {\tt Make.sgi}, and {\tt Make.solaris}. + +%------------------------------------------------------------------------------- +\subsection{Real and complex floating-point} +%------------------------------------------------------------------------------- + +The {\tt umfpack\_di\_*} and {\tt umfpack\_dl\_*} routines take (real) double +precision arguments, and return double precision arguments. In the +{\tt umfpack\_zi\_*} and {\tt umfpack\_zl\_*} routines, these same arguments +hold the real part of the matrices; and second double precision arrays hold +the imaginary part of the input and output matrices. Internally, complex +numbers are stored in arrays with their real and imaginary parts interleaved, +as required by the BLAS. + +%------------------------------------------------------------------------------- +\subsection{Primary routines, and a simple example} +%------------------------------------------------------------------------------- + +Five primary UMFPACK routines are required to factorize $\m{A}$ or +solve $\m{Ax}=\m{b}$. They are fully described in Section~\ref{Primary}: + +\begin{itemize} +\item {\tt umfpack\_*\_symbolic}: + + Pre-orders the columns of $\m{A}$ to reduce fill-in. + Returns an opaque {\tt Symbolic} object as a {\tt void *} + pointer. The object contains the symbolic analysis and is needed for the + numeric factorization. This routine requires only $O(|\m{A}|)$ space, + where $|\m{A}|$ is the number of nonzero entries in the matrix. It computes + upper bounds on the nonzeros in $\m{L}$ and $\m{U}$, the floating-point + operations required, and the memory usage of {\tt umfpack\_*\_numeric}. The + {\tt Symbolic} object is small; it contains just the column pre-ordering, + the supernodal column elimination tree, and information about each frontal + matrix. It is no larger than about $13n$ integers if $\m{A}$ is + $n$-by-$n$. + +\item {\tt umfpack\_*\_numeric}: + + Numerically scales and then factorizes a sparse matrix into + $\m{PAQ}$, $\m{PRAQ}$, or $\m{PR}^{-1}\m{AQ}$ into the product $\m{LU}$, + where + $\m{P}$ and $\m{Q}$ are permutation matrices, $\m{R}$ is a diagonal + matrix of scale factors, $\m{L}$ is lower triangular with unit diagonal, + and $\m{U}$ is upper triangular. Requires the + symbolic ordering and analysis computed by {\tt umfpack\_*\_symbolic} + or {\tt umfpack\_*\_qsymbolic}. + Returns an opaque {\tt Numeric} object as a + {\tt void *} pointer. The object contains the numerical factorization and + is used by {\tt umfpack\_*\_solve}. You can factorize a new matrix with a + different values (but identical pattern) as the matrix analyzed by + {\tt umfpack\_*\_symbolic} or {\tt umfpack\_*\_qsymbolic} by re-using the + {\tt Symbolic} object (this feature is available when using UMFPACK in a + C or Fortran program, but not in MATLAB). + The matrix + $\m{U}$ will have zeros on the diagonal if $\m{A}$ is singular; this + produces a warning, but the factorization is still valid. + +\item {\tt umfpack\_*\_solve}: + + Solves a sparse linear system ($\m{Ax}=\m{b}$, $\m{A}\tr\m{x}=\m{b}$, or + systems involving just $\m{L}$ or $\m{U}$), using the numeric factorization + computed by {\tt umfpack\_*\_numeric}. Iterative refinement with sparse + backward error \cite{ardd:89} is used by default. The matrix $\m{A}$ must + be square. If it is singular, then a divide-by-zero will occur, and your + solution with contain IEEE Inf's or NaN's in the appropriate places. + +\item {\tt umfpack\_*\_free\_symbolic}: + + Frees the {\tt Symbolic} object created by {\tt umfpack\_*\_symbolic} + or {\tt umfpack\_*\_qsymbolic}. + +\item {\tt umfpack\_*\_free\_numeric}: + + Frees the {\tt Numeric} object created by {\tt umfpack\_*\_numeric}. + +\end{itemize} + +Be careful not to free a {\tt Symbolic} object with +{\tt umfpack\_*\_free\_numeric}. Nor should you attempt to free a {\tt Numeric} +object with {\tt umfpack\_*\_free\_symbolic}. +Failure to free these objects will lead to memory leaks. + +The matrix $\m{A}$ is represented in compressed column form, which is +identical to the sparse matrix representation used by MATLAB. It consists +of three or four arrays, where the matrix is {\tt m}-by-{\tt n}, +with {\tt nz} entries. For the {\tt int} version of UMFPACK: + +{\footnotesize +\begin{verbatim} + int Ap [n+1] ; + int Ai [nz] ; + double Ax [nz] ; +\end{verbatim} +} + +For the {\tt long} version of UMFPACK: + +{\footnotesize +\begin{verbatim} + long Ap [n+1] ; + long Ai [nz] ; + double Ax [nz] ; +\end{verbatim} +} + +The complex versions add another array for the imaginary part: + +{\footnotesize +\begin{verbatim} + double Az [nz] ; +\end{verbatim} +} + +All nonzeros are entries, but an entry may be numerically zero. The row indices +of entries in column {\tt j} are stored in + {\tt Ai[Ap[j]} \ldots {\tt Ap[j+1]-1]}. +The corresponding numerical values are stored in + {\tt Ax[Ap[j]} \ldots {\tt Ap[j+1]-1]}. +The imaginary part, for the complex versions, is stored in + {\tt Az[Ap[j]} \ldots {\tt Ap[j+1]-1]}. + +No duplicate row indices may be present, and the row indices in any given +column must be sorted in ascending order. The first entry {\tt Ap[0]} must be +zero. The total number of entries in the matrix is thus {\tt nz = Ap[n]}. +Except for the fact that extra zero entries can be included, there is thus a +unique compressed column representation of any given matrix $\m{A}$. +For a more flexible method for providing an input matrix to UMFPACK, +see Section~\ref{triplet}. + +Here is a simple main program, {\tt umfpack\_simple.c}, that illustrates the +basic usage of UMFPACK. See Section~\ref{Synopsis} for a short description +of each calling sequence, including a list of options for the first +argument of {\tt umfpack\_di\_solve}. + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_simple.c via sed +\end{verbatim} +} + +The {\tt Ap}, {\tt Ai}, and {\tt Ax} arrays represent the matrix +\[ +\m{A} = \left[ +\begin{array}{rrrrr} + 2 & 3 & 0 & 0 & 0 \\ + 3 & 0 & 4 & 0 & 6 \\ + 0 & -1 & -3 & 2 & 0 \\ + 0 & 0 & 1 & 0 & 0 \\ + 0 & 4 & 2 & 0 & 1 \\ +\end{array} +\right]. +\] +and the solution to $\m{Ax}=\m{b}$ is $\m{x} = [1 \, 2 \, 3 \, 4 \, 5]\tr$. +The program uses default control settings and does not return any statistics +about the ordering, factorization, or solution ({\tt Control} and {\tt Info} +are both {\tt (double *) NULL}). It also ignores the status value returned by +most user-callable UMFPACK routines. + +%------------------------------------------------------------------------------- +\subsection{A note about zero-sized arrays} +%------------------------------------------------------------------------------- + +UMFPACK uses many user-provided arrays of +size {\tt m} or {\tt n} (the order of the matrix), and of size +{\tt nz} (the number of nonzeros in a matrix). UMFPACK does not handle +zero-dimensioned arrays; +it returns an error code if {\tt m} or {\tt n} +are zero. However, {\tt nz} can be zero, since all singular matrices are +handled correctly. If you attempt to {\tt malloc} an array of size {\tt nz} += 0, however, {\tt malloc} will return a null pointer which UMFPACK will report +as a missing argument. If you {\tt malloc} an array of +size {\tt nz} to pass to UMFPACK, make sure that you handle the {\tt nz} = 0 +case correctly (use a size equal to the maximum of {\tt nz} and 1, or use a +size of {\tt nz+1}). + +%------------------------------------------------------------------------------- +\subsection{Alternative routines} +%------------------------------------------------------------------------------- + +Three alternative routines are provided that modify UMFPACK's default +behavior. They are fully described in Section~\ref{Alternative}: + +\begin{itemize} +\item {\tt umfpack\_*\_defaults}: + + Sets the default control parameters in the {\tt Control} array. These can + then be modified as desired before passing the array to the other UMFPACK + routines. Control parameters are summarized in Section~\ref{control_param}. + Three particular parameters deserve special notice. + UMFPACK uses relaxed partial pivoting, where a candidate pivot entry is + numerically acceptable if its magnitude is greater than or equal to a + tolerance parameter times the magnitude of the largest entry in the same + column. The parameter {\tt Control [UMFPACK\_PIVOT\_TOLERANCE]} has a + default value of 0.1, and is used for the unsymmetric strategy. + For complex matrices, a cheap approximation of the absolute value is + used for the threshold pivoting test + ($|a| \approx |a_{\mbox{real}}|+|a_{\mbox{imag}}|$). + + For the symmetric strategy, a second tolerance is used for diagonal + entries: \newline {\tt Control [UMFPACK\_SYM\_PIVOT\_TOLERANCE]}, with + a default value of 0.001. The first parameter (with a default of 0.1) + is used for any off-diagonal candidate pivot entries. + + These two parameters may be too small for some matrices, particularly for + ill-conditioned or poorly scaled ones. With the default pivot tolerances + and default iterative refinement, + {\tt x = umfpack (A,'}$\backslash${\tt ',b)} + is just as accurate as (or more accurate) than + {\tt x = A}$\backslash${\tt b} + in MATLAB 6.1 for nearly all matrices. + + If {\tt Control [UMFPACK\_PIVOT\_TOLERANCE]} is zero, than any + nonzero entry is acceptable as a pivot (this is changed from Version 4.0, + which treated a value of 0.0 the same as 1.0). If the symmetric strategy is + used, and {\tt Control [UMFPACK\_SYM\_PIVOT\_TOLERANCE]} is zero, then any + nonzero entry on the diagonal is accepted as a pivot. Off-diagonal pivoting + will still occur if the diagonal entry is exactly zero. The + {\tt Control [UMFPACK\_SYM\_PIVOT\_TOLERANCE]} parameter is new to Version + 4.1. It is similar in function to the pivot tolerance for left-looking + methods (the MATLAB {\tt THRESH} option in {\tt [L,U,P] = lu (A, THRESH)}, + and the pivot tolerance parameter in SuperLU). + + The parameter {\tt Control [UMFPACK\_STRATEGY]} can be used to bypass + UMFPACK's automatic strategy selection. The automatic strategy nearly + always selects the best method. When it does not, the different methods + nearly always give about the same quality of results. There may be + cases where the automatic strategy fails to pick a good strategy. Also, + you can save some computing time if you know the right strategy for your + set of matrix problems. + +\item {\tt umfpack\_*\_qsymbolic}: + + An alternative to {\tt umfpack\_*\_symbolic}. Allows the user to specify + his or her own column pre-ordering, rather than using the default COLAMD + or AMD pre-orderings. For example, a graph partitioning-based order + of $\m{A}\tr\m{A}$ would be suitable for UMFPACK's unsymmetric strategy. + A partitioning of $\m{A}+\m{A}\tr$ would be suitable for UMFPACK's + symmetric or 2-by-2 strategies. + +\item {\tt umfpack\_*\_wsolve}: + + An alternative to {\tt umfpack\_*\_solve} which does not dynamically + allocate any memory. Requires the user to pass two additional work + arrays. + +\end{itemize} + +%------------------------------------------------------------------------------- +\subsection{Matrix manipulation routines} +\label{triplet} +%------------------------------------------------------------------------------- + +The compressed column data structure is compact, and simplifies the UMFPACK +routines that operate on the sparse matrix $\m{A}$. However, it can be +inconvenient for the user to generate. Section~\ref{Manipulate} presents the +details of routines for manipulating sparse matrices in {\em triplet} form, +compressed column form, and compressed row form (the transpose of the +compressed column form). The triplet form of a matrix consists of three or +four arrays. For the {\tt int} version of UMFPACK: + +{\footnotesize +\begin{verbatim} + int Ti [nz] ; + int Tj [nz] ; + double Tx [nz] ; +\end{verbatim} +} + +For the {\tt long} version: + +{\footnotesize +\begin{verbatim} + long Ti [nz] ; + long Tj [nz] ; + double Tx [nz] ; +\end{verbatim} +} + +The complex versions use another array to hold the imaginary part: + +{\footnotesize +\begin{verbatim} + double Tz [nz] ; +\end{verbatim} +} + +The {\tt k}-th triplet is $(i,j,a_{ij})$, where $i =$ {\tt Ti[k]}, +$j =$ {\tt Tj[k]}, and $a_{ij} =$ {\tt Tx[k]}. For the complex versions, +{\tt Tx[k]} is the real part of $a_{ij}$ and +{\tt Tz[k]} is the imaginary part. +The triplets can be in any +order in the {\tt Ti}, {\tt Tj}, and {\tt Tx} arrays (and {\tt Tz} for +the complex versions), and duplicate entries may +exist. Any duplicate entries are summed when the triplet form is converted to +compressed column form. This is a convenient way to create a matrix arising in +finite-element methods, for example. + +Four routines are provided for manipulating sparse matrices: + +\begin{itemize} +\item {\tt umfpack\_*\_triplet\_to\_col}: + + Converts a triplet form of a matrix to compressed column form (ready for + input to \newline + {\tt umfpack\_*\_symbolic}, {\tt umfpack\_*\_qsymbolic}, and + {\tt umfpack\_*\_numeric}). Identical to {\tt A = spconvert(i,j,x)} in + MATLAB, except that zero entries are not removed, so that the pattern of + entries in the compressed column form of $\m{A}$ are fully under user + control. This is important if you want to factorize a new matrix with the + {\tt Symbolic} object from a prior matrix with the same pattern as the new + one. + +\item {\tt umfpack\_*\_col\_to\_triplet}: + + The opposite of {\tt umfpack\_*\_triplet\_to\_col}. Identical to + {\tt [i,j,x] = find(A)} in MATLAB, except that numerically zero entries + may be included. + +\item {\tt umfpack\_*\_transpose}: + + Transposes and optionally permutes a column form matrix \cite{Gustavson78}. + Identical to + {\tt R = A(P,Q)'} (linear algebraic transpose, using the complex conjugate) + or {\tt R = A(P,Q).'} (the array transpose) + in MATLAB, except for the presence of numerically zero entries. + + Factorizing $\m{A}\tr$ and then solving $\m{Ax}=\m{b}$ with the transposed + factors can sometimes be much faster or much slower than factorizing + $\m{A}$. It is highly dependent on your particular matrix. + +\item {\tt umfpack\_*\_scale}: + + Applies the row scale factors to a user-provided vector. This is not + required to solve the sparse linear system $\m{Ax}=\m{b}$ or + $\m{A}\tr\m{x}=\m{b}$, since {\tt umfpack\_*\_solve} applies the scale + factors for those systems. This routine is new to Version 4.1. + +\end{itemize} + +It is quite easy to add matrices in triplet form, subtract them, transpose +them, permute them, construct a submatrix, and multiply a triplet-form matrix +times a vector. UMFPACK does not provide code for these basic operations, +however. Refer to the discussion of +{\tt umfpack\_*\_triplet\_to\_col} in Section~\ref{Manipulate} for more details +on how to compute these operations in your own code. +The only primary matrix operation not provided by UMFPACK is the +multiplication of two sparse matrices \cite{Gustavson78}. + +%------------------------------------------------------------------------------- +\subsection{Getting the contents of opaque objects} +%------------------------------------------------------------------------------- + +There are cases where you may wish to do more with the LU factorization +of a matrix than solve a linear system. The opaque {\tt Symbolic} and +{\tt Numeric} objects are just that - opaque. You cannot do anything with them +except to pass them back to subsequent calls to UMFPACK. Three routines +are provided for copying their contents into user-provided arrays using simpler +data structures. Four routines are provided for saving and loading the +{\tt Numeric} and {\tt Symbolic} objects to/from binary files. +They are fully described in Section~\ref{Get}: + +\begin{itemize} +\item {\tt umfpack\_*\_get\_lunz}: + + Returns the number of nonzeros in $\m{L}$ and $\m{U}$. + +\item {\tt umfpack\_*\_get\_numeric}: + + Copies $\m{L}$, $\m{U}$, $\m{P}$, $\m{Q}$, and $\m{R}$ + from the {\tt Numeric} object + into arrays provided by the user. The matrix $\m{L}$ is returned in + compressed row form (with the column indices in each row sorted in ascending + order). The matrix $\m{U}$ is returned in compressed column form (with + sorted columns). There are no explicit zero entries in $\m{L}$ and $\m{U}$, + but such entries may exist in the {\tt Numeric} object. The permutations + $\m{P}$ and $\m{Q}$ are represented as permutation vectors, where + {\tt P[k] = i} means that row {\tt i} of the original matrix is the + the {\tt k}-th row of $\m{PAQ}$, and where + {\tt Q[k] = j} means that column {\tt j} of the original matrix is the + {\tt k}-th column of $\m{PAQ}$. This is identical to how MATLAB uses + permutation vectors (type {\tt help colamd} in MATLAB 6.1 or later). + +\item {\tt umfpack\_*\_get\_symbolic}: + + Copies the contents of the {\tt Symbolic} object (the initial row and column + preordering, supernodal column elimination tree, and information + about each frontal matrix) into arrays provided by the user. + +\item {\tt umfpack\_*\_save\_numeric}: + + Saves a copy of the {\tt Numeric} object to a file, in binary format. + +\item {\tt umfpack\_*\_load\_numeric}: + + Creates a {\tt Numeric} object by loading it from a file created + by {\tt umfpack\_*\_save\_numeric}. + +\item {\tt umfpack\_*\_save\_symbolic}: + + Saves a copy of the {\tt Symbolic} object to a file, in binary format. + +\item {\tt umfpack\_*\_load\_symbolic}: + + Creates a {\tt Symbolic} object by loading it from a file created + by {\tt umfpack\_*\_save\_symbolic}. + +\end{itemize} + +UMFPACK itself does not make use of these routines; +they are provided solely for returning the contents of the opaque +{\tt Symbolic} and {\tt Numeric} objects to the user, and saving/loading +them to/from a binary file. + +%------------------------------------------------------------------------------- +\subsection{Reporting routines} +\label{Reporting} +%------------------------------------------------------------------------------- + +None of the UMFPACK routines discussed so far prints anything, even when an +error occurs. UMFPACK provides you with nine routines for printing the input +and output arguments (including the {\tt Control} settings and {\tt Info} +statistics) of UMFPACK routines discussed above. They are fully described in +Section~\ref{Report}: + +\begin{itemize} +\item {\tt umfpack\_*\_report\_status}: + + Prints the status (return value) of other {\tt umfpack\_*} routines. + +\item {\tt umfpack\_*\_report\_info}: + + Prints the statistics returned in the {\tt Info} array by + {\tt umfpack\_*\_*symbolic}, + {\tt umfpack\_*\_numeric}, and {\tt umfpack\_*\_*solve}. + +\item {\tt umfpack\_*\_report\_control}: + + Prints the {\tt Control} settings. + +\item {\tt umfpack\_*\_report\_matrix}: + + Verifies and prints a compressed column-form or compressed row-form sparse + matrix. + +\item {\tt umfpack\_*\_report\_triplet}: + + Verifies and prints a matrix in triplet form. + +\item {\tt umfpack\_*\_report\_symbolic}: + + Verifies and prints a {\tt Symbolic} object. + +\item {\tt umfpack\_*\_report\_numeric}: + + Verifies and prints a {\tt Numeric} object. + +\item {\tt umfpack\_*\_report\_perm}: + + Verifies and prints a permutation vector. + +\item {\tt umfpack\_*\_report\_vector}: + + Verifies and prints a real or complex vector. + +\end{itemize} + +The {\tt umfpack\_*\_report\_*} routines behave slightly differently when +compiled +into the C-callable UMFPACK library than when used in the MATLAB mexFunction. +MATLAB stores its sparse matrices using the same compressed column data +structure discussed above, where row and column indices of an $m$-by-$n$ +matrix are in the range 0 to $m-1$ or $n-1$, respectively. +It prints them as if they are in the range 1 to $m$ or $n$. +The UMFPACK mexFunction behaves the same way. + +You can control how much the {\tt umfpack\_*\_report\_*} routines print by +modifying the {\tt Control [UMFPACK\_PRL]} parameter. Its default value is 1. +Here is a summary of how the routines use this print level parameter: + +\begin{itemize} +\item {\tt umfpack\_*\_report\_status}: + + No output if the print level is 0 or less, even when an error occurs. + If 1, then error messages are printed, and nothing is printed if + the status is {\tt UMFPACK\_OK}. A warning message is printed if + the matrix is singular. If 2 or more, then the status is always + printed. If 4 or more, then the UMFPACK Copyright is printed. + If 6 or more, then the UMFPACK License is printed. See also the first page + of this User Guide for the Copyright and License. + +\item {\tt umfpack\_*\_report\_control}: + + No output if the print level is 1 or less. If 2 or more, all of + {\tt Control} is printed. + +\item {\tt umfpack\_*\_report\_info}: + + No output if the print level is 1 or less. If 2 or more, all of + {\tt Info} is printed. + +\item all other {\tt umfpack\_*\_report\_*} routines: + + If the print level is 2 or less, then these routines return silently without + checking their inputs. If 3 or more, the inputs are fully verified and a + short status summary is printed. If 4, then the first few entries of the + input arguments are printed. If 5, then all of the input arguments are + printed. + +\end{itemize} + +This print level parameter has an additional effect on the MATLAB mexFunction. +If zero, then no warnings of singular or nearly singular matrices are +printed (similar to the MATLAB commands +{\tt warning off MATLAB:singularMatrix} and +{\tt warning off MATLAB:nearlySingularMatrix}). + +%------------------------------------------------------------------------------- +\subsection{Utility routines} +%------------------------------------------------------------------------------- + +UMFPACK v4.0 included a routine that returns the time used by the process, +{\tt umfpack\_timer}. The routine uses either {\tt getrusage} (which is +preferred), or the ANSI C {\tt clock} routine if that is not available. +It is fully described in Section~\ref{Utility}. It is still available in +UMFPACK v4.1 and following, but not used internally. +Two new timing routines are provided in UMFPACK Version 4.1 and following, +{\tt umfpack\_tic} and {\tt umfpack\_toc}. They use POSIX-compliant +{\tt sysconf} and {\tt times} routines to find both the CPU time +and wallclock time. +These three routines are the only user-callable +routine that is identical in all four {\tt int}/{\tt long}, real/complex +versions (there is no {\tt umfpack\_di\_timer} routine, for example). + +%------------------------------------------------------------------------------- +\subsection{Control parameters} +\label{control_param} +%------------------------------------------------------------------------------- + +UMFPACK uses an optional {\tt double} array (currently of size 20) +to modify its control parameters. If you pass {\tt (double *) NULL} instead +of a {\tt Control} array, then defaults are used. +This array will almost certainly grow in size in future releases, +so be sure to dimension your {\tt Control} array to be of size +{\tt UMFPACK\_CONTROL}. That constant is currently defined to be 20, +but will increase in future versions. + +The contents of this array may be modified by the user +(see {\tt umfpack\_*\_defaults}). Each +user-callable routine includes a complete description of how each control +setting modifies its behavior. Table~\ref{control} summarizes the entire +contents of the {\tt Control} array. +Note that ANSI C uses 0-based indexing, while MATLAB uses 1-based +indexing. Thus, {\tt Control(1)} in MATLAB is the same as +{\tt Control[0]} or {\tt Control[UMFPACK\_PRL]} in ANSI C. + +\begin{table} +\caption{UMFPACK Control parameters} +\label{control} +{\footnotesize +\begin{tabular}{llll} +\hline + +MATLAB & ANSI C & default & description \\ +\hline +{\tt Control(1)} & {\tt Control[UMFPACK\_PRL]} & 1 & printing level \\ +{\tt Control(2)} & {\tt Control[UMFPACK\_DENSE\_ROW]} & 0.2 & dense row parameter \\ +{\tt Control(3)} & {\tt Control[UMFPACK\_DENSE\_COL]} & 0.2 & dense column parameter \\ +{\tt Control(4)} & {\tt Control[UMFPACK\_PIVOT\_TOLERANCE]} & 0.1 & partial pivoting tolerance \\ +{\tt Control(5)} & {\tt Control[UMFPACK\_BLOCK\_SIZE]} & 32 & BLAS block size \\ +{\tt Control(6)} & {\tt Control[UMFPACK\_STRATEGY]} & 0 (auto) & select strategy \\ +{\tt Control(7)} & {\tt Control[UMFPACK\_ALLOC\_INIT]} & 0.7 & initial memory allocation \\ +{\tt Control(8)} & {\tt Control[UMFPACK\_IRSTEP]} & 2 & max iter. refinement steps \\ +{\tt Control(13)} & {\tt Control[UMFPACK\_2BY2\_TOLERANCE]} & 0.01 & defines ``large'' entries \\ +{\tt Control(14)} & {\tt Control[UMFPACK\_FIXQ]} & 0 (auto) & fix or modify Q \\ +{\tt Control(15)} & {\tt Control[UMFPACK\_AMD\_DENSE]} & 10 & AMD dense row/column parameter \\ +{\tt Control(16)} & {\tt Control[UMFPACK\_SYM\_PIVOT\_TOLERANCE]} & 0.001 & for diagonal entries \\ +{\tt Control(17)} & {\tt Control[UMFPACK\_SCALE]} & 1 (sum) & row scaling (none, sum, or max) \\ +{\tt Control(18)} & {\tt Control[UMFPACK\_FRONT\_ALLOC\_INIT]} & 0.5 & frontal matrix allocation ratio \\ +{\tt Control(19)} & {\tt Control[UMFPACK\_DROPTOL]} & 0 & drop tolerance \\ +{\tt Control(20)} & {\tt Control[UMFPACK\_AGGRESSIVE]} & 1 (yes) & aggressive absorption \\ + & & & in AMD and COLAMD \\ +% +\hline +\multicolumn{4}{l}{Can only be changed at compile time:} \\ +{\tt Control(9)} & {\tt Control[UMFPACK\_COMPILED\_WITH\_BLAS]} & - & true if BLAS is used \\ +{\tt Control(10)} & {\tt Control[UMFPACK\_COMPILED\_FOR\_MATLAB]} & - & true for mexFunction \\ +{\tt Control(11)} & {\tt Control[UMFPACK\_COMPILED\_WITH\_GETRUSAGE]} & - & 1 if {\tt getrusage} used \\ +{\tt Control(12)} & {\tt Control[UMFPACK\_COMPILED\_IN\_DEBUG\_MODE]} & - & true if debug mode enabled \\ +\hline +\end{tabular} +} +\end{table} + +Let $\alpha_r = ${\tt Control [UMFPACK\_DENSE\_ROW]}, + $\alpha_c = ${\tt Control [UMFPACK\_DENSE\_COL]}, and + $\alpha = ${\tt Control [UMFPACK\_AMD\_DENSE]}. +Suppose the submatrix $\m{S}$, obtained after eliminating pivots with +zero Markowitz cost, is $m$-by-$n$. +Then a row is considered ``dense'' if it has more than +$\max (16, 16 \alpha_r \sqrt{n})$ entries. +A column is considered ``dense'' if it has more than +$\max (16, 16 \alpha_c \sqrt{m})$ entries. +These rows and columns are treated different in COLAMD and during numerical +factorization. In COLAMD, dense columns are placed last in their natural +order, and dense rows are ignored. During numerical factorization, dense +rows are stored differently. +In AMD, a row/column of the square matrix $\m{S}+\m{S}\tr$ is +considered ``dense'' if it has more than $\max (16, \alpha \sqrt{n})$ entries. +These rows/columns are placed last in AMD's output ordering. +For more details on the control parameters, refer to the documentation of +{\tt umfpack\_*\_qsymbolic}, {\tt umfpack\_*\_numeric}, {\tt umfpack\_*\_solve}, +and the {\tt umfpack\_*\_report\_*} routines, +in Sections~\ref{Primary}~through~\ref{Report}, below. + +%------------------------------------------------------------------------------- +\subsection{Error codes} +\label{error_codes} +%------------------------------------------------------------------------------- + +Many of the routines return a {\tt status} value. +This is also returned as the first entry in the {\tt Info} array, for +those routines with that argument. The following list summarizes +all of the error codes in UMFPACK v4.3. Each error code is given a +specific name in the {\tt umfpack.h} include file, so you can use +those constants instead of hard-coded values in your program. +Future versions may report additional error codes. + +A value of zero means everything was successful, and the matrix is +non-singular. A value greater than zero means the routine was successful, +but a warning occurred. Currently, the only warning code is when the +matrix is singular. A negative value means the routine was not successful. +In this case, no {\tt Symbolic} or {\tt Numeric} object was created. + +\begin{itemize} +\item {\tt UMFPACK\_OK}, (0): UMFPACK was successful. + +\item {\tt UMFPACK\_WARNING\_singular\_matrix}, (1): Matrix is singular. + There are exact zeros on the diagonal of $\m{U}$. + +\item {\tt UMFPACK\_ERROR\_out\_of\_memory}, (-1): Not enough memory. + The ANSI C {\tt malloc} or {\tt realloc} routine failed. + +\item {\tt UMFPACK\_ERROR\_invalid\_Numeric\_object}, (-3): + Routines that take a {\tt Numeric} object as input (or load it + from a file) check this object and return this error code if it is + invalid. This can be caused by a memory leak or overrun in your + program, which can overwrite part of the Numeric object. It can also + be caused by passing a Symbolic object by mistake, or some other pointer. + If you try to factorize a matrix using one version of UMFPACK and + then use the factors in another version, this error code will trigger as + well. You cannot factor your matrix using + version 4.0 and then solve with version 4.1. + You cannot use different precisions of the same version + (real and complex, for example). + It is possible for the {\tt Numeric} object to be corrupted by your + program in subtle ways that are not detectable by this quick check. + In this case, you may see an + {\tt UMFPACK\_ERROR\_different\_pattern} error code, or even an + {\tt UMFPACK\_ERROR\_internal\_error}. + +\item {\tt UMFPACK\_ERROR\_invalid\_Symbolic\_object}, (-4): + Routines that take a {\tt Symbolic} object as input (or load it + from a file) check this object and return this error code if it is + invalid. The causes of this error are analogous to the + {\tt UMFPACK\_ERROR\_invalid\_Numeric\_object} error described above. + +\item {\tt UMFPACK\_ERROR\_argument\_missing}, (-5): + Some arguments of some are optional (you can pass a {\tt NULL} pointer + instead of an array). This error code occurs if you pass a {\tt NULL} + pointer when that argument is required to be present. + \item {\tt UMFPACK\_ERROR\_n\_nonpositive} (-6): + The number of rows or columns of the matrix must be greater than zero. + \item {\tt UMFPACK\_ERROR\_invalid\_matrix} (-8): + The matrix is invalid. For the column-oriented input, this error + code will occur if the contents of {\tt Ap} and/or {\tt Ai} are invalid. + + {\tt Ap} is an integer array of size {\tt n\_col+1}. + On input, it holds the + ``pointers'' for the column form of the sparse matrix $\m{A}$. + Column {\tt j} of + the matrix A is held in {\tt Ai [(Ap [j])} \ldots {\tt (Ap [j+1]-1)]}. + The first entry, {\tt Ap [0]}, must be zero, + and {\tt Ap [j]} $\le$ {\tt Ap [j+1]} must hold for all + {\tt j} in the range 0 to {\tt n\_col-1}. + The value {\tt nz = Ap [n\_col]} is thus the + total number of entries in the pattern of the matrix A. + {\tt nz} must be greater than or equal to zero. + + The nonzero pattern (row indices) for column {\tt j} is stored in + {\tt Ai [(Ap [j])} \ldots {\tt (Ap [j+1]-1)]}. The row indices in a given + column {\tt j} + must be in ascending order, and no duplicate row indices may be present. + Row indices must be in the range 0 to {\tt n\_row-1} + (the matrix is 0-based). + + Some routines take a triplet-form input, with arguments + {\tt nz}, {\tt Ti}, and {\tt Tj}. This error code is returned + if {\tt nz} is less than zero, + if any row index in {\tt Ti} is outside the range 0 to {\tt n\_col-1}, or + if any column index in {\tt Tj} is outside the range 0 to {\tt n\_row-1}. + +\item {\tt UMFPACK\_ERROR\_different\_pattern}, (-11): + The most common cause of this error is that the pattern of the + matrix has changed between the symbolic and numeric factorization. + It can also occur if the {\tt Numeric} or {\tt Symbolic} object has + been subtly corrupted by your program. + +\item {\tt UMFPACK\_ERROR\_invalid\_system}, (-13): + The {\tt sys} argument provided to one of the solve routines is invalid. + +\item {\tt UMFPACK\_ERROR\_invalid\_permutation}, (-15): + The permutation vector provided as input is invalid. + +\item {\tt UMFPACK\_ERROR\_file\_IO}, (-17): + This error code is returned by the routines that save and load + the {\tt Numeric} or {\tt Symbolic} objects to/from a file, if a + file I/O error has occurred. The file may not exist or may not be readable, + you may be trying to create a file that you don't have permission to create, + or you may be out of disk space. The file you are trying to read might + be the wrong one, and an earlier end-of-file condition would then result + in this error. + +\item {\tt UMFPACK\_ERROR\_internal\_error}, (-911): + An internal error has occurred, of unknown cause. This is either a bug + in UMFPACK, or the result of a memory overrun from your program. + Try modifying the file {\tt AMD/Source/amd\_internal.h} and adding + the statement {\tt \#undef NDEBUG}, to enable the debugging mode. + Recompile UMFPACK and rerun your program. + A failed assertion might occur which + can give you a better indication as to what is going wrong. Be aware that + UMFPACK will be extraordinarily slow when running in debug mode. + If all else fails, contact the developer (davis@cise.ufl.edu) with + as many details as possible. + +\end{itemize} + +%------------------------------------------------------------------------------- +\subsection{Larger examples} +%------------------------------------------------------------------------------- + +Full examples of all user-callable UMFPACK routines +are available in four stand-alone C main programs, {\tt umfpack\_*\_demo.c}. +Another example is +the UMFPACK mexFunction, {\tt umfpackmex.c}. The mexFunction accesses only the +user-callable C interface to UMFPACK. The only features that it does not use +are the support for the triplet form (MATLAB's sparse arrays are already in the +compressed column form) and the ability to reuse the {\tt Symbolic} object to +numerically factorize a matrix whose pattern is the same as a prior matrix +analyzed by {\tt umfpack\_*\_symbolic} or {\tt umfpack\_*\_qsymbolic}. The +latter is an important feature, but the mexFunction does not return its opaque +{\tt Symbolic} and {\tt Numeric} objects to MATLAB. Instead, it gets the +contents of these objects after extracting them via the {\tt umfpack\_*\_get\_*} +routines, and returns them as MATLAB sparse matrices. + +The {\tt umf4.c} program for reading matrices in Harwell/Boeing format +\cite{DuffGrimesLewis87b} is provided. It requires three Fortran 77 programs +({\tt readhb.f}, {\tt readhb\_nozeros.f}, and {\tt readhb\_size.f}) +for reading in the sample Harwell/Boeing files in the {\tt UMFPACK/Demo/HB} +directory. More matrices are available at +http://www.cise.ufl.edu/research/sparse/matrices. +Type {\tt make hb} in the {\tt UMFPACK/Demo/HB} directory +to compile and run this demo. This program was used for the experimental +results in \cite{Davis03}. + +%------------------------------------------------------------------------------- +\section{Synopsis of C-callable routines} +\label{Synopsis} +%------------------------------------------------------------------------------- + +Each subsection, below, summarizes the input variables, output variables, return +values, and calling sequences of the routines in one category. Variables with +the same name as those already listed in a prior category have the same size +and type. + +The real, {\tt long} integer {\tt umfpack\_dl\_*} routines are +identical to the real, {\tt int} routines, except that {\tt \_di\_} is replaced +with {\tt \_dl\_} in the name, and all {\tt int} arguments become {\tt long}. +Similarly, the complex, {\tt long} integer {\tt umfpack\_zl\_*} routines are +identical to the complex, {\tt int} routines, except that {\tt \_zi\_} is +replaced +with {\tt \_zl\_} in the name, and all {\tt int} arguments become {\tt long}. +Only the real and complex {\tt int} versions are listed in the synopsis below. + +The matrix $\m{A}$ is {\tt m}-by-{\tt n} with {\tt nz} entries. + +The {\tt sys} argument of {\tt umfpack\_*\_solve} +is an integer in the range 0 to 14 which defines which linear system is +to be solved. Valid values are listed in Table~\ref{sys}. +The notation $\m{A}\he$ refers to the matrix transpose, which is the +complex conjugate transpose for complex matrices ({\tt A'} in MATLAB). +The array transpose is $\m{A}\tr$, which is {\tt A.'} in MATLAB. + +\begin{table} +\begin{center} +\caption{UMFPACK {\tt sys} parameter} +\label{sys} +{\footnotesize +\begin{tabular}{ll|l} +\hline +Value & & system \\ +\hline +{\tt UMFPACK\_A} & (0) & $\m{Ax}=\m{b}$ \\ +{\tt UMFPACK\_At} & (1) & $\m{A}\he\m{x}=\m{b}$ \\ +{\tt UMFPACK\_Aat} & (2) & $\m{A}\tr\m{x}=\m{b}$ \\ +\hline +{\tt UMFPACK\_Pt\_L} & (3) & $\m{P}\tr\m{Lx}=\m{b}$ \\ +{\tt UMFPACK\_L} & (4) & $\m{Lx}=\m{b}$ \\ +{\tt UMFPACK\_Lt\_P} & (5) & $\m{L}\he\m{Px}=\m{b}$ \\ +{\tt UMFPACK\_Lat\_P} & (6) & $\m{L}\tr\m{Px}=\m{b}$ \\ +{\tt UMFPACK\_Lt} & (7) & $\m{L}\he\m{x}=\m{b}$ \\ +{\tt UMFPACK\_Lat} & (8) & $\m{L}\tr\m{x}=\m{b}$ \\ +\hline +{\tt UMFPACK\_U\_Qt} & (9) & $\m{UQ}\tr\m{x}=\m{b}$ \\ +{\tt UMFPACK\_U} & (10) & $\m{Ux}=\m{b}$ \\ +{\tt UMFPACK\_Q\_Ut} & (11) & $\m{QU}\he\m{x}=\m{b}$ \\ +{\tt UMFPACK\_Q\_Uat} & (12) & $\m{QU}\tr\m{x}=\m{b}$ \\ +{\tt UMFPACK\_Ut} & (13) & $\m{U}\he\m{x}=\m{b}$ \\ +{\tt UMFPACK\_Uat} & (14) & $\m{U}\tr\m{x}=\m{b}$ \\ +\hline +\end{tabular} +} +\end{center} +\end{table} + +%------------------------------------------------------------------------------- +\subsection{Primary routines: real/{\tt int}} +%------------------------------------------------------------------------------- + +{\footnotesize +\begin{verbatim} +#include "umfpack.h" +int status, sys, n, m, nz, Ap [n+1], Ai [nz] ; +double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], Ax [nz], X [n], B [n] ; +void *Symbolic, *Numeric ; + +status = umfpack_di_symbolic (m, n, Ap, Ai, Ax, &Symbolic, Control, Info) ; +status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, Control, Info) ; +status = umfpack_di_solve (sys, Ap, Ai, Ax, X, B, Numeric, Control, Info) ; +umfpack_di_free_symbolic (&Symbolic) ; +umfpack_di_free_numeric (&Numeric) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{Alternative routines: real/{\tt int}} +%------------------------------------------------------------------------------- + +{\footnotesize +\begin{verbatim} +int Qinit [n], Wi [n] ; +double W [5*n] ; + +umfpack_di_defaults (Control) ; +status = umfpack_di_qsymbolic (m, n, Ap, Ai, Ax, Qinit, &Symbolic, Control, Info) ; +status = umfpack_di_wsolve (sys, Ap, Ai, Ax, X, B, Numeric, Control, Info, Wi, W) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{Matrix manipulation routines: real/{\tt int}} +%------------------------------------------------------------------------------- + +{\footnotesize +\begin{verbatim} +int Ti [nz], Tj [nz], P [m], Q [n], Rp [m+1], Ri [nz], Map [nz] ; +double Tx [nz], Rx [nz], Y [m], Z [m] ; + +status = umfpack_di_col_to_triplet (n, Ap, Tj) ; +status = umfpack_di_triplet_to_col (m, n, nz, Ti, Tj, Tx, Ap, Ai, Ax, Map) ; +status = umfpack_di_transpose (m, n, Ap, Ai, Ax, P, Q, Rp, Ri, Rx) ; +status = umfpack_di_scale (Y, Z, Numeric) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{Getting the contents of opaque objects: real/{\tt int}} +%------------------------------------------------------------------------------- + +The {\tt filename} string should be large enough to hold the name of a file. + +{\footnotesize +\begin{verbatim} +int lnz, unz, Lp [m+1], Lj [lnz], Up [n+1], Ui [unz], do_recip ; +double Lx [lnz], Ux [unz], D [min (m,n)], Rs [m] ; +int nfr, nchains, P1 [m], Q1 [n], Front_npivcol [n+1], Front_parent [n+1], Front_1strow [n+1], + Front_leftmostdesc [n+1], Chain_start [n+1], Chain_maxrows [n+1], Chain_maxcols [n+1] ; +char filename [100] ; + +status = umfpack_di_get_lunz (&lnz, &unz, &m, &n, &nz_udiag, Numeric) ; +status = umfpack_di_get_numeric (Lp, Lj, Lx, Up, Ui, Ux, P, Q, D, &do_recip, Rs, Numeric) ; +status = umfpack_di_get_symbolic (&m, &n, &n1, &nz, &nfr, &nchains, P1, Q1, + Front_npivcol, Front_parent, Front_1strow, Front_leftmostdesc, + Chain_start, Chain_maxrows, Chain_maxcols, Symbolic) ; +status = umfpack_di_load_numeric (&Numeric, filename) ; +status = umfpack_di_save_numeric (Numeric, filename) ; +status = umfpack_di_load_symbolic (&Symbolic, filename) ; +status = umfpack_di_save_symbolic (Symbolic, filename) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{Reporting routines: real/{\tt int}} +%------------------------------------------------------------------------------- + +{\footnotesize +\begin{verbatim} + +umfpack_di_report_status (Control, status) ; +umfpack_di_report_control (Control) ; +umfpack_di_report_info (Control, Info) ; +status = umfpack_di_report_matrix (m, n, Ap, Ai, Ax, 1, Control) ; +status = umfpack_di_report_matrix (m, n, Rp, Ri, Rx, 0, Control) ; +status = umfpack_di_report_numeric (Numeric, Control) ; +status = umfpack_di_report_perm (m, P, Control) ; +status = umfpack_di_report_perm (n, Q, Control) ; +status = umfpack_di_report_symbolic (Symbolic, Control) ; +status = umfpack_di_report_triplet (m, n, nz, Ti, Tj, Tx, Control) ; +status = umfpack_di_report_vector (n, X, Control) ; +\end{verbatim} +} + + + + + + +%------------------------------------------------------------------------------- +\subsection{Primary routines: complex/{\tt int}} +%------------------------------------------------------------------------------- + +{\footnotesize +\begin{verbatim} +double Az [nz], Xx [n], Xz [n], Bx [n], Bz [n] ; + +status = umfpack_zi_symbolic (m, n, Ap, Ai, Ax, Az, &Symbolic, Control, Info) ; +status = umfpack_zi_numeric (Ap, Ai, Ax, Az, Symbolic, &Numeric, Control, Info) ; +status = umfpack_zi_solve (sys, Ap, Ai, Ax, Az, Xx, Xz, Bx, Bz, Numeric, Control, Info) ; +umfpack_zi_free_symbolic (&Symbolic) ; +umfpack_zi_free_numeric (&Numeric) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{Alternative routines: complex/{\tt int}} +%------------------------------------------------------------------------------- + +{\footnotesize +\begin{verbatim} +double Wz [10*n] ; + +umfpack_zi_defaults (Control) ; +status = umfpack_zi_qsymbolic (m, n, Ap, Ai, Ax, Az, Qinit, &Symbolic, Control, Info) ; +status = umfpack_zi_wsolve (sys, Ap, Ai, Ax, Az, Xx, Xz, Bx, Bz, Numeric, Control, Info, Wi, Wz) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{Matrix manipulation routines: complex/{\tt int}} +%------------------------------------------------------------------------------- + +{\footnotesize +\begin{verbatim} +double Tz [nz], Rz [nz], Yx [m], Yz [m], Zx [m], Zz [m] ; + +status = umfpack_zi_col_to_triplet (n, Ap, Tj) ; +status = umfpack_zi_triplet_to_col (m, n, nz, Ti, Tj, Tx, Tz, Ap, Ai, Ax, Az, Map) ; +status = umfpack_zi_transpose (m, n, Ap, Ai, Ax, Az, P, Q, Rp, Ri, Rx, Rz, 1) ; +status = umfpack_zi_transpose (m, n, Ap, Ai, Ax, Az, P, Q, Rp, Ri, Rx, Rz, 0) ; +status = umfpack_zi_scale (Yx, Yz, Zx, Zz, Numeric) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{Getting the contents of opaque objects: complex/{\tt int}} +%------------------------------------------------------------------------------- + +{\footnotesize +\begin{verbatim} +double Lz [lnz], Uz [unz], Dx [min (m,n)], Dz [min (m,n)] ; + +status = umfpack_zi_get_lunz (&lnz, &unz, &m, &n, &nz_udiag, Numeric) ; +status = umfpack_zi_get_numeric (Lp, Lj, Lx, Lz, Up, Ui, Ux, Uz, P, Q, Dx, Dz, + &do_recip, Rs, Numeric) ; +status = umfpack_zi_get_symbolic (&m, &n, &n1, &nz, &nfr, &nchains, P1, Q1, + Front_npivcol, Front_parent, Front_1strow, Front_leftmostdesc, + Chain_start, Chain_maxrows, Chain_maxcols, Symbolic) ; +status = umfpack_zi_load_numeric (&Numeric, filename) ; +status = umfpack_zi_save_numeric (Numeric, filename) ; +status = umfpack_zi_load_symbolic (&Symbolic, filename) ; +status = umfpack_zi_save_symbolic (Symbolic, filename) ; +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\subsection{Reporting routines: complex/{\tt int}} +%------------------------------------------------------------------------------- + +{\footnotesize +\begin{verbatim} + +umfpack_zi_report_status (Control, status) ; +umfpack_zi_report_control (Control) ; +umfpack_zi_report_info (Control, Info) ; +status = umfpack_zi_report_matrix (m, n, Ap, Ai, Ax, Az, 1, Control) ; +status = umfpack_zi_report_matrix (m, n, Rp, Ri, Rx, Rz, 0, Control) ; +status = umfpack_zi_report_numeric (Numeric, Control) ; +status = umfpack_zi_report_perm (m, P, Control) ; +status = umfpack_zi_report_perm (n, Q, Control) ; +status = umfpack_zi_report_symbolic (Symbolic, Control) ; +status = umfpack_zi_report_triplet (m, n, nz, Ti, Tj, Tx, Tz, Control) ; +status = umfpack_zi_report_vector (n, Xx, Xz, Control) ; +\end{verbatim} +} + + + + + +%------------------------------------------------------------------------------- +\section{Synopsis of utility routines} +%------------------------------------------------------------------------------- + +These routines are the same in all four versions of UMFPACK. + +{\footnotesize +\begin{verbatim} +double t, s [2] ; + +t = umfpack_timer ( ) ; +umfpack_tic (s) ; +umfpack_toc (s) ; + +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\section{Synopsis of AMD ordering routines} +%------------------------------------------------------------------------------- + +UMFPACK makes use of the AMD ordering package for its symmetric ordering +strategy. You may also use these four user-callable routines in your own C +programs. You need to include the {\tt amd.h} file only if you make direct +calls to the AMD routines themselves. The {\tt int} versions are summarized +below; {\tt long} versions are also available. Refer to the AMD User Guide +for more information, or to the file {\tt amd.h} which documents these routines. + +{\footnotesize +\begin{verbatim} +#include "amd.h" +double amd_control [AMD_CONTROL], amd_info [AMD_INFO] ; + +amd_defaults (amd_control) ; +status = amd_order (n, Ap, Ai, P, amd_control, amd_info) ; +amd_control (amd_control) ; +amd_info (amd_info) ; + +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\section{Using UMFPACK in a Fortran program} +%------------------------------------------------------------------------------- + +UMFPACK v4.3 includes a basic Fortran 77 interface to some of the C-callable +UMFPACK routines. +Since interfacing C and Fortran programs is not portable, this interface might +not work with all C and Fortran compilers. Refer to Section~\ref{Install} for +more details. The following Fortran routines are provided. +The list includes the C-callable routines that the Fortran interface +routine calls. Refer to the corresponding C routines in Section~\ref{C} for +more details on what the Fortran routine does. + +\begin{itemize} +\item {\tt umf4def}: sets the default control parameters + ({\tt umfpack\_di\_defaults}). + +\item {\tt umf4sym}: pre-ordering and symbolic factorization + ({\tt umfpack\_di\_symbolic}). + +\item {\tt umf4num}: numeric factorization + ({\tt umfpack\_di\_numeric}). + +\item {\tt umf4solr}: solve a linear system with iterative refinement + ({\tt umfpack\_di\_solve}). + +\item {\tt umf4sol}: solve a linear system without iterative refinement + ({\tt umfpack\_di\_solve}). Sets {\tt Control [UMFPACK\_IRSTEP]} + to zero, and does not require the matrix $\m{A}$. + +\item {\tt umf4scal}: scales a vector using UMFPACK's scale factors + ({\tt umfpack\_di\_scale}). + +\item {\tt umf4fnum}: free the {\tt Numeric} object + ({\tt umfpack\_di\_free\_numeric}). + +\item {\tt umf4fsym}: free the {\tt Symbolic} object + ({\tt umfpack\_di\_free\_symbolic}). + +\item {\tt umf4pcon}: prints the control parameters + ({\tt umfpack\_di\_report\_control}). + +\item {\tt umf4pinf}: print statistics + ({\tt umfpack\_di\_report\_info}). + +\item {\tt umf4snum}: save the {\tt Numeric} object to a file + ({\tt umfpack\_di\_save\_numeric}). + +\item {\tt umf4ssym}: save the {\tt Symbolic} object to a file + ({\tt umfpack\_di\_save\_symbolic}). + +\item {\tt umf4lnum}: load the {\tt Numeric} object from a file + ({\tt umfpack\_di\_load\_numeric}). + +\item {\tt umf4lsym}: load the {\tt Symbolic} object from a file + ({\tt umfpack\_di\_load\_symbolic}). +\end{itemize} + +The matrix $\m{A}$ is passed to UMFPACK in compressed column form, with 0-based +indices. In Fortran, for an {\tt m}-by-{\tt n} matrix $\m{A}$ with {\tt nz} +entries, the row indices of the first column (column 1) are in +{\tt Ai (Ap(1)+1} \ldots {\tt Ap(2))}, with values in +{\tt Ax (Ap(1)+1} \ldots {\tt Ap(2))}. The last column (column {\tt n}) is in +{\tt Ai (Ap(n)+1} \ldots {\tt Ap(n+1))} and +{\tt Ax (Ap(n)+1} \ldots {\tt Ap(n+1))}. +The number of entries in the matrix is thus {\tt nz = Ap (n+1)}. +The row indices in {\tt Ai} are in the range 0 to {\tt m}-1. They must be +sorted, with no duplicate entries allowed. None of the UMFPACK routines +modify the input matrix $\m{A}$. +The following definitions apply for the Fortran routines: + +{\footnotesize +\begin{verbatim} + integer m, n, Ap (n+1), Ai (nz), symbolic, numeric, filenum, status + double precision Ax (nz), control (20), info (90), x (n), b (n) +\end{verbatim} +} + +UMFPACK's status is returned in either a {\tt status} argument, or in +{\tt info (1)}. +It is zero if UMFPACK was successful, 1 if the matrix is singular (this is a +warning, not an error), and negative if an error occurred. +Section~\ref{error_codes} summarizes the possible values of {\tt status} +and {\tt info (1)}. +See Table~\ref{sys} for a list of the values of the {\tt sys} argument. +See Table~\ref{control} for a list of the control parameters (the +Fortran usage is the same as the MATLAB usage for this array). + +For the {\tt Numeric} and {\tt Symbolic} handles, it is probably safe to +assume that a Fortran {\tt integer} is sufficient to store a C pointer. If +that does not work, try defining {\tt numeric} and {\tt symbolic} in your +Fortran program as integer arrays of size 2. You will need to define them +as {\tt integer*8} if you compile UMFPACK in the 64-bit mode. + +To avoid passing strings between C and Fortran in the load/save routines, +a file number is passed instead, and the C interface constructs a file name +(if {\tt filenum} is 42, the {\tt Numeric} file name is {\tt n42.umf}, and +the {\tt Symbolic} file name is {\tt s42.umf}). + +The following is a summary of the calling sequence of each Fortran +interface routine. An example of their use is in the {\tt Demo/umf4hb.f} +file. That routine also includes an example of how to convert a 1-based +sparse matrix into 0-based form. For more details on the arguments of each +routine, refer to the arguments of the same name in the corresponding +C-callable routine, in Sections~\ref{Primary}~through~\ref{Utility}. +The only exception is the {\tt control} argument of {\tt umf4sol}, +which sets {\tt control (8)} to zero to disable iterative refinement. +Note that the solve routines do not overwrite {\tt b} with the solution, +but return their solution in a different array, {\tt x}. + +{\footnotesize +\begin{verbatim} + call umf4def (control) + call umf4sym (m, n, Ap, Ai, Ax, symbolic, control, info) + call umf4num (Ap, Ai, Ax, symbolic, numeric, control, info) + call umf4solr (sys, Ap, Ai, Ax, x, b, numeric, control, info) + call umf4sol (sys, x, b, numeric, control, info) + call umf4scal (x, b, numeric, status) + call umf4fnum (numeric) + call umf4fsym (symbolic) + call umf4pcon (control) + call umf4pinf (control) + call umf4snum (numeric, filenum, status) + call umf4ssym (symbolic, filenum, status) + call umf4lnum (numeric, filenum, status) + call umf4lsym (symbolic, filenum, status) +\end{verbatim} +} + +Access to the complex routines in UMFPACK is provided by the interface +routines in {\tt umf4\_f77zwrapper.c}. The following is a synopsis +of each routine. All the arguments are the same as the real versions, +except {\tt Az}, {\tt xz}, and {\tt bz} are the imaginary parts of +the matrix, solution, and right-hand-side, respectively. The +{\tt Ax}, {\tt x}, and {\tt b} are the real parts. + +{\footnotesize +\begin{verbatim} + call umf4zdef (control) + call umf4zsym (m, n, Ap, Ai, Ax, Az, symbolic, control, info) + call umf4znum (Ap, Ai, Ax, Az, symbolic, numeric, control, info) + call umf4zsolr (sys, Ap, Ai, Ax, Az, x, xz, b, bz, numeric, control, info) + call umf4zsol (sys, x, xz, b, bz, numeric, control, info) + call umf4zscal (x, xz, b, bz, numeric, status) + call umf4zfnum (numeric) + call umf4zfsym (symbolic) + call umf4zpcon (control) + call umf4zpinf (control) + call umf4zsnum (numeric, filenum, status) + call umf4zssym (symbolic, filenum, status) + call umf4zlnum (numeric, filenum, status) + call umf4zlsym (symbolic, filenum, status) +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\section{Installing the C library} +\label{Install} +%------------------------------------------------------------------------------- + +The following discussion assumes you have the {\tt make} program, either in +Unix, or in Windows with Cygwin\footnote{www.cygwin.com}. +You can skip this section and go to next one if all you want to use is +the UMFPACK and AMD mexFunctions in MATLAB. + +You will need to install both UMFPACK v4.3 and AMD v1.1 (or AMD v1.0) to use UMFPACK. +The {\tt UMFPACK} and {\tt AMD} subdirectories must be placed side-by-side +within the same directory. AMD is a stand-alone package that +is required by UMFPACK. UMFPACK can be compiled without the +BLAS \cite{DaydeDuff99,ACM679a,ATLAS,GotoVandeGeijn02}, +but your performance will be much less than what it should be. + +System-dependent configurations are in the {\tt AMD/Make} +and {\tt UMFPACK/Make} directories (the \newline +{\tt UMFPACK/Make} directory is actually +just a symbolic link to {\tt AMD/Make}\footnote{Windows might not extract +the symbolic link {\tt UMFPACK/Make} correctly. If it doesn't, simply +create the {\tt UMFPACK/Make} folder by copying it from {\tt AMD/Make}.}). +You can edit the {\tt Make.include} +files in either of those directories to customize the compilation. The default +settings will work on most systems, except that UMFPACK will be compiled so +that it does not use the BLAS. Sample configuration files are provided +for Linux, Sun Solaris, SGI IRIX, IBM AIX, and the DEC/Compaq Alpha. + +To compile and install both packages, +go to the {\tt UMFPACK} directory and type {\tt make}. This will compile the +libraries ({\tt AMD/Lib/libamd.a} and {\tt UMFPACK/Lib/libumfpack.a}). +A demo of the AMD ordering routine will be compiled and tested in +the {\tt AMD/Demo} directory, and five demo programs will then be +compiled and tested in the {\tt UMFPACK/Demo} directory. +The outputs of these demo programs will then be compared with output +files in the distribution. Expect to see a few differences, such as +residual norms, compile-time control settings, and perhaps memory usage +differences. The AMD and UMFPACK mexFunctions for +use in MATLAB will also be compiled. If you do not have MATLAB 6.0 or +later, type {\tt make lib} instead. + +If you have the GNU version of {\tt make}, the {\tt Source/GNUmakefile} and +{\tt MATLAB/GNUmakefile} files are used. These are much more concise than +what the ``old'' version of {\tt make} can handle. If you do not have +GNU {\tt make}, the {\tt Source/Makefile} and {\tt MATLAB/Makefile} files +are used instead. Each UMFPACK source file is compiled into four +versions ({\tt double} / complex, and {\tt int} / {\tt long}). A proper +old-style {\tt Makefile} is cumbersome in this case, so these two +{\tt Makefile}'s have been constructed by brute force. They ignore +dependencies, and simply compile everything. I highly recommend using GNU +{\tt make} if you wish to modify UMFPACK. + +If you compile UMFPACK and AMD and then later change the {\tt Make.include} +file or your system-specific configuration file such as {\tt Make.linux}, +then you should type {\tt make purge} and then {\tt make} to recompile. + +Here are the various parameters that you can control in your +{\tt Make.include} file: + +\begin{itemize} +\item {\tt CC = } your C compiler, such as {\tt cc}. +\item {\tt RANLIB = } your system's {\tt ranlib} program, if needed. +\item {\tt CFLAGS = } optimization flags, such as {\tt -O}. + Add {\tt -DLP64} if you are compiling in 64-bit mode + (32 bit {\tt int}'s, 64 bit {\tt long}'s, and 64 bit pointers). +\item {\tt CONFIG = } configuration settings for the BLAS, memory allocation + routines, and timing routines. +\item {\tt LIB = } your libraries, such as {\tt -lm} or {\tt -lblas}. +\item {\tt RM =} the command to delete a file. +\item {\tt MV =} the command to rename a file. +\item {\tt MEX =} the command to compile a MATLAB mexFunction. + If you are using MATLAB 5, you need to add {\tt -DNBLAS} and + {\tt -DNUTIL} to this command. An example is provided in + the {\tt Make/Make.include} file. +\item {\tt F77 =} the command to compile a Fortran program (optional). +\item {\tt F77FLAGS =} the Fortran compiler flags (optional). +\item {\tt F77LIB =} the Fortran libraries (optional). +\end{itemize} + +The {\tt CONFIG} string can include combinations of the following; +most deal with how the BLAS are called: +\begin{itemize} +\item {\tt -DNBLAS} if you do not have any BLAS at all. +\item {\tt -DCBLAS} if you have the C-BLAS \cite{ATLAS}. +\item {\tt -DNSUNPERF} if you are on Solaris but do not have the Sun + Performance Library (for the BLAS). +\item {\tt -DNSCSL} if you on SGI IRIX but do not have the SCSL BLAS library. +\item {\tt -DLONGBLAS} if your BLAS can take {\tt long} integer input + arguments. If not defined, then the {\tt umfpack\_*l\_*} versions of + UMFPACK that use {\tt long} integers do not call the BLAS. + This flag is set internally when using the Sun Performance BLAS + or SGI's SCSL BLAS (both have 64-bit versions of the BLAS). +\item Options for controlling how C calls the Fortran BLAS: + {\tt -DBLAS\_BY\_VALUE}, {\tt -DBLAS\_NO\_UNDERSCORE}, + and {\tt -DBLAS\_CHAR\_ARG}. These are set automatically for Windows, + Sun Solaris, SGI Irix, Red Hat Linux, Compaq Alpha, and + AIX (the IBM RS 6000). They are ignored if you are using + the C-BLAS interface to the BLAS. +\item {\tt -DGETRUSAGE} if you have the {\tt getrusage} function. +\item {\tt -DNUTIL} if you wish to compile the MATLAB-callable + UMFPACK mexFunction with the {\tt mxMalloc}, {\tt mxRealloc} + and {\tt mxFree} routines, instead of the undocumented (but + superior) {\tt utMalloc}, {\tt utRealloc}, and {\tt utFree} + routines. The default is to use the {\tt ut*} routines on + Unix, and the {\tt mx*} routines on Windows. +\item {\tt -DNPOSIX} if you do not have the POSIX-compliant + {\tt sysconf} and {\tt times} routines used by + {\tt umfpack\_tic} and {\tt umfpack\_toc}. +\item {\tt -DNRECIPROCAL} controls a trade-off between speed and accuracy. + If defined (or if the pivot value itself is less than $10^{-12}$), + then the pivot column is divided by the pivot value during numeric + factorization. Otherwise, it is multiplied by the reciprocal of the + pivot, which is faster but can be less accurate. The default is + to multiply by the reciprocal unless the pivot value is small. + This option also modifies how the rows of the matrix $\m{A}$ are + scaled. If {\tt -DNRECIPROCAL} is defined (or if any scale factor is + less than $10^{-12}$), entries in the rows of $\m{A}$ are divided + by the scale factors. Otherwise, they are multiplied by the reciprocal. + When compiling the complex routines with the GNU {\tt gcc} compiler, the + pivot column is always divided by the pivot entry, because of a + numerical accuracy issue encountered with {\tt gcc} version 3.2 with a + few complex matrices on a Pentium 4M (running Linux). You can still + use {\tt -DNRECIPROCAL} to control how the scale factors + for the rows of $\m{A}$ are applied. +\item {\tt -DNO\_DIVIDE\_BY\_ZERO} controls how UMFPACK treats zeros + on the diagonal of $\m{U}$, for a singular matrix $\m{A}$. + If defined, then no division by + zero is performed (a zero entry on the diagonal of $\m{U}$ is + treated as if it were equal to one). By default, + UMFPACK will divide by zero. +\item {\tt -DNO\_TIMER} controls whether or not timing routines + are to be called. If defined, no timers are used. + Timers are included by default. +\end{itemize} + +If a Fortran BLAS package is used you may see compiler warnings. The BLAS +routines +{\tt dgemm}, {\tt dgemv}, {\tt dger}, {\tt dtrsm}, {\tt dtrsv}, {\tt dscal} +and their corresponding complex versions are used. +Header files are not provided for the Fortran +BLAS. You may safely ignore all of these warnings. + +I highly recommend the recent BLAS by Goto and van de Geijn +\cite{GotoVandeGeijn02}. Using this BLAS increased the performance +of UMFPACK by up to 50\% on a Dell Latitude C840 laptop (2GHz Pentium 4M, +512K L2 cache, 1GB main memory). The peak performance of +{\tt umfpack\_di\_numeric} with Goto and van de Geijn's BLAS is 1.6 Gflops +on this computer. In MATLAB, the peak performance of UMFPACK on +a dense matrix (stored in sparse format) is 900 Mflops, as compared to +1 Gflop for {\tt x = A}$\backslash${\tt b} +when {\tt A} is stored as a regular full matrix. + +When you compile your program that uses the C-callable UMFPACK library, +you need to link your program with both libraries +({\tt UMFPACK/Lib/libumfpack.a} and {\tt AMD/Lib/libamd.a}) +and you need to tell your compiler to look in the +directories {\tt UMFPACK/Include} and {\tt AMD/Include} for include +files. See {\tt UMFPACK/Demo/Makefile} for an example. +You do not need to directly include any AMD include files in your +program, unless you directly call AMD routines. You only need the +\begin{verbatim} +#include "umfpack.h" +\end{verbatim} +statement, as described in Section~\ref{Synopsis}. + +If you would like to compile both 32-bit and 64-bit versions of the libraries, +you will need to do it in two steps. Modify your {\tt Make/Make.} +file, and select the 32-bit option. Type {\tt make} in the {\tt UMFPACK} +directory, which creates the {\tt UMFPACK/Lib/libumfpack.a} and +{\tt AMD/Lib/libamd.a} libraries. Rename those two files. Edit your +{\tt Make/Make.} and select the 64-bit option. Type {\tt make purge}, +and then {\tt make}, and you will create the 64-bit libraries. +You can use the same {\tt umfpack.h} include file for both 32-bit and +64-bit versions. Simply link your program with the appropriate 32-bit +or 64-bit compiled version of the UMFPACK and AMD libraries. + +Type {\tt make hb} in the {\tt UMFPACK/Demo/HB} directory +to compile and run a C program that reads in and factorizes +Harwell/Boeing matrices. Note that this uses a stand-alone Fortran +program to read in the Fortran-formatted Harwell/Boeing matrices and +write them to a file which can be read by a C program. + +%------------------------------------------------------------------------------- +\section{Installing the MATLAB interface} +%------------------------------------------------------------------------------- + +If all you want to do is use the UMFPACK mexFunction in MATLAB, you can skip +the use of the {\tt make} command described above. Simply type +{\tt umfpack\_make} in MATLAB while in the {\tt UMFPACK/MATLAB} directory. +You can also type {\tt amd\_make} in the {\tt AMD/MATLAB} directory +to compile the stand-alone AMD mexFunction (this is not required to +compile the UMFPACK mexFunction). This works on any computer with MATLAB, +including Windows. This method of creating the UMFPACK mexFunction +has been tested with MATLAB 5.3, 6.1, and 6.5. + +You will be prompted to select several configuration options, including +whether or not to use the BLAS. +MATLAB 5.3 (or earlier) does not include the BLAS, so you either have to +compile UMFPACK without the BLAS (UMFPACK will be slow), or modify your +{\tt /bin/mexopts.sh} by adding your BLAS library +to the {\tt CLIBS} string, +where {\tt } is the directory in which MATLAB is installed. + +If you are using Windows and the {\tt lcc} compiler bundled with +MATLAB 6.1, then you may need to copy the +{\tt UMFPACK}$\backslash${\tt MATLAB}$\backslash${\tt lcc\_lib}$\backslash${\tt libmwlapack.lib} +file into the +{\tt }$\backslash${\tt extern}$\backslash${\tt lib}$\backslash${\tt win32}$\backslash${\tt lcc}$\backslash$ +directory. +Next, type {\tt mex -setup} +at the MATLAB prompt, and ask MATLAB to select the {\tt lcc} compiler. +MATLAB 6.1 has built-in BLAS, but in that version of MATLAB the BLAS +cannot be accessed by a mexFunction compiled by {\tt lcc} without first copying +this file to the location listed above. +If you have MATLAB 6.5 or later, you can probably skip this step. + +%------------------------------------------------------------------------------- +\section{Installing the Fortran interface} +%------------------------------------------------------------------------------- + +Once the 32-bit C-callable UMFPACK library is compiled, you can also compile +the Fortran interface, by typing {\tt make fortran}. This will create +the {\tt umf4hb} program, test it, and compare the output with the +file {\tt umf4hb.out} in the distribution. +If you compiled UMFPACK in 64-bit mode, you need to use {\tt make fortran64} +instead, which compiles the {\tt umf4hb64} program and compares its output +with the file {\tt umf4hb64.out}. +Refer to the comments in the {\tt Demo/umf4\_f77wrapper.c} file +for more details. + +This interface is {\bf highly} non-portable, since it depends +on how C and Fortran are interfaced. +Because of this issue, the interface is included in the {\tt Demo} directory, +and not as a primary part of the UMFPACK library. The interface routines are +not included in the compiled {\tt UMFPACK/Lib/libumfpack.a} library, but left +as stand-alone compiled files ({\tt umf4\_f77wrapper.o} and +{\tt umf4\_f77wrapper64.o} in the {\tt Demo} directory). +You may need to modify the interface routines in the file +{\tt umf4\_f77wrapper.c} if you are using compilers for which this interface +has not been tested. + +%------------------------------------------------------------------------------- +\section{Known Issues} +%------------------------------------------------------------------------------- + +The Microsoft C or C++ compilers on a Pentium badly break the IEEE 754 standard, +and do not treat NaN's properly. According to IEEE 754, the expression +{\tt (x != x)} is supposed to be true if and only if {\tt x} is NaN. For +non-compliant compilers in Windows that expression is always false, and another +test must be used: {\tt (x < x)} is true if and only if {\tt x} +is NaN. For compliant compilers, {\tt (x < x)} is always false, for any +value of {\tt x} (including NaN). +To cover both cases, UMFPACK when running under Microsoft Windows +defines the following macro, which is true if and only if {\tt x} is NaN, +regardless of whether your compiler is compliant or not: + +\begin{verbatim} +#define SCALAR_IS_NAN(x) (((x) != (x)) || ((x) < (x))) +\end{verbatim} + +If your compiler breaks this test, then UMFPACK will fail catastrophically +if it encounters a NaN. You will not just see NaN's in your output; UMFPACK +will probably crash with a segmentation fault. In that case, you might try to +see if the common (but non-ANSI C) routine {\tt isnan} is available, and modify +the macro {\tt SCALAR\_IS\_NAN} in {\tt umf\_version.h} accordingly. The +simpler (and IEEE 754-compliant) test {\tt (x != x)} is always true with Linux +on a PC, and on every Unix compiler I have tested. + +Some compilers will complain about the Fortran BLAS being defined implicitly. +C prototypes for the BLAS are not used, except the C-BLAS. Some compilers +will complain about unrecognized {\tt \#pragma}'s. You may safely ignore +all of these warnings. + +%------------------------------------------------------------------------------- +\section{Future work} +\label{Future} +%------------------------------------------------------------------------------- + +Here are a few features that are not in UMFPACK Version 4.3, in no particular +order. They may appear in a future release of UMFPACK. If you are interested, +let me know and I could consider including them: + +\begin{enumerate} + +\item Future versions may have different default {\tt Control} parameters. + Future versions may return more statistics in the {\tt Info} array, and + they may use more entries in the {\tt Control} array. + These two arrays will probably become larger, since there are very few + unused entries. If they change in size, the constants + {\tt UMFPACK\_CONTROL} and {\tt UMFPACK\_INFO} defined in {\tt umfpack.h} + will be changed to reflect their new size. Your C program should use + these constants when declaring the size of these two arrays. Do not + define them as {\tt Control [20]} and {\tt Info [90]}. + +\item Forward/back solvers for the conventional row or column-form data + structure for $\m{L}$ and $\m{U}$ (the output of + {\tt umfpack\_*\_di\_get\_numeric}). This would enable a separate + solver that could be used to write a MATLAB mexFunction + {\tt x = lu\_refine (A, b, L, U, P, Q, R)} that gives MATLAB access + to the iterative refinement algorithm with sparse backward error + analysis. It would also be easier to handle sparse right-hand-sides + in this data structure, and end up with good asymptotic run-time + in this case + (particularly for $\m{Lx}=\m{b}$; see \cite{GilbertPeierls88}). + +\item Complex absolute value computations could be + based on FDLIBM (see \newline + http://www.netlib.org/fdlibm), + using the {\tt hypot(x,y)} routine. + +\item When using iterative refinement, the residual $\m{Ax}-\m{b}$ could be + returned by {\tt umfpack\_solve}. + +\item The solve routines could handle multiple right-hand sides, and sparse + right-hand sides. See {\tt umfpack\_solve} for the MATLAB version + of this feature. + +\item An option to redirect the error and diagnostic output. + +\item Permutation to block-triangular-form \cite{Duff78a} for the C-callable + interface. There are two routines in the ACM Collected + Algorithms (529 and 575) \cite{Duff81b,Duff78b} + that could be translated from Fortran + to C and included in UMFPACK. This would result in better performance + for matrices from circuit simulation and + chemical process engineering. See {\tt umfpack\_btf.m} for the MATLAB + version of this feature. + +\item The ability to use user-provided {\tt malloc}, {\tt free}, and + {\tt realloc} memory allocation routines. Note that UMFPACK makes very + few calls to these routines. You can do this at compile-time by + modifying the definitions of {\tt ALLOCATE}, {\tt FREE}, and + {\tt REALLOCATE} in the file {\tt umf\_internal.h}. Be sure to document + your changes carefully when you change UMFPACK source code. + +\item The ability to use user-provided work arrays, so that {\tt malloc}, + {\tt free}, and {\tt realloc} realloc are not called. The + {\tt umfpack\_*\_wsolve} routine is one example. + +\item A method that takes time proportional to the number of nonzeros in + $\m{A}$ to compute the symbolic factorization \cite{GilbertNgPeyton94}. + This would improve the performance of the symmetric and 2-by-2 strategies, + and the unsymmetric strategy when dense rows are present. + The current method takes + time proportional to the number of nonzeros in the upper bound of $\m{U}$. + The method used in UMFPACK exploits super-columns, however, so this + bound is rarely reached. + +\item The complex versions could use ANSI C99 {\tt double \_Complex} arguments, + and support the use of interleaved real/imaginary parts as input and output + arguments. The \newline + {\tt umfpack\_*\_report\_vector} routine is one example. + +\item Other basic sparse matrix operations, such as sparse matrix + multiplication, could be included. + +\item A more complete Fortran interface. + +\item A C++ interface. + +\item A parallel version using MPI. This would require a large amount + of effort. + +\end{enumerate} + + +%------------------------------------------------------------------------------- +\newpage +\section{The primary UMFPACK routines} +\label{Primary} +%------------------------------------------------------------------------------- + +The include files are the same for all four versions of +UMFPACK. The generic integer type is {\tt Int}, which is an {\tt int} or +{\tt long}, depending on which version of UMFPACK you are using. + +\subsection{umfpack\_*\_symbolic} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_symbolic.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_numeric} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_numeric.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_solve} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_solve.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_free\_symbolic} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_free_symbolic.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_free\_numeric} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_free_numeric.h via sed +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\newpage +\section{Alternative routines} +\label{Alternative} +%------------------------------------------------------------------------------- + +\subsection{umfpack\_*\_defaults} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_defaults.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_qsymbolic} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_qsymbolic.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_wsolve} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_wsolve.h via sed +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\newpage +\section{Matrix manipulation routines} +\label{Manipulate} +%------------------------------------------------------------------------------- + +\subsection{umfpack\_*\_col\_to\_triplet} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_col_to_triplet.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_triplet\_to\_col} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_triplet_to_col.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_transpose} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_transpose.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_scale} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_scale.h via sed +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\newpage +\section{Getting the contents of opaque objects} +\label{Get} +%------------------------------------------------------------------------------- + +\subsection{umfpack\_*\_get\_lunz} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_get_lunz.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_get\_numeric} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_get_numeric.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_get\_symbolic} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_get_symbolic.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_save\_numeric} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_save_numeric.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_load\_numeric} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_load_numeric.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_save\_symbolic} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_save_symbolic.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_load\_symbolic} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_load_symbolic.h via sed +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\newpage +\section{Reporting routines} +\label{Report} +%------------------------------------------------------------------------------- + +\subsection{umfpack\_*\_report\_status} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_report_status.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_report\_control} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_report_control.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_report\_info} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_report_info.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_report\_matrix} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_report_matrix.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_report\_numeric} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_report_numeric.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_report\_perm} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_report_perm.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_report\_symbolic} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_report_symbolic.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_report\_triplet} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_report_triplet.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_*\_report\_vector} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_report_vector.h via sed +\end{verbatim} +} + +%------------------------------------------------------------------------------- +\newpage +\section{Utility routines} +\label{Utility} +%------------------------------------------------------------------------------- + +\subsection{umfpack\_timer} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_timer.h via sed +\end{verbatim} +} + +\newpage +\subsection{umfpack\_tic and umfpack\_toc} + +{\footnotesize +\begin{verbatim} +INCLUDE umfpack_tictoc.h via sed +\end{verbatim} +} + + +%------------------------------------------------------------------------------- +\newpage +% References +%------------------------------------------------------------------------------- + +\bibliographystyle{plain} +\bibliography{UserGuide} + +\end{document} diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack.h new file mode 100644 index 0000000000..c1918f6e51 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack.h @@ -0,0 +1,405 @@ +/* ========================================================================== */ +/* === umfpack.h ============================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + This is the umfpack.h include file, and should be included in all user code + that uses UMFPACK. Do not include any of the umf_* header files in user + code. All routines in UMFPACK starting with "umfpack_" are user-callable. + All other routines are prefixed "umf_XY_", (where X is d or z, and Y is + i or l) and are not user-callable. +*/ + +#ifndef UMFPACK_H +#define UMFPACK_H + +/* -------------------------------------------------------------------------- */ +/* size of Info and Control arrays */ +/* -------------------------------------------------------------------------- */ + +#define UMFPACK_INFO 90 /* these might be larger in future versions */ +#define UMFPACK_CONTROL 20 + +/* -------------------------------------------------------------------------- */ +/* User-callable routines */ +/* -------------------------------------------------------------------------- */ + +/* Primary routines: */ +#include "umfpack_symbolic.h" +#include "umfpack_numeric.h" +#include "umfpack_solve.h" +#include "umfpack_free_symbolic.h" +#include "umfpack_free_numeric.h" + +/* Alternative routines: */ +#include "umfpack_defaults.h" +#include "umfpack_qsymbolic.h" +#include "umfpack_wsolve.h" + +/* Matrix manipulation routines: */ +#include "umfpack_triplet_to_col.h" +#include "umfpack_col_to_triplet.h" +#include "umfpack_transpose.h" +#include "umfpack_scale.h" + +/* Getting the contents of the Symbolic and Numeric opaque objects: */ +#include "umfpack_get_lunz.h" +#include "umfpack_get_numeric.h" +#include "umfpack_get_symbolic.h" +#include "umfpack_save_numeric.h" +#include "umfpack_load_numeric.h" +#include "umfpack_save_symbolic.h" +#include "umfpack_load_symbolic.h" + +/* Reporting routines (the above 14 routines print nothing): */ +#include "umfpack_report_status.h" +#include "umfpack_report_info.h" +#include "umfpack_report_control.h" +#include "umfpack_report_matrix.h" +#include "umfpack_report_triplet.h" +#include "umfpack_report_vector.h" +#include "umfpack_report_symbolic.h" +#include "umfpack_report_numeric.h" +#include "umfpack_report_perm.h" + +/* Utility routines: */ +#include "umfpack_timer.h" +#include "umfpack_tictoc.h" + +/* -------------------------------------------------------------------------- */ +/* Version, copyright, and license */ +/* -------------------------------------------------------------------------- */ + +#define UMFPACK_VERSION "UMFPACK V4.3 (Jan. 16, 2004)" + +#define UMFPACK_COPYRIGHT \ +"UMFPACK: Copyright (c) 2004 by Timothy A. Davis. All Rights Reserved.\n" + +#define UMFPACK_LICENSE_PART1 \ +"\nUMFPACK License:\n" \ +"\n" \ +" Your use or distribution of UMFPACK or any modified version of\n" \ +" UMFPACK implies that you agree to this License.\n" \ +"\n" \ +" THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY\n" \ +" EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK.\n" +#define UMFPACK_LICENSE_PART2 \ +"\n" \ +" Permission is hereby granted to use or copy this program, provided\n" \ +" that the Copyright, this License, and the Availability of the original\n" \ +" version is retained on all copies. User documentation of any code that\n" \ +" uses UMFPACK or any modified version of UMFPACK code must cite the\n" \ +" Copyright, this License, the Availability note, and \"Used by permission.\"\n" +#define UMFPACK_LICENSE_PART3 \ +" Permission to modify the code and to distribute modified code is granted,\n" \ +" provided the Copyright, this License, and the Availability note are\n" \ +" retained, and a notice that the code was modified is included. This\n" \ +" software was developed with support from the National Science Foundation,\n" \ +" and is provided to you free of charge.\n" \ +"\n" \ +"Availability: http://www.cise.ufl.edu/research/sparse/umfpack\n" \ +"\n" + +/* -------------------------------------------------------------------------- */ +/* contents of Info */ +/* -------------------------------------------------------------------------- */ + +/* Note that umfpack_report.m must coincide with these definitions. */ + +/* returned by all routines that use Info: */ +#define UMFPACK_STATUS 0 /* UMFPACK_OK, or other result */ +#define UMFPACK_NROW 1 /* n_row input value */ +#define UMFPACK_NCOL 16 /* n_col input value */ +#define UMFPACK_NZ 2 /* # of entries in A */ + +/* computed in UMFPACK_*symbolic and UMFPACK_numeric: */ +#define UMFPACK_SIZE_OF_UNIT 3 /* sizeof (Unit) */ + +/* computed in UMFPACK_*symbolic: */ +#define UMFPACK_SIZE_OF_INT 4 /* sizeof (int) */ +#define UMFPACK_SIZE_OF_LONG 5 /* sizeof (long) */ +#define UMFPACK_SIZE_OF_POINTER 6 /* sizeof (void *) */ +#define UMFPACK_SIZE_OF_ENTRY 7 /* sizeof (Entry), real or complex */ +#define UMFPACK_NDENSE_ROW 8 /* number of dense rows */ +#define UMFPACK_NEMPTY_ROW 9 /* number of empty rows */ +#define UMFPACK_NDENSE_COL 10 /* number of dense rows */ +#define UMFPACK_NEMPTY_COL 11 /* number of empty rows */ +#define UMFPACK_SYMBOLIC_DEFRAG 12 /* # of memory compactions */ +#define UMFPACK_SYMBOLIC_PEAK_MEMORY 13 /* memory used by symbolic analysis */ +#define UMFPACK_SYMBOLIC_SIZE 14 /* size of Symbolic object, in Units */ +#define UMFPACK_SYMBOLIC_TIME 15 /* time (sec.) for symbolic analysis */ +#define UMFPACK_SYMBOLIC_WALLTIME 17 /* wall clock time for sym. analysis */ +#define UMFPACK_STRATEGY_USED 18 /* strategy used: sym, unsym, 2by2 */ +#define UMFPACK_ORDERING_USED 19 /* ordering used: colamd, amd, given */ +#define UMFPACK_QFIXED 31 /* whether Q is fixed or refined */ +#define UMFPACK_DIAG_PREFERRED 32 /* whether diagonal pivoting attempted*/ +#define UMFPACK_PATTERN_SYMMETRY 33 /* symmetry of pattern of S */ +#define UMFPACK_NZ_A_PLUS_AT 34 /* nnz (S+S'), excl. diagonal */ +#define UMFPACK_NZDIAG 35 /* nnz (diag (S)) */ + +/* AMD statistics, computed in UMFPACK_*symbolic: */ +#define UMFPACK_SYMMETRIC_LUNZ 36 /* nz in L+U, if AMD ordering used */ +#define UMFPACK_SYMMETRIC_FLOPS 37 /* flops for LU, if AMD ordering used */ +#define UMFPACK_SYMMETRIC_NDENSE 38 /* # of "dense" rows/cols in S+S' */ +#define UMFPACK_SYMMETRIC_DMAX 39 /* max nz in cols of L, for AMD */ + +/* statistics for 2-by-2 strategy */ +#define UMFPACK_2BY2_NWEAK 51 /* number of weak diagonal entries*/ +#define UMFPACK_2BY2_UNMATCHED 52 /* # of weak diagonals not matched*/ +#define UMFPACK_2BY2_PATTERN_SYMMETRY 53 /* symmetry of pattern of P*S */ +#define UMFPACK_2BY2_NZ_PA_PLUS_PAT 54 /* nz in PS+(PS)' */ +#define UMFPACK_2BY2_NZDIAG 55 /* nz on diagonal of PS+(PS)' */ + +/* statistcs for singleton pruning */ +#define UMFPACK_COL_SINGLETONS 56 +#define UMFPACK_ROW_SINGLETONS 57 +#define UMFPACK_N2 58 +#define UMFPACK_S_SYMMETRIC 59 + +/* estimates computed in UMFPACK_*symbolic: */ +#define UMFPACK_NUMERIC_SIZE_ESTIMATE 20 /* final size of Numeric->Memory */ +#define UMFPACK_PEAK_MEMORY_ESTIMATE 21 /* for symbolic & numeric */ +#define UMFPACK_FLOPS_ESTIMATE 22 /* flop count */ +#define UMFPACK_LNZ_ESTIMATE 23 /* nz in L, incl. diagonal */ +#define UMFPACK_UNZ_ESTIMATE 24 /* nz in U, incl. diagonal */ +#define UMFPACK_VARIABLE_INIT_ESTIMATE 25 /* initial size of Numeric->Memory*/ +#define UMFPACK_VARIABLE_PEAK_ESTIMATE 26 /* peak size of Numeric->Memory */ +#define UMFPACK_VARIABLE_FINAL_ESTIMATE 27 /* final size of Numeric->Memory */ +#define UMFPACK_MAX_FRONT_SIZE_ESTIMATE 28 /* max frontal matrix size */ +#define UMFPACK_MAX_FRONT_NROWS_ESTIMATE 29 /* max # rows in any front */ +#define UMFPACK_MAX_FRONT_NCOLS_ESTIMATE 30 /* max # columns in any front */ + +/* exact values, (estimates shown above) computed in UMFPACK_numeric: */ +#define UMFPACK_NUMERIC_SIZE 40 /* final size of Numeric->Memory */ +#define UMFPACK_PEAK_MEMORY 41 /* for symbolic & numeric */ +#define UMFPACK_FLOPS 42 /* flop count */ +#define UMFPACK_LNZ 43 /* nz in L, incl. diagonal */ +#define UMFPACK_UNZ 44 /* nz in U, incl. diagonal */ +#define UMFPACK_VARIABLE_INIT 45 /* initial size of Numeric->Memory*/ +#define UMFPACK_VARIABLE_PEAK 46 /* peak size of Numeric->Memory */ +#define UMFPACK_VARIABLE_FINAL 47 /* final size of Numeric->Memory */ +#define UMFPACK_MAX_FRONT_SIZE 48 /* max frontal matrix size */ +#define UMFPACK_MAX_FRONT_NROWS 49 /* max # rows in any front */ +#define UMFPACK_MAX_FRONT_NCOLS 50 /* max # columns in any front */ + +/* computed in UMFPACK_numeric: */ +#define UMFPACK_NUMERIC_DEFRAG 60 /* # of garbage collections */ +#define UMFPACK_NUMERIC_REALLOC 61 /* # of memory reallocations */ +#define UMFPACK_NUMERIC_COSTLY_REALLOC 62 /* # of costlly memory realloc's */ +#define UMFPACK_COMPRESSED_PATTERN 63 /* # of integers in LU pattern */ +#define UMFPACK_LU_ENTRIES 64 /* # of reals in LU factors */ +#define UMFPACK_NUMERIC_TIME 65 /* numeric factorization time */ +#define UMFPACK_UDIAG_NZ 66 /* nz on diagonal of U */ +#define UMFPACK_RCOND 67 /* est. reciprocal condition # */ +#define UMFPACK_WAS_SCALED 68 /* none, max row, or sum row */ +#define UMFPACK_RSMIN 69 /* min (max row) or min (sum row) */ +#define UMFPACK_RSMAX 70 /* max (max row) or max (sum row) */ +#define UMFPACK_UMIN 71 /* min abs diagonal entry of U */ +#define UMFPACK_UMAX 72 /* max abs diagonal entry of U */ +#define UMFPACK_ALLOC_INIT_USED 73 /* alloc_init parameter used */ +#define UMFPACK_FORCED_UPDATES 74 /* # of forced updates */ +#define UMFPACK_NUMERIC_WALLTIME 75 /* numeric wall clock time */ +#define UMFPACK_NOFF_DIAG 76 /* number of off-diagonal pivots */ + +#define UMFPACK_ALL_LNZ 77 /* nz in L, if no dropped entries */ +#define UMFPACK_ALL_UNZ 78 /* nz in U, if no dropped entries */ +#define UMFPACK_NZDROPPED 79 /* # of dropped small entries */ + +/* computed in UMFPACK_solve: */ +#define UMFPACK_IR_TAKEN 80 /* # of iterative refinement steps taken */ +#define UMFPACK_IR_ATTEMPTED 81 /* # of iter. refinement steps attempted */ +#define UMFPACK_OMEGA1 82 /* omega1, sparse backward error estimate */ +#define UMFPACK_OMEGA2 83 /* omega2, sparse backward error estimate */ +#define UMFPACK_SOLVE_FLOPS 84 /* flop count for solve */ +#define UMFPACK_SOLVE_TIME 85 /* solve time (seconds) */ +#define UMFPACK_SOLVE_WALLTIME 86 /* solve time (wall clock, seconds) */ + +/* Info [87, 88, 89] unused */ + +/* Unused parts of Info may be used in future versions of UMFPACK. */ + +/* -------------------------------------------------------------------------- */ + +/* Info [UMFPACK_ORDERING_USED] is one of the following: */ +#define UMFPACK_ORDERING_COLAMD 0 /* COLAMD(A) */ +#define UMFPACK_ORDERING_AMD 1 /* AMD(A+A') */ +#define UMFPACK_ORDERING_GIVEN 2 /* Q is provided on input */ + +/* -------------------------------------------------------------------------- */ +/* contents of Control */ +/* -------------------------------------------------------------------------- */ + +/* used in all UMFPACK_report_* routines: */ +#define UMFPACK_PRL 0 /* print level */ + +/* used in UMFPACK_*symbolic only: */ +#define UMFPACK_DENSE_ROW 1 /* dense row parameter */ +#define UMFPACK_DENSE_COL 2 /* dense col parameter */ +#define UMFPACK_BLOCK_SIZE 4 /* BLAS-3 block size */ +#define UMFPACK_STRATEGY 5 /* auto, symmetric, unsym., or 2by2 */ +#define UMFPACK_2BY2_TOLERANCE 12 /* 2-by-2 pivot tolerance */ +#define UMFPACK_FIXQ 13 /* -1: no fixQ, 0: default, 1: fixQ */ +#define UMFPACK_AMD_DENSE 14 /* for AMD ordering */ +#define UMFPACK_AGGRESSIVE 19 /* whether or not to use aggressive + * absorption in AMD and COLAMD */ + +/* used in UMFPACK_numeric only: */ +#define UMFPACK_PIVOT_TOLERANCE 3 /* threshold partial pivoting setting */ +#define UMFPACK_ALLOC_INIT 6 /* initial allocation ratio */ +#define UMFPACK_SYM_PIVOT_TOLERANCE 15 /* threshold, only for diag. entries */ +#define UMFPACK_SCALE 16 /* what row scaling to do */ +#define UMFPACK_FRONT_ALLOC_INIT 17 /* frontal matrix allocation ratio */ +#define UMFPACK_DROPTOL 18 /* drop tolerance for entries in L,U */ + +/* used in UMFPACK_*solve only: */ +#define UMFPACK_IRSTEP 7 /* max # of iterative refinements */ + +/* compile-time settings - Control [8..11] cannot be changed at run time: */ +#define UMFPACK_COMPILED_WITH_BLAS 8 /* uses the BLAS */ +#define UMFPACK_COMPILED_FOR_MATLAB 9 /* 1 if MATLAB mexFunction, etc. */ +#define UMFPACK_COMPILED_WITH_GETRUSAGE 10 /* uses getrusage timer, or not */ +#define UMFPACK_COMPILED_IN_DEBUG_MODE 11 /* debugging enabled (very slow!) */ + +#if 0 +/* No longer unused. These parameters are now used for the new symmetric and + * 2-by-2 ordering strategies. See 5, 12, 13, and 14, above. */ +#define UMFPACK_RELAXED_AMALGAMATION 5 /* unused (was in v4.0) */ +#define UMFPACK_PIVOT_OPTION 12 /* unused (was in v3.2) */ +#define UMFPACK_RELAXED2_AMALGAMATION 13 /* unused (was in v4.0) */ +#define UMFPACK_RELAXED3_AMALGAMATION 14 /* unused (was in v4.0) */ +#endif + +/* -------------------------------------------------------------------------- */ + +/* Control [UMFPACK_STRATEGY] is one of the following: */ +#define UMFPACK_STRATEGY_AUTO 0 /* use sym. or unsym. strategy */ +#define UMFPACK_STRATEGY_UNSYMMETRIC 1 /* COLAMD(A), coletree postorder, + not prefer diag*/ +#define UMFPACK_STRATEGY_2BY2 2 /* AMD(PA+PA'), no coletree postorder, + prefer diag(PA) where P is pseudo + max transversal */ +#define UMFPACK_STRATEGY_SYMMETRIC 3 /* AMD(A+A'), no coletree postorder, + prefer diagonal */ + +/* Control [UMFPACK_SCALE] is one of the following: */ +#define UMFPACK_SCALE_NONE 0 /* no scaling */ +#define UMFPACK_SCALE_SUM 1 /* default: divide each row by sum (abs (row))*/ +#define UMFPACK_SCALE_MAX 2 /* divide each row by max (abs (row)) */ + +/* -------------------------------------------------------------------------- */ +/* default values of Control: */ +/* -------------------------------------------------------------------------- */ + +/* Note that the default block sized changed for Version 3.1 and following. + * In Version 4.1, the relaxed amalgamation parameters were removed. These are + * now fixed internally (see umf_local_search.c), and cannot be changed. + * COLAMD aggressive absorption did not exist in v4.0. In v4.1, it is in + * use by default (but can be turned off). Aggressive absorption is used by + * default in AMD, also. + * + * Drop tolerance was added in V4.2. + */ + +#define UMFPACK_DEFAULT_PRL 1 +#define UMFPACK_DEFAULT_DENSE_ROW 0.2 +#define UMFPACK_DEFAULT_DENSE_COL 0.2 +#define UMFPACK_DEFAULT_PIVOT_TOLERANCE 0.1 +#define UMFPACK_DEFAULT_2BY2_TOLERANCE 0.01 +#define UMFPACK_DEFAULT_SYM_PIVOT_TOLERANCE 0.001 +#define UMFPACK_DEFAULT_BLOCK_SIZE 32 +#define UMFPACK_DEFAULT_ALLOC_INIT 0.7 +#define UMFPACK_DEFAULT_FRONT_ALLOC_INIT 0.5 +#define UMFPACK_DEFAULT_IRSTEP 2 +#define UMFPACK_DEFAULT_SCALE UMFPACK_SCALE_SUM +#define UMFPACK_DEFAULT_STRATEGY UMFPACK_STRATEGY_AUTO +#define UMFPACK_DEFAULT_AMD_DENSE AMD_DEFAULT_DENSE +#define UMFPACK_DEFAULT_FIXQ 0 +#define UMFPACK_DEFAULT_AGGRESSIVE 1 +#define UMFPACK_DEFAULT_DROPTOL 0 + +#if 0 +/* no longer unused: for unsymmetric strategy (were used in v4.0) */ +#define UMFPACK_DEFAULT_RELAXED_AMALGAMATION 0.25 /* unused */ +#define UMFPACK_DEFAULT_RELAXED2_AMALGAMATION 0.1 /* unused */ +#define UMFPACK_DEFAULT_RELAXED3_AMALGAMATION 0.125 /* unused */ +#endif + +/* default values of Control may change in future versions of UMFPACK. */ + +/* -------------------------------------------------------------------------- */ +/* status codes */ +/* -------------------------------------------------------------------------- */ + +#define UMFPACK_OK (0) + +/* status > 0 means a warning, but the method was successful anyway. */ +/* A Symbolic or Numeric object was still created. */ +#define UMFPACK_WARNING_singular_matrix (1) + +/* status < 0 means an error, and the method was not successful. */ +/* No Symbolic of Numeric object was created. */ +#define UMFPACK_ERROR_out_of_memory (-1) +#define UMFPACK_ERROR_invalid_Numeric_object (-3) +#define UMFPACK_ERROR_invalid_Symbolic_object (-4) +#define UMFPACK_ERROR_argument_missing (-5) +#define UMFPACK_ERROR_n_nonpositive (-6) +#define UMFPACK_ERROR_invalid_matrix (-8) /* replaces errors -[7:10,12,14] */ +#define UMFPACK_ERROR_different_pattern (-11) +#define UMFPACK_ERROR_invalid_system (-13) +#define UMFPACK_ERROR_invalid_permutation (-15) +#define UMFPACK_ERROR_internal_error (-911) +#define UMFPACK_ERROR_file_IO (-17) + +/* The following error codes are no longer used. They are left in for + * historical reasons. They appeared in Version 4.0. Most of them are combined + * into the single UMFPACK_ERROR_invalid_matrix error code (-8). The last one, + * UMFPACK_ERROR_problem_too_large, has been removed. This error can no longer + * occur. */ +#define UMFPACK_ERROR_nz_negative (-7) /* unused */ +#define UMFPACK_ERROR_jumbled_matrix (-8) /* unused */ +#define UMFPACK_ERROR_Ap0_nonzero (-9) /* unused */ +#define UMFPACK_ERROR_row_index_out_of_bounds (-10) /* unused */ +#define UMFPACK_ERROR_col_length_negative (-12) /* unused */ +#define UMFPACK_ERROR_invalid_triplet (-14) /* unused */ +#define UMFPACK_ERROR_problem_too_large (-16) /* unused */ + +/* -------------------------------------------------------------------------- */ +/* solve codes */ +/* -------------------------------------------------------------------------- */ + +/* Solve the system ( )x=b, where ( ) is defined below. "t" refers to the */ +/* linear algebraic transpose (complex conjugate if A is complex), or the (') */ +/* operator in MATLAB. "at" refers to the array transpose, or the (.') */ +/* operator in MATLAB. */ + +#define UMFPACK_A (0) /* Ax=b */ +#define UMFPACK_At (1) /* A'x=b */ +#define UMFPACK_Aat (2) /* A.'x=b */ + +#define UMFPACK_Pt_L (3) /* P'Lx=b */ +#define UMFPACK_L (4) /* Lx=b */ +#define UMFPACK_Lt_P (5) /* L'Px=b */ +#define UMFPACK_Lat_P (6) /* L.'Px=b */ +#define UMFPACK_Lt (7) /* L'x=b */ +#define UMFPACK_Lat (8) /* L.'x=b */ + +#define UMFPACK_U_Qt (9) /* UQ'x=b */ +#define UMFPACK_U (10) /* Ux=b */ +#define UMFPACK_Q_Ut (11) /* QU'x=b */ +#define UMFPACK_Q_Uat (12) /* QU.'x=b */ +#define UMFPACK_Ut (13) /* U'x=b */ +#define UMFPACK_Uat (14) /* U.'x=b */ + +/* -------------------------------------------------------------------------- */ + +/* Integer constants are used for status and solve codes instead of enum */ +/* to make it easier for a Fortran code to call UMFPACK. */ + +#endif /* UMFPACK_H */ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_col_to_triplet.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_col_to_triplet.h new file mode 100644 index 0000000000..54cb72237f --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_col_to_triplet.h @@ -0,0 +1,111 @@ +/* ========================================================================== */ +/* === umfpack_col_to_triplet =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_col_to_triplet +( + int n_col, + const int Ap [ ], + int Tj [ ] +) ; + +long umfpack_dl_col_to_triplet +( + long n_col, + const long Ap [ ], + long Tj [ ] +) ; + +int umfpack_zi_col_to_triplet +( + int n_col, + const int Ap [ ], + int Tj [ ] +) ; + +long umfpack_zl_col_to_triplet +( + long n_col, + const long Ap [ ], + long Tj [ ] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int n_col, *Tj, *Ap, status ; + status = umfpack_di_col_to_triplet (n_col, Ap, Tj) ; + +double long Syntax: + + #include "umfpack.h" + long n_col, *Tj, *Ap, status ; + status = umfpack_dl_col_to_triplet (n_col, Ap, Tj) ; + +complex int Syntax: + + #include "umfpack.h" + int n_col, *Tj, *Ap, status ; + status = umfpack_zi_col_to_triplet (n_col, Ap, Tj) ; + +complex long Syntax: + + #include "umfpack.h" + long n_col, *Tj, *Ap, status ; + status = umfpack_zl_col_to_triplet (n_col, Ap, Tj) ; + +Purpose: + + Converts a column-oriented matrix to a triplet form. Only the column + pointers, Ap, are required, and only the column indices of the triplet form + are constructed. This routine is the opposite of umfpack_*_triplet_to_col. + The matrix may be singular and/or rectangular. Analogous to [i, Tj, x] = + find (A) in MATLAB, except that zero entries present in the column-form of + A are present in the output, and i and x are not created (those are just Ai + and Ax+Az*1i, respectively, for a column-form matrix A). + +Returns: + + UMFPACK_OK if successful + UMFPACK_ERROR_argument_missing if Ap or Tj is missing + UMFPACK_ERROR_n_nonpositive if n_col <= 0 + UMFPACK_ERROR_invalid_matrix if Ap [n_col] < 0, Ap [0] != 0, or + Ap [j] > Ap [j+1] for any j in the range 0 to n-1. + Unsorted columns and duplicate entries do not cause an error (these would + only be evident by examining Ai). Empty rows and columns are OK. + +Arguments: + + Int n_col ; Input argument, not modified. + + A is an n_row-by-n_col matrix. Restriction: n_col > 0. + (n_row is not required) + + Int Ap [n_col+1] ; Input argument, not modified. + + The column pointers of the column-oriented form of the matrix. See + umfpack_*_*symbolic for a description. The number of entries in + the matrix is nz = Ap [n_col]. Restrictions on Ap are the same as those + for umfpack_*_transpose. Ap [0] must be zero, nz must be >= 0, and + Ap [j] <= Ap [j+1] and Ap [j] <= Ap [n_col] must be true for all j in + the range 0 to n_col-1. Empty columns are OK (that is, Ap [j] may equal + Ap [j+1] for any j in the range 0 to n_col-1). + + Int Tj [nz] ; Output argument. + + Tj is an integer array of size nz on input, where nz = Ap [n_col]. + Suppose the column-form of the matrix is held in Ap, Ai, Ax, and Az + (see umfpack_*_*symbolic for a description). Then on output, the + triplet form of the same matrix is held in Ai (row indices), Tj (column + indices), and Ax (numerical values). Note, however, that this routine + does not require Ai and Ax (or Az for the complex version) in order to + do the conversion. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_defaults.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_defaults.h new file mode 100644 index 0000000000..ee6106914a --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_defaults.h @@ -0,0 +1,75 @@ +/* ========================================================================== */ +/* === umfpack_defaults ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +void umfpack_di_defaults +( + double Control [UMFPACK_CONTROL] +) ; + +void umfpack_dl_defaults +( + double Control [UMFPACK_CONTROL] +) ; + +void umfpack_zi_defaults +( + double Control [UMFPACK_CONTROL] +) ; + +void umfpack_zl_defaults +( + double Control [UMFPACK_CONTROL] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + umfpack_di_defaults (Control) ; + +double long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + umfpack_dl_defaults (Control) ; + +complex int Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + umfpack_zi_defaults (Control) ; + +complex long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + umfpack_zl_defaults (Control) ; + +Purpose: + + Sets the default control parameter settings. + + NOTE: new control parameters have been added to the Control array for + Version 4.1. These entries were unused in Version 4.0. The default block + size for the BLAS has increased from 24 to 32. Some rarely used control + parameters have been removed (those that controlled relaxed amalgamation). + +Arguments: + + double Control [UMFPACK_CONTROL] ; Output argument. + + Control is set to the default control parameter settings. You can + then modify individual settings by changing specific entries in the + Control array. If Control is a (double *) NULL pointer, then + umfpack_*_defaults returns silently (no error is generated, since + passing a NULL pointer for Control to any UMFPACK routine is valid). +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_free_numeric.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_free_numeric.h new file mode 100644 index 0000000000..e0b4ea7d41 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_free_numeric.h @@ -0,0 +1,68 @@ +/* ========================================================================== */ +/* === umfpack_free_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +void umfpack_di_free_numeric +( + void **Numeric +) ; + +void umfpack_dl_free_numeric +( + void **Numeric +) ; + +void umfpack_zi_free_numeric +( + void **Numeric +) ; + +void umfpack_zl_free_numeric +( + void **Numeric +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Numeric ; + umfpack_di_free_numeric (&Numeric) ; + +double long Syntax: + + #include "umfpack.h" + void *Numeric ; + umfpack_dl_free_numeric (&Numeric) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + umfpack_zi_free_numeric (&Numeric) ; + +complex long Syntax: + + #include "umfpack.h" + void *Numeric ; + umfpack_zl_free_numeric (&Numeric) ; + +Purpose: + + Deallocates the Numeric object and sets the Numeric handle to NULL. This + routine is the only valid way of destroying the Numeric object. + +Arguments: + + void **Numeric ; Input argument, set to (void *) NULL on output. + + Numeric points to a valid Numeric object, computed by umfpack_*_numeric. + No action is taken if Numeric is a (void *) NULL pointer. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_free_symbolic.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_free_symbolic.h new file mode 100644 index 0000000000..103988a7f6 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_free_symbolic.h @@ -0,0 +1,68 @@ +/* ========================================================================== */ +/* === umfpack_free_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +void umfpack_di_free_symbolic +( + void **Symbolic +) ; + +void umfpack_dl_free_symbolic +( + void **Symbolic +) ; + +void umfpack_zi_free_symbolic +( + void **Symbolic +) ; + +void umfpack_zl_free_symbolic +( + void **Symbolic +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Symbolic ; + umfpack_di_free_symbolic (&Symbolic) ; + +double long Syntax: + + #include "umfpack.h" + void *Symbolic ; + umfpack_dl_free_symbolic (&Symbolic) ; + +complex int Syntax: + + #include "umfpack.h" + void *Symbolic ; + umfpack_zi_free_symbolic (&Symbolic) ; + +complex long Syntax: + + #include "umfpack.h" + void *Symbolic ; + umfpack_zl_free_symbolic (&Symbolic) ; + +Purpose: + + Deallocates the Symbolic object and sets the Symbolic handle to NULL. This + routine is the only valid way of destroying the Symbolic object. + +Arguments: + + void **Symbolic ; Input argument, set to (void *) NULL on output. + + Points to a valid Symbolic object computed by umfpack_*_symbolic. + No action is taken if Symbolic is a (void *) NULL pointer. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_lunz.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_lunz.h new file mode 100644 index 0000000000..8f9d4b0fab --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_lunz.h @@ -0,0 +1,134 @@ +/* ========================================================================== */ +/* === umfpack_get_lunz ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_get_lunz +( + int *lnz, + int *unz, + int *n_row, + int *n_col, + int *nz_udiag, + void *Numeric +) ; + +long umfpack_dl_get_lunz +( + long *lnz, + long *unz, + long *n_row, + long *n_col, + long *nz_udiag, + void *Numeric +) ; + +int umfpack_zi_get_lunz +( + int *lnz, + int *unz, + int *n_row, + int *n_col, + int *nz_udiag, + void *Numeric +) ; + +long umfpack_zl_get_lunz +( + long *lnz, + long *unz, + long *n_row, + long *n_col, + long *nz_udiag, + void *Numeric +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status, lnz, unz, n_row, n_col ; + status = umfpack_di_get_lunz (&lnz, &unz, &n_row, &n_col, Numeric) ; + +double long Syntax: + + #include "umfpack.h" + void *Numeric ; + long status, lnz, unz, n_row, n_col ; + status = umfpack_dl_get_lunz (&lnz, &unz, &n_row, &n_col, Numeric) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status, lnz, unz, n_row, n_col ; + status = umfpack_zi_get_lunz (&lnz, &unz, &n_row, &n_col, Numeric) ; + +complex long Syntax: + + #include "umfpack.h" + void *Numeric ; + long status, lnz, unz, n_row, n_col ; + status = umfpack_zl_get_lunz (&lnz, &unz, &n_row, &n_col, Numeric) ; + +Purpose: + + Determines the size and number of nonzeros in the LU factors held by the + Numeric object. These are also the sizes of the output arrays required + by umfpack_*_get_numeric. + + The matrix L is n_row -by- min(n_row,n_col), with lnz nonzeros, including + the entries on the unit diagonal of L. + + The matrix U is min(n_row,n_col) -by- n_col, with unz nonzeros, including + nonzeros on the diagonal of U. + +Returns: + + UMFPACK_OK if successful. + UMFPACK_ERROR_invalid_Numeric_object if Numeric is not a valid object. + UMFPACK_ERROR_argument_missing if any other argument is (Int *) NULL. + +Arguments: + + Int *lnz ; Output argument. + + The number of nonzeros in L, including the diagonal (which is all + one's). This value is the required size of the Lj and Lx arrays as + computed by umfpack_*_get_numeric. The value of lnz is identical to + Info [UMFPACK_LNZ], if that value was returned by umfpack_*_numeric. + + Int *unz ; Output argument. + + The number of nonzeros in U, including the diagonal. This value is the + required size of the Ui and Ux arrays as computed by + umfpack_*_get_numeric. The value of unz is identical to + Info [UMFPACK_UNZ], if that value was returned by umfpack_*_numeric. + + Int *n_row ; Output argument. + Int *n_col ; Output argument. + + The order of the L and U matrices. L is n_row -by- min(n_row,n_col) + and U is min(n_row,n_col) -by- n_col. + + Int *nz_udiag ; Output argument. + + The number of numerically nonzero values on the diagonal of U. The + matrix is singular if nz_diag < min(n_row,n_col). A divide-by-zero + will occur if nz_diag < n_row == n_col when solving a sparse system + involving the matrix U in umfpack_*_*solve. The value of nz_udiag is + identical to Info [UMFPACK_UDIAG_NZ] if that value was returned by + umfpack_*_numeric. + + void *Numeric ; Input argument, not modified. + + Numeric must point to a valid Numeric object, computed by + umfpack_*_numeric. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_numeric.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_numeric.h new file mode 100644 index 0000000000..42001c4b83 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_numeric.h @@ -0,0 +1,253 @@ +/* ========================================================================== */ +/* === umfpack_get_numeric ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_get_numeric +( + int Lp [ ], + int Lj [ ], + double Lx [ ], + int Up [ ], + int Ui [ ], + double Ux [ ], + int P [ ], + int Q [ ], + double Dx [ ], + int *do_recip, + double Rs [ ], + void *Numeric +) ; + +long umfpack_dl_get_numeric +( + long Lp [ ], + long Lj [ ], + double Lx [ ], + long Up [ ], + long Ui [ ], + double Ux [ ], + long P [ ], + long Q [ ], + double Dx [ ], + long *do_recip, + double Rs [ ], + void *Numeric +) ; + +int umfpack_zi_get_numeric +( + int Lp [ ], + int Lj [ ], + double Lx [ ], double Lz [ ], + int Up [ ], + int Ui [ ], + double Ux [ ], double Uz [ ], + int P [ ], + int Q [ ], + double Dx [ ], double Dz [ ], + int *do_recip, + double Rs [ ], + void *Numeric +) ; + +long umfpack_zl_get_numeric +( + long Lp [ ], + long Lj [ ], + double Lx [ ], double Lz [ ], + long Up [ ], + long Ui [ ], + double Ux [ ], double Uz [ ], + long P [ ], + long Q [ ], + double Dx [ ], double Dz [ ], + long *do_recip, + double Rs [ ], + void *Numeric +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Numeric ; + int *Lp, *Lj, *Up, *Ui, *P, *Q, status, do_recip ; + double *Lx, *Ux, *Dx, *Rs ; + status = umfpack_di_get_numeric (Lp, Lj, Lx, Up, Ui, Ux, P, Q, Dx, + &do_recip, Rs, Numeric) ; + +double long Syntax: + + #include "umfpack.h" + void *Numeric ; + long *Lp, *Lj, *Up, *Ui, *P, *Q, status, do_recip ; + double *Lx, *Ux, *Dx, *Rs ; + status = umfpack_dl_get_numeric (Lp, Lj, Lx, Up, Ui, Ux, P, Q, Dx, + &do_recip, Rs, Numeric) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + int *Lp, *Lj, *Up, *Ui, *P, *Q, status, do_recip ; + double *Lx, *Lz, *Ux, *Uz, *Dx, *Dz, *Rs ; + status = umfpack_zi_get_numeric (Lp, Lj, Lx, Lz, Up, Ui, Ux, Uz, P, Q, + Dx, Dz, &do_recip, Rs, Numeric) ; + +complex long Syntax: + + #include "umfpack.h" + void *Numeric ; + long *Lp, *Lj, *Up, *Ui, *P, *Q, status, do_recip ; + double *Lx, *Lz, *Ux, *Uz, *Dx, *Dz, *Rs ; + status = umfpack_zl_get_numeric (Lp, Lj, Lx, Lz, Up, Ui, Ux, Uz, P, Q, + Dx, Dz, &do_recip, Rs, Numeric) ; + +Purpose: + + This routine copies the LU factors and permutation vectors from the Numeric + object into user-accessible arrays. This routine is not needed to solve a + linear system. Note that the output arrays Lp, Lj, Lx, Up, Ui, Ux, P, Q, + Dx, and Rs are not allocated by umfpack_*_get_numeric; they must exist on + input. Similarly, Lz, Uz and Dz must exist on input for the complex + versions. + + All output arguments are optional. If any of them are NULL + on input, then that part of the LU factorization is not copied. You can + use this routine to extract just the parts of the LU factorization that + you want. For example, to retrieve just the column permutation Q, use: + + #define noD (double *) NULL + #define noI (int *) NULL + status = umfpack_di_get_numeric (noI, noI, noD, noI, noI, noD, noI, + Q, noD, noI, noD, Numeric) ; + +Returns: + + Returns UMFPACK_OK if successful. Returns UMFPACK_ERROR_out_of_memory + if insufficient memory is available for the 2*max(n_row,n_col) integer + workspace that umfpack_*_get_numeric allocates to construct L and/or U. + Returns UMFPACK_ERROR_invalid_Numeric_object if the Numeric object provided + as input is invalid. + +Arguments: + + Int Lp [n_row+1] ; Output argument. + Int Lj [lnz] ; Output argument. + double Lx [lnz] ; Output argument. + double Lz [lnz] ; Output argument for complex versions. + + The n_row-by-min(n_row,n_col) matrix L is returned in compressed-row + form. The column indices of row i and corresponding numerical values + are in: + + Lj [Lp [i] ... Lp [i+1]-1] + Lx [Lp [i] ... Lp [i+1]-1] real part + Lz [Lp [i] ... Lp [i+1]-1] imaginary part (complex versions) + + respectively. Each row is stored in sorted order, from low column + indices to higher. The last entry in each row is the diagonal, which + is numerically equal to one. The sizes of Lp, Lj, Lx, and Lz are + returned by umfpack_*_get_lunz. If Lp, Lj, or Ux (or Uz for the + complex version) are not present, then the matrix L is not returned. + This is not an error condition. The L matrix can be printed if n_row, + Lp, Lj, Lx (and Lz for the complex versions) are passed to + umfpack_*_report_matrix (using the "row" form). + + Future complex version: if Lx is present and Lz is NULL, then both real + and imaginary parts will be returned in Lx[0..2*lnz-1], with Lx[2*k] + and Lx[2*k+1] being the real and imaginary part of the kth entry. + + Int Up [n_col+1] ; Output argument. + Int Ui [unz] ; Output argument. + double Ux [unz] ; Output argument. + double Uz [unz] ; Output argument for complex versions. + + The min(n_row,n_col)-by-n_col matrix U is returned in compressed-column + form. The row indices of column j and corresponding numerical values + are in + + Ui [Up [j] ... Up [j+1]-1] + Ux [Up [j] ... Up [j+1]-1] real part + Uz [Up [j] ... Up [j+1]-1] imaginary part (complex versions) + + respectively. Each column is stored in sorted order, from low row + indices to higher. The last entry in each column is the diagonal + (assuming that it is nonzero). The sizes of Up, Ui, Ux, and Uz are + returned by umfpack_*_get_lunz. If Up, Ui, or Ux (or Uz for the complex + version) are not present, then the matrix U is not returned. This is + not an error condition. The U matrix can be printed if n_col, Up, Ui, + Ux (and Uz for the complex versions) are passed to + umfpack_*_report_matrix (using the "column" form). + + Future complex version: if Ux is present and Uz is NULL, then both real + and imaginary parts will be returned in Ux[0..2*unz-1], with Ux[2*k] + and Ux[2*k+1] being the real and imaginary part of the kth entry. + + Int P [n_row] ; Output argument. + + The permutation vector P is defined as P [k] = i, where the original + row i of A is the kth pivot row in PAQ. If you do not want the P vector + to be returned, simply pass (Int *) NULL for P. This is not an error + condition. You can print P and Q with umfpack_*_report_perm. + + Int Q [n_col] ; Output argument. + + The permutation vector Q is defined as Q [k] = j, where the original + column j of A is the kth pivot column in PAQ. If you not want the Q + vector to be returned, simply pass (Int *) NULL for Q. This is not + an error condition. Note that Q is not necessarily identical to + Qtree, the column pre-ordering held in the Symbolic object. Refer to + the description of Qtree and Front_npivcol in umfpack_*_get_symbolic for + details. + + double Dx [min(n_row,n_col)] ; Output argument. + double Dz [min(n_row,n_col)] ; Output argument for complex versions. + + The diagonal of U is also returned in Dx and Dz. You can extract the + diagonal of U without getting all of U by passing a non-NULL Dx (and + Dz for the complex version) and passing Up, Ui, and Ux as NULL. Dx is + the real part of the diagonal, and Dz is the imaginary part. + + Future complex version: if Dx is present and Dz is NULL, then both real + and imaginary parts will be returned in Dx[0..2*min(n_row,n_col)-1], + with Dx[2*k] and Dx[2*k+1] being the real and imaginary part of the kth + entry. + + Int *do_recip ; Output argument. + + If do_recip is returned as zero (false), then the scale factors Rs [i] + are to be used by multiplying row i by Rs [i]. Otherwise, the entries + in row i are to be divided by Rs [i]. + + If UMFPACK has been compiled with gcc, or for MATLAB as either a + built-in routine or as a mexFunction, then the NRECIPROCAL flag is + set, and do_recip will always be zero (false). + + NOTE: this argument is new to version 4.1. + + double Rs [n_row] ; Output argument. + + The row scale factors are returned in Rs [0..n_row-1]. Row i of A is + scaled by dividing or multiplying its values by Rs [i]. If default + scaling is in use, Rs [i] is the sum of the absolute values of row i + (or its reciprocal). If max row scaling is in use, then Rs [i] is the + maximum absolute value in row i (or its reciprocal). + Otherwise, Rs [i] = 1. If row i is all zero, Rs [i] = 1 as well. For + the complex version, an approximate absolute value is used + (|x_real|+|x_imag|). + + NOTE: this argument is new to version 4.1. + + void *Numeric ; Input argument, not modified. + + Numeric must point to a valid Numeric object, computed by + umfpack_*_numeric. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_symbolic.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_symbolic.h new file mode 100644 index 0000000000..5c48418a6c --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_get_symbolic.h @@ -0,0 +1,342 @@ +/* ========================================================================== */ +/* === umfpack_get_symbolic ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_get_symbolic +( + int *n_row, + int *n_col, + int *n1, + int *nz, + int *nfr, + int *nchains, + int P [ ], + int Q [ ], + int Front_npivcol [ ], + int Front_parent [ ], + int Front_1strow [ ], + int Front_leftmostdesc [ ], + int Chain_start [ ], + int Chain_maxrows [ ], + int Chain_maxcols [ ], + void *Symbolic +) ; + +long umfpack_dl_get_symbolic +( + long *n_row, + long *n_col, + long *n1, + long *nz, + long *nfr, + long *nchains, + long P [ ], + long Q [ ], + long Front_npivcol [ ], + long Front_parent [ ], + long Front_1strow [ ], + long Front_leftmostdesc [ ], + long Chain_start [ ], + long Chain_maxrows [ ], + long Chain_maxcols [ ], + void *Symbolic +) ; + +int umfpack_zi_get_symbolic +( + int *n_row, + int *n_col, + int *n1, + int *nz, + int *nfr, + int *nchains, + int P [ ], + int Q [ ], + int Front_npivcol [ ], + int Front_parent [ ], + int Front_1strow [ ], + int Front_leftmostdesc [ ], + int Chain_start [ ], + int Chain_maxrows [ ], + int Chain_maxcols [ ], + void *Symbolic +) ; + +long umfpack_zl_get_symbolic +( + long *n_row, + long *n_col, + long *n1, + long *nz, + long *nfr, + long *nchains, + long P [ ], + long Q [ ], + long Front_npivcol [ ], + long Front_parent [ ], + long Front_1strow [ ], + long Front_leftmostdesc [ ], + long Chain_start [ ], + long Chain_maxrows [ ], + long Chain_maxcols [ ], + void *Symbolic +) ; + +/* + +double int Syntax: + + #include "umfpack.h" + int status, n_row, n_col, nz, nfr, nchains, *P, *Q, + *Front_npivcol, *Front_parent, *Front_1strow, *Front_leftmostdesc, + *Chain_start, *Chain_maxrows, *Chain_maxcols ; + void *Symbolic ; + status = umfpack_di_get_symbolic (&n_row, &n_col, &nz, &nfr, &nchains, + P, Q, Front_npivcol, Front_parent, Front_1strow, + Front_leftmostdesc, Chain_start, Chain_maxrows, Chain_maxcols, + Symbolic) ; + +double long Syntax: + + #include "umfpack.h" + long status, n_row, n_col, nz, nfr, nchains, *P, *Q, + *Front_npivcol, *Front_parent, *Front_1strow, *Front_leftmostdesc, + *Chain_start, *Chain_maxrows, *Chain_maxcols ; + void *Symbolic ; + status = umfpack_dl_get_symbolic (&n_row, &n_col, &nz, &nfr, &nchains, + P, Q, Front_npivcol, Front_parent, Front_1strow, + Front_leftmostdesc, Chain_start, Chain_maxrows, Chain_maxcols, + Symbolic) ; + +complex int Syntax: + + #include "umfpack.h" + int status, n_row, n_col, nz, nfr, nchains, *P, *Q, + *Front_npivcol, *Front_parent, *Front_1strow, *Front_leftmostdesc, + *Chain_start, *Chain_maxrows, *Chain_maxcols ; + void *Symbolic ; + status = umfpack_zi_get_symbolic (&n_row, &n_col, &nz, &nfr, &nchains, + P, Q, Front_npivcol, Front_parent, Front_1strow, + Front_leftmostdesc, Chain_start, Chain_maxrows, Chain_maxcols, + Symbolic) ; + +complex long Syntax: + + #include "umfpack.h" + long status, n_row, n_col, nz, nfr, nchains, *P, *Q, + *Front_npivcol, *Front_parent, *Front_1strow, *Front_leftmostdesc, + *Chain_start, *Chain_maxrows, *Chain_maxcols ; + void *Symbolic ; + status = umfpack_zl_get_symbolic (&n_row, &n_col, &nz, &nfr, &nchains, + P, Q, Front_npivcol, Front_parent, Front_1strow, + Front_leftmostdesc, Chain_start, Chain_maxrows, Chain_maxcols, + Symbolic) ; + +Purpose: + + Copies the contents of the Symbolic object into simple integer arrays + accessible to the user. This routine is not needed to factorize and/or + solve a sparse linear system using UMFPACK. Note that the output arrays + P, Q, Front_npivcol, Front_parent, Front_1strow, Front_leftmostdesc, + Chain_start, Chain_maxrows, and Chain_maxcols are not allocated by + umfpack_*_get_symbolic; they must exist on input. + + All output arguments are optional. If any of them are NULL + on input, then that part of the symbolic analysis is not copied. You can + use this routine to extract just the parts of the symbolic analysis that + you want. For example, to retrieve just the column permutation Q, use: + + #define noI (int *) NULL + status = umfpack_di_get_symbolic (noI, noI, noI, noI, noI, noI, noI, + Q, noI, noI, noI, noI, noI, noI, noI, Symbolic) ; + + The only required argument the last one, the pointer to the Symbolic object. + + The Symbolic object is small. Its size for an n-by-n square matrix varies + from 4*n to 13*n, depending on the matrix. The object holds the initial + column permutation, the supernodal column elimination tree, and information + about each frontal matrix. You can print it with umfpack_*_report_symbolic. + +Returns: + + Returns UMFPACK_OK if successful, UMFPACK_ERROR_invalid_Symbolic_object + if Symbolic is an invalid object. + +Arguments: + + Int *n_row ; Output argument. + Int *n_col ; Output argument. + + The dimensions of the matrix A analyzed by the call to + umfpack_*_symbolic that generated the Symbolic object. + + Int *n1 ; Output argument. + + The number of pivots with zero Markowitz cost (they have just one entry + in the pivot row, or the pivot column, or both). These appear first in + the output permutations P and Q. + + NOTE: this argument is new to version 4.1. + + Int *nz ; Output argument. + + The number of nonzeros in A. + + Int *nfr ; Output argument. + + The number of frontal matrices that will be used by umfpack_*_numeric + to factorize the matrix A. It is in the range 0 to n_col. + + Int *nchains ; Output argument. + + The frontal matrices are related to one another by the supernodal + column elimination tree. Each node in this tree is one frontal matrix. + The tree is partitioned into a set of disjoint paths, and a frontal + matrix chain is one path in this tree. Each chain is factorized using + a unifrontal technique, with a single working array that holds each + frontal matrix in the chain, one at a time. nchains is in the range + 0 to nfr. + + Int P [n_row] ; Output argument. + + The initial row permutation. If P [k] = i, then this means that + row i is the kth row in the pre-ordered matrix. In general, this P is + not the same as the final row permutation computed by umfpack_*_numeric. + + For the unsymmetric strategy, P defines the row-merge order. Let j be + the column index of the leftmost nonzero entry in row i of A*Q. Then + P defines a sort of the rows according to this value. A row can appear + earlier in this ordering if it is aggressively absorbed before it can + become a pivot row. If P [k] = i, row i typically will not be the kth + pivot row. + + For the symmetric strategy, P = Q. For the 2-by-2 strategy, P is the + row permutation that places large entries on the diagonal of P*A*Q. + If no pivoting occurs during numerical factorization, P [k] = i also + defines the final permutation of umfpack_*_numeric, for either the + symmetric or 2-by-2 strategies. + + Int Q [n_col] ; Output argument. + + The initial column permutation. If Q [k] = j, then this means that + column j is the kth pivot column in the pre-ordered matrix. Q is + not necessarily the same as the final column permutation Q, computed by + umfpack_*_numeric. The numeric factorization may reorder the pivot + columns within each frontal matrix to reduce fill-in. If the matrix is + structurally singular, and if the symmetric or 2-by-2 strategies or + used (or if Control [UMFPACK_FIXQ] > 0), then this Q will be the same + as the final column permutation computed in umfpack_*_numeric. + + Int Front_npivcol [n_col+1] ; Output argument. + + This array should be of size at least n_col+1, in order to guarantee + that it will be large enough to hold the output. Only the first nfr+1 + entries are used, however. + + The kth frontal matrix holds Front_npivcol [k] pivot columns. Thus, the + first frontal matrix, front 0, is used to factorize the first + Front_npivcol [0] columns; these correspond to the original columns + Q [0] through Q [Front_npivcol [0]-1]. The next frontal matrix + is used to factorize the next Front_npivcol [1] columns, which are thus + the original columns Q [Front_npivcol [0]] through + Q [Front_npivcol [0] + Front_npivcol [1] - 1], and so on. Columns + with no entries at all are put in a placeholder "front", + Front_npivcol [nfr]. The sum of Front_npivcol [0..nfr] is equal to + n_col. + + Any modifications that umfpack_*_numeric makes to the initial column + permutation are constrained to within each frontal matrix. Thus, for + the first frontal matrix, Q [0] through Q [Front_npivcol [0]-1] is some + permutation of the columns Q [0] through + Q [Front_npivcol [0]-1]. For second frontal matrix, + Q [Front_npivcol [0]] through Q [Front_npivcol [0] + Front_npivcol[1]-1] + is some permutation of the same portion of Q, and so on. All pivot + columns are numerically factorized within the frontal matrix originally + determined by the symbolic factorization; there is no delayed pivoting + across frontal matrices. + + Int Front_parent [n_col+1] ; Output argument. + + This array should be of size at least n_col+1, in order to guarantee + that it will be large enough to hold the output. Only the first nfr+1 + entries are used, however. + + Front_parent [0..nfr] holds the supernodal column elimination tree + (including the placeholder front nfr, which may be empty). Each node in + the tree corresponds to a single frontal matrix. The parent of node f + is Front_parent [f]. + + Int Front_1strow [n_col+1] ; Output argument. + + This array should be of size at least n_col+1, in order to guarantee + that it will be large enough to hold the output. Only the first nfr+1 + entries are used, however. + + Front_1strow [k] is the row index of the first row in A (P,Q) + whose leftmost entry is in a pivot column for the kth front. This is + necessary only to properly factorize singular matrices. It is new to + Version 4.0. Rows in the range Front_1strow [k] to + Front_1strow [k+1]-1 first become pivot row candidates at the kth front. + Any rows not eliminated in the kth front may be selected as pivot rows + in the parent of k (Front_parent [k]) and so on up the tree. + + Int Front_leftmostdesc [n_col+1] ; Output argument. + + This array should be of size at least n_col+1, in order to guarantee + that it will be large enough to hold the output. Only the first nfr+1 + entries are used, however. + + Front_leftmostdesc [k] is the leftmost descendant of front k, or k + if the front has no children in the tree. Since the rows and columns + (P and Q) have been post-ordered via a depth-first-search of + the tree, rows in the range Front_1strow [Front_leftmostdesc [k]] to + Front_1strow [k+1]-1 form the entire set of candidate pivot rows for + the kth front (some of these will typically have already been selected + by fronts in the range Front_leftmostdesc [k] to front k-1, before + the factorization reaches front k). + + Chain_start [n_col+1] ; Output argument. + + This array should be of size at least n_col+1, in order to guarantee + that it will be large enough to hold the output. Only the first + nchains+1 entries are used, however. + + The kth frontal matrix chain consists of frontal matrices Chain_start[k] + through Chain_start [k+1]-1. Thus, Chain_start [0] is always 0, and + Chain_start [nchains] is the total number of frontal matrices, nfr. For + two adjacent fronts f and f+1 within a single chain, f+1 is always the + parent of f (that is, Front_parent [f] = f+1). + + Int Chain_maxrows [n_col+1] ; Output argument. + Int Chain_maxcols [n_col+1] ; Output argument. + + These arrays should be of size at least n_col+1, in order to guarantee + that they will be large enough to hold the output. Only the first + nchains entries are used, however. + + The kth frontal matrix chain requires a single working array of + dimension Chain_maxrows [k] by Chain_maxcols [k], for the unifrontal + technique that factorizes the frontal matrix chain. Since the symbolic + factorization only provides an upper bound on the size of each frontal + matrix, not all of the working array is necessarily used during the + numerical factorization. + + Note that the upper bound on the number of rows and columns of each + frontal matrix is computed by umfpack_*_symbolic, but all that is + required by umfpack_*_numeric is the maximum of these two sets of + values for each frontal matrix chain. Thus, the size of each + individual frontal matrix is not preserved in the Symbolic object. + + void *Symbolic ; Input argument, not modified. + + The Symbolic object, which holds the symbolic factorization computed by + umfpack_*_symbolic. The Symbolic object is not modified by + umfpack_*_get_symbolic. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_load_numeric.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_load_numeric.h new file mode 100644 index 0000000000..7d1f3b33dc --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_load_numeric.h @@ -0,0 +1,96 @@ +/* ========================================================================== */ +/* === umfpack_load_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_load_numeric +( + void **Numeric, + char *filename +) ; + +long umfpack_dl_load_numeric +( + void **Numeric, + char *filename +) ; + +int umfpack_zi_load_numeric +( + void **Numeric, + char *filename +) ; + +long umfpack_zl_load_numeric +( + void **Numeric, + char *filename +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int status ; + char *filename ; + void *Numeric ; + status = umfpack_di_load_numeric (&Numeric, filename) ; + +double long Syntax: + + #include "umfpack.h" + long status ; + char *filename ; + void *Numeric ; + status = umfpack_dl_load_numeric (&Numeric, filename) ; + +complex int Syntax: + + #include "umfpack.h" + int status ; + char *filename ; + void *Numeric ; + status = umfpack_zi_load_numeric (&Numeric, filename) ; + +complex long Syntax: + + #include "umfpack.h" + long status ; + char *filename ; + void *Numeric ; + status = umfpack_zl_load_numeric (&Numeric, filename) ; + +Purpose: + + Loads a Numeric object from a file created by umfpack_*_save_numeric. The + Numeric handle passed to this routine is overwritten with the new object. + If that object exists prior to calling this routine, a memory leak will + occur. The contents of Numeric are ignored on input. + +Returns: + + UMFPACK_OK if successful. + UMFPACK_ERROR_out_of_memory if not enough memory is available. + UMFPACK_ERROR_file_IO if an I/O error occurred. + +Arguments: + + void **Numeric ; Output argument. + + **Numeric is the address of a (void *) pointer variable in the user's + calling routine (see Syntax, above). On input, the contents of this + variable are not defined. On output, this variable holds a (void *) + pointer to the Numeric object (if successful), or (void *) NULL if + a failure occurred. + + char *filename ; Input argument, not modified. + + A string that contains the filename from which to read the Numeric + object. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_load_symbolic.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_load_symbolic.h new file mode 100644 index 0000000000..69ece02688 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_load_symbolic.h @@ -0,0 +1,96 @@ +/* ========================================================================== */ +/* === umfpack_load_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_load_symbolic +( + void **Symbolic, + char *filename +) ; + +long umfpack_dl_load_symbolic +( + void **Symbolic, + char *filename +) ; + +int umfpack_zi_load_symbolic +( + void **Symbolic, + char *filename +) ; + +long umfpack_zl_load_symbolic +( + void **Symbolic, + char *filename +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int status ; + char *filename ; + void *Symbolic ; + status = umfpack_di_load_symbolic (&Symbolic, filename) ; + +double long Syntax: + + #include "umfpack.h" + long status ; + char *filename ; + void *Symbolic ; + status = umfpack_dl_load_symbolic (&Symbolic, filename) ; + +complex int Syntax: + + #include "umfpack.h" + int status ; + char *filename ; + void *Symbolic ; + status = umfpack_zi_load_symbolic (&Symbolic, filename) ; + +complex long Syntax: + + #include "umfpack.h" + long status ; + char *filename ; + void *Symbolic ; + status = umfpack_zl_load_symbolic (&Symbolic, filename) ; + +Purpose: + + Loads a Symbolic object from a file created by umfpack_*_save_symbolic. The + Symbolic handle passed to this routine is overwritten with the new object. + If that object exists prior to calling this routine, a memory leak will + occur. The contents of Symbolic are ignored on input. + +Returns: + + UMFPACK_OK if successful. + UMFPACK_ERROR_out_of_memory if not enough memory is available. + UMFPACK_ERROR_file_IO if an I/O error occurred. + +Arguments: + + void **Symbolic ; Output argument. + + **Symbolic is the address of a (void *) pointer variable in the user's + calling routine (see Syntax, above). On input, the contents of this + variable are not defined. On output, this variable holds a (void *) + pointer to the Symbolic object (if successful), or (void *) NULL if + a failure occurred. + + char *filename ; Input argument, not modified. + + A string that contains the filename from which to read the Symbolic + object. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_numeric.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_numeric.h new file mode 100644 index 0000000000..a57fd0d012 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_numeric.h @@ -0,0 +1,552 @@ +/* ========================================================================== */ +/* === umfpack_numeric ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_numeric +( + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + void *Symbolic, + void **Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +long umfpack_dl_numeric +( + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], + void *Symbolic, + void **Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +int umfpack_zi_numeric +( + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], const double Az [ ], + void *Symbolic, + void **Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +long umfpack_zl_numeric +( + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], const double Az [ ], + void *Symbolic, + void **Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Symbolic, *Numeric ; + int *Ap, *Ai, status ; + double *Ax, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ; + status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, Control, Info); + +double long Syntax: + + #include "umfpack.h" + void *Symbolic, *Numeric ; + long *Ap, *Ai, status ; + double *Ax, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ; + status = umfpack_dl_numeric (Ap, Ai, Ax, Symbolic, &Numeric, Control, Info); + +complex int Syntax: + + #include "umfpack.h" + void *Symbolic, *Numeric ; + int *Ap, *Ai, status ; + double *Ax, *Az, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ; + status = umfpack_zi_numeric (Ap, Ai, Ax, Az, Symbolic, &Numeric, + Control, Info) ; + +complex long Syntax: + + #include "umfpack.h" + void *Symbolic, *Numeric ; + long *Ap, *Ai, status ; + double *Ax, *Az, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ; + status = umfpack_zl_numeric (Ap, Ai, Ax, Symbolic, &Numeric, + Control, Info) ; + +Purpose: + + Given a sparse matrix A in column-oriented form, and a symbolic analysis + computed by umfpack_*_*symbolic, the umfpack_*_numeric routine performs the + numerical factorization, PAQ=LU, PRAQ=LU, or P(R\A)Q=LU, where P and Q are + permutation matrices (represented as permutation vectors), R is the row + scaling, L is unit-lower triangular, and U is upper triangular. This is + required before the system Ax=b (or other related linear systems) can be + solved. umfpack_*_numeric can be called multiple times for each call to + umfpack_*_*symbolic, to factorize a sequence of matrices with identical + nonzero pattern. Simply compute the Symbolic object once, with + umfpack_*_*symbolic, and reuse it for subsequent matrices. This routine + safely detects if the pattern changes, and sets an appropriate error code. + +Returns: + + The status code is returned. See Info [UMFPACK_STATUS], below. + +Arguments: + + Int Ap [n_col+1] ; Input argument, not modified. + + This must be identical to the Ap array passed to umfpack_*_*symbolic. + The value of n_col is what was passed to umfpack_*_*symbolic (this is + held in the Symbolic object). + + Int Ai [nz] ; Input argument, not modified, of size nz = Ap [n_col]. + + This must be identical to the Ai array passed to umfpack_*_*symbolic. + + double Ax [nz] ; Input argument, not modified, of size nz = Ap [n_col]. + + The numerical values of the sparse matrix A. The nonzero pattern (row + indices) for column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and + the corresponding numerical values are stored in + Ax [(Ap [j]) ... (Ap [j+1]-1)]. + + double Az [nz] ; Input argument, not modified, for complex versions. + + For the complex versions, this holds the imaginary part of A. The + imaginary part of column j is held in Az [(Ap [j]) ... (Ap [j+1]-1)]. + + Future complex version: if Ax is present and Az is NULL, then both real + and imaginary parts will be contained in Ax[0..2*nz-1], with Ax[2*k] + and Ax[2*k+1] being the real and imaginary part of the kth entry. + + void *Symbolic ; Input argument, not modified. + + The Symbolic object, which holds the symbolic factorization computed by + umfpack_*_*symbolic. The Symbolic object is not modified by + umfpack_*_numeric. + + void **Numeric ; Output argument. + + **Numeric is the address of a (void *) pointer variable in the user's + calling routine (see Syntax, above). On input, the contents of this + variable are not defined. On output, this variable holds a (void *) + pointer to the Numeric object (if successful), or (void *) NULL if + a failure occurred. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PIVOT_TOLERANCE]: relative pivot tolerance for + threshold partial pivoting with row interchanges. In any given + column, an entry is numerically acceptable if its absolute value is + greater than or equal to Control [UMFPACK_PIVOT_TOLERANCE] times + the largest absolute value in the column. A value of 1.0 gives true + partial pivoting. If less than or equal to zero, then any nonzero + entry is numerically acceptable as a pivot (this is changed from + Version 4.0). Default: 0.1. + + Smaller values tend to lead to sparser LU factors, but the solution + to the linear system can become inaccurate. Larger values can lead + to a more accurate solution (but not always), and usually an + increase in the total work. + + For complex matrices, a cheap approximate of the absolute value + is used for the threshold partial pivoting test (|a_real| + |a_imag| + instead of the more expensive-to-compute exact absolute value + sqrt (a_real^2 + a_imag^2)). + + Control [UMFPACK_SYM_PIVOT_TOLERANCE]: This parameter is new to V4.1. + If diagonal pivoting is attempted (the symmetric or symmetric-2by2 + strategies are used) then this parameter is used to control when the + diagonal entry is selected in a given pivot column. The absolute + value of the entry must be >= Control [UMFPACK_SYM_PIVOT_TOLERANCE] + times the largest absolute value in the column. A value of zero + will ensure that no off-diagonal pivoting is performed, except that + zero diagonal entries are not selected if there are any off-diagonal + nonzero entries. + + If an off-diagonal pivot is selected, an attempt is made to restore + symmetry later on. Suppose A (i,j) is selected, where i != j. + If column i has not yet been selected as a pivot column, then + the entry A (j,i) is redefined as a "diagonal" entry, except that + the tighter tolerance (Control [UMFPACK_PIVOT_TOLERANCE]) is + applied. This strategy has an effect similar to 2-by-2 pivoting + for symmetric indefinite matrices. If a 2-by-2 block pivot with + nonzero structure + + i j + i: 0 x + j: x 0 + + is selected in a symmetric indefinite factorization method, the + 2-by-2 block is inverted and a rank-2 update is applied. In + UMFPACK, this 2-by-2 block would be reordered as + + j i + i: x 0 + j: 0 x + + In both cases, the symmetry of the Schur complement is preserved. + + Control [UMFPACK_SCALE]: This parameter is new to V4.1. Version 4.0 + did not scale the matrix. Note that the user's input matrix is + never modified, only an internal copy is scaled. + + There are three valid settings for this parameter. If any other + value is provided, the default is used. + + UMFPACK_SCALE_NONE: no scaling is performed. + + UMFPACK_SCALE_SUM: each row of the input matrix A is divided by + the sum of the absolute values of the entries in that row. + The scaled matrix has an infinity norm of 1. + + UMFPACK_SCALE_MAX: each row of the input matrix A is divided by + the maximum the absolute values of the entries in that row. + In the scaled matrix the largest entry in each row has + a magnitude exactly equal to 1. + + Note that for complex matrices, a cheap approximate absolute value + is used, |a_real| + |a_imag|, instead of the exact absolute value + sqrt ((a_real)^2 + (a_imag)^2). + + Scaling is very important for the "symmetric" strategy when + diagonal pivoting is attempted. It also improves the performance + of the "unsymmetric" strategy. + + Default: UMFPACK_SCALE_SUM. + + Control [UMFPACK_ALLOC_INIT]: This parameter has changed in V4.1. + + When umfpack_*_numeric starts, it allocates memory for the Numeric + object. Part of this is of fixed size (approximately n double's + + 12*n integers). The remainder is of variable size, which grows to + hold the LU factors and the frontal matrices created during + factorization. A estimate of the upper bound is computed by + umfpack_*_*symbolic, and returned by umfpack_*_*symbolic in + Info [UMFPACK_VARIABLE_PEAK_ESTIMATE] (in Units). + + If Control [UMFPACK_ALLOC_INIT] is >= 0, umfpack_*_numeric initially + allocates space for the variable-sized part equal to this estimate + times Control [UMFPACK_ALLOC_INIT]. Typically, for matrices for + which the "unsymmetric" strategy applies, umfpack_*_numeric needs + only about half the estimated memory space, so a setting of 0.5 or + 0.6 often provides enough memory for umfpack_*_numeric to factorize + the matrix with no subsequent increases in the size of this block. + + If the matrix is ordered via AMD, then this non-negative parameter + is ignored. The initial allocation ratio computed automatically, + as 1.2 * (nz + Info [UMFPACK_SYMMETRIC_LUNZ]) / + (Info [UMFPACK_LNZ_ESTIMATE] + Info [UMFPACK_UNZ_ESTIMATE] - + min (n_row, n_col)). + + If Control [UMFPACK_ALLOC_INIT] is negative, then umfpack_*_numeric + allocates a space with initial size (in Units) equal to + (-Control [UMFPACK_ALLOC_INIT]). + + Regardless of the value of this parameter, a space equal to or + greater than the the bare minimum amount of memory needed to start + the factorization is always initially allocated. The bare initial + memory required is returned by umfpack_*_*symbolic in + Info [UMFPACK_VARIABLE_INIT_ESTIMATE] (an exact value, not an + estimate). + + If the variable-size part of the Numeric object is found to be too + small sometime after numerical factorization has started, the memory + is increased in size by a factor of 1.2. If this fails, the + request is reduced by a factor of 0.95 until it succeeds, or until + it determines that no increase in size is possible. Garbage + collection then occurs. + + The strategy of attempting to "malloc" a working space, and + re-trying with a smaller space, may not work under MATLAB, since + mxMalloc aborts the mexFunction if it fails. The built-in umfpack + routine (version 4.0) in MATLAB 6.5 uses utMalloc instead, which + avoids this problem. As a mexFunction, utMalloc is used unless + -DNUTIL is defined at compile time. The utMalloc routine, and + utFree and utRealloc, are not documented. If the mexFunction + doesn't work, then compile it with -DNUTIL instead. + + If you are using the umfpack mexFunction, decrease the magnitude of + Control [UMFPACK_ALLOC_INIT] if you run out of memory in MATLAB. + + Default initial allocation size: 0.7. Thus, with the default + control settings and the "unsymmetric" strategy, the upper-bound is + reached after two reallocations (0.7 * 1.2 * 1.2 = 1.008). + + Changing this parameter has little effect on fill-in or operation + count. It has a small impact on run-time (the extra time required + to do the garbage collection and memory reallocation). + + Control [UMFPACK_FRONT_ALLOC_INIT]: This parameter is new to V4.1. + + When UMFPACK starts the factorization of each "chain" of frontal + matrices, it allocates a working array to hold the frontal matrices + as they are factorized. The symbolic factorization computes the + size of the largest possible frontal matrix that could occur during + the factorization of each chain. + + If Control [UMFPACK_FRONT_ALLOC_INIT] is >= 0, the following + strategy is used. If the AMD ordering was used, this non-negative + parameter is ignored. A front of size (d+2)*(d+2) is allocated, + where d = Info [UMFPACK_SYMMETRIC_DMAX]. Otherwise, a front of + size Control [UMFPACK_FRONT_ALLOC_INIT] times the largest front + possible for this chain is allocated. + + If Control [UMFPACK_FRONT_ALLOC_INIT] is negative, then a front of + size (-Control [UMFPACK_FRONT_ALLOC_INIT]) is allocated (where the + size is in terms of the number of numerical entries). This is done + regardless of the ordering method or ordering strategy used. + + Default: 0.5. + + Control [UMFPACK_DROPTOL]: This parameter is new to V4.2. + + Entries in L and U with absolute value less than or equal to the + drop tolerance are removed from the data structures (unless leaving + them there reduces memory usage by reducing the space required + for the nonzero pattern of L and U). + + Default: 0.0. + + double Info [UMFPACK_INFO] ; Output argument. + + Contains statistics about the numeric factorization. If a + (double *) NULL pointer is passed, then no statistics are returned in + Info (this is not an error condition). The following statistics are + computed in umfpack_*_numeric: + + Info [UMFPACK_STATUS]: status code. This is also the return value, + whether or not Info is present. + + UMFPACK_OK + + Numeric factorization was successful. umfpack_*_numeric + computed a valid numeric factorization. + + UMFPACK_WARNING_singular_matrix + + Numeric factorization was successful, but the matrix is + singular. umfpack_*_numeric computed a valid numeric + factorization, but you will get a divide by zero in + umfpack_*_*solve. For the other cases below, no Numeric object + is created (*Numeric is (void *) NULL). + + UMFPACK_ERROR_out_of_memory + + Insufficient memory to complete the numeric factorization. + + UMFPACK_ERROR_argument_missing + + One or more required arguments are missing. + + UMFPACK_ERROR_invalid_Symbolic_object + + Symbolic object provided as input is invalid. + + UMFPACK_ERROR_different_pattern + + The pattern (Ap and/or Ai) has changed since the call to + umfpack_*_*symbolic which produced the Symbolic object. + + Info [UMFPACK_NROW]: the value of n_row stored in the Symbolic object. + + Info [UMFPACK_NCOL]: the value of n_col stored in the Symbolic object. + + Info [UMFPACK_NZ]: the number of entries in the input matrix. + This value is obtained from the Symbolic object. + + Info [UMFPACK_SIZE_OF_UNIT]: the number of bytes in a Unit, for memory + usage statistics below. + + Info [UMFPACK_VARIABLE_INIT]: the initial size (in Units) of the + variable-sized part of the Numeric object. If this differs from + Info [UMFPACK_VARIABLE_INIT_ESTIMATE], then the pattern (Ap and/or + Ai) has changed since the last call to umfpack_*_*symbolic, which is + an error condition. + + Info [UMFPACK_VARIABLE_PEAK]: the peak size (in Units) of the + variable-sized part of the Numeric object. This size is the amount + of space actually used inside the block of memory, not the space + allocated via UMF_malloc. You can reduce UMFPACK's memory + requirements by setting Control [UMFPACK_ALLOC_INIT] to the ratio + Info [UMFPACK_VARIABLE_PEAK] / Info[UMFPACK_VARIABLE_PEAK_ESTIMATE]. + This will ensure that no memory reallocations occur (you may want to + add 0.001 to make sure that integer roundoff does not lead to a + memory size that is 1 Unit too small; otherwise, garbage collection + and reallocation will occur). + + Info [UMFPACK_VARIABLE_FINAL]: the final size (in Units) of the + variable-sized part of the Numeric object. It holds just the + sparse LU factors. + + Info [UMFPACK_NUMERIC_SIZE]: the actual final size (in Units) of the + entire Numeric object, including the final size of the variable + part of the object. Info [UMFPACK_NUMERIC_SIZE_ESTIMATE], + an estimate, was computed by umfpack_*_*symbolic. The estimate is + normally an upper bound on the actual final size, but this is not + guaranteed. + + Info [UMFPACK_PEAK_MEMORY]: the actual peak memory usage (in Units) of + both umfpack_*_*symbolic and umfpack_*_numeric. An estimate, + Info [UMFPACK_PEAK_MEMORY_ESTIMATE], was computed by + umfpack_*_*symbolic. The estimate is normally an upper bound on the + actual peak usage, but this is not guaranteed. With testing on + hundreds of matrix arising in real applications, I have never + observed a matrix where this estimate or the Numeric size estimate + was less than the actual result, but this is theoretically possible. + Please send me one if you find such a matrix. + + Info [UMFPACK_FLOPS]: the actual count of the (useful) floating-point + operations performed. An estimate, Info [UMFPACK_FLOPS_ESTIMATE], + was computed by umfpack_*_*symbolic. The estimate is guaranteed to + be an upper bound on this flop count. The flop count excludes + "useless" flops on zero values, flops performed during the pivot + search (for tentative updates and assembly of candidate columns), + and flops performed to add frontal matrices together. + + For the real version, only (+ - * /) are counted. For the complex + version, the following counts are used: + + operation flops + c = 1/b 6 + c = a*b 6 + c -= a*b 8 + + Info [UMFPACK_LNZ]: the actual nonzero entries in final factor L, + including the diagonal. This excludes any zero entries in L, + although some of these are stored in the Numeric object. The + Info [UMFPACK_LU_ENTRIES] statistic does account for all + explicitly stored zeros, however. Info [UMFPACK_LNZ_ESTIMATE], + an estimate, was computed by umfpack_*_*symbolic. The estimate is + guaranteed to be an upper bound on Info [UMFPACK_LNZ]. + + Info [UMFPACK_UNZ]: the actual nonzero entries in final factor U, + including the diagonal. This excludes any zero entries in U, + although some of these are stored in the Numeric object. The + Info [UMFPACK_LU_ENTRIES] statistic does account for all + explicitly stored zeros, however. Info [UMFPACK_UNZ_ESTIMATE], + an estimate, was computed by umfpack_*_*symbolic. The estimate is + guaranteed to be an upper bound on Info [UMFPACK_UNZ]. + + Info [UMFPACK_NUMERIC_DEFRAG]: The number of garbage collections + performed during umfpack_*_numeric, to compact the contents of the + variable-sized workspace used by umfpack_*_numeric. No estimate was + computed by umfpack_*_*symbolic. In the current version of UMFPACK, + garbage collection is performed and then the memory is reallocated, + so this statistic is the same as Info [UMFPACK_NUMERIC_REALLOC], + below. It may differ in future releases. + + Info [UMFPACK_NUMERIC_REALLOC]: The number of times that the Numeric + object was increased in size from its initial size. A rough upper + bound on the peak size of the Numeric object was computed by + umfpack_*_*symbolic, so reallocations should be rare. However, if + umfpack_*_numeric is unable to allocate that much storage, it + reduces its request until either the allocation succeeds, or until + it gets too small to do anything with. If the memory that it + finally got was small, but usable, then the reallocation count + could be high. No estimate of this count was computed by + umfpack_*_*symbolic. + + Info [UMFPACK_NUMERIC_COSTLY_REALLOC]: The number of times that the + system realloc library routine (or mxRealloc for the mexFunction) + had to move the workspace. Realloc can sometimes increase the size + of a block of memory without moving it, which is much faster. This + statistic will always be <= Info [UMFPACK_NUMERIC_REALLOC]. If your + memory space is fragmented, then the number of "costly" realloc's + will be equal to Info [UMFPACK_NUMERIC_REALLOC]. + + Info [UMFPACK_COMPRESSED_PATTERN]: The number of integers used to + represent the pattern of L and U. + + Info [UMFPACK_LU_ENTRIES]: The total number of numerical values that + are stored for the LU factors. Some of the values may be explicitly + zero in order to save space (allowing for a smaller compressed + pattern). + + Info [UMFPACK_NUMERIC_TIME]: The CPU time taken, in seconds. + + Info [UMFPACK_RCOND]: A rough estimate of the condition number, equal + to min (abs (diag (U))) / max (abs (diag (U))), or zero if the + diagonal of U is all zero. + + Info [UMFPACK_UDIAG_NZ]: The number of numerically nonzero values on + the diagonal of U. + + Info [UMFPACK_UMIN]: the smallest absolute value on the diagonal of U. + + Info [UMFPACK_UMAX]: the smallest absolute value on the diagonal of U. + + Info [UMFPACK_MAX_FRONT_SIZE]: the size of the + largest frontal matrix (number of entries). + + ------------------------------------------------------------------------ + The following statistics were added to Version 4.1: + ------------------------------------------------------------------------ + + Info [UMFPACK_NUMERIC_WALLTIME]: The wallclock time taken, in seconds. + + Info [UMFPACK_MAX_FRONT_NROWS]: the max number of + rows in any frontal matrix. + + Info [UMFPACK_MAX_FRONT_NCOLS]: the max number of + columns in any frontal matrix. + + Info [UMFPACK_WAS_SCALED]: the scaling used, either UMFPACK_SCALE_NONE, + UMFPACK_SCALE_SUM, or UMFPACK_SCALE_MAX. + + Info [UMFPACK_RSMIN]: if scaling is performed, the smallest scale factor + for any row (either the smallest sum of absolute entries, or the + smallest maximum of absolute entries). + + Info [UMFPACK_RSMAX]: if scaling is performed, the largest scale factor + for any row (either the largest sum of absolute entries, or the + largest maximum of absolute entries). + + Info [UMFPACK_ALLOC_INIT_USED]: the initial allocation parameter used. + + Info [UMFPACK_FORCED_UPDATES]: the number of BLAS-3 updates to the + frontal matrices that were required because the frontal matrix + grew larger than its current working array. + + Info [UMFPACK_NOFF_DIAG]: number of off-diagonal pivots selected, if the + symmetric or 2-by-2 strategies are used. + + ------------------------------------------------------------------------ + The following statistics were added to Version 4.2: + ------------------------------------------------------------------------ + + Info [UMFPACK_NZDROPPED]: the number of entries smaller in absolute + value than Control [UMFPACK_DROPTOL] that were dropped from L and U. + Note that entries on the diagonal of U are never dropped. + + Info [UMFPACK_ALL_LNZ]: the number of entries in L, including the + diagonal, if no small entries are dropped. + + Info [UMFPACK_ALL_UNZ]: the number of entries in U, including the + diagonal, if no small entries are dropped. + + Only the above listed Info [...] entries are accessed. The remaining + entries of Info are not accessed or modified by umfpack_*_numeric. + Future versions might modify different parts of Info. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_qsymbolic.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_qsymbolic.h new file mode 100644 index 0000000000..23088a12de --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_qsymbolic.h @@ -0,0 +1,140 @@ +/* ========================================================================== */ +/* === umfpack_qsymbolic ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_qsymbolic +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + const int Qinit [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +long umfpack_dl_qsymbolic +( + long n_row, + long n_col, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], + const long Qinit [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +int umfpack_zi_qsymbolic +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], const double Az [ ], + const int Qinit [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +long umfpack_zl_qsymbolic +( + long n_row, + long n_col, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], const double Az [ ], + const long Qinit [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Symbolic ; + int n_row, n_col, *Ap, *Ai, *Qinit, status ; + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], *Ax ; + status = umfpack_di_qsymbolic (n_row, n_col, Ap, Ai, Ax, Qinit, + &Symbolic, Control, Info) ; + +double long Syntax: + + #include "umfpack.h" + void *Symbolic ; + long n_row, n_col, *Ap, *Ai, *Qinit, status ; + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], *Ax ; + status = umfpack_dl_qsymbolic (n_row, n_col, Ap, Ai, Ax, Qinit, + &Symbolic, Control, Info) ; + +complex int Syntax: + + #include "umfpack.h" + void *Symbolic ; + int n_row, n_col, *Ap, *Ai, *Qinit, status ; + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], *Ax, *Az ; + status = umfpack_zi_qsymbolic (n_row, n_col, Ap, Ai, Ax, Az, Qinit, + &Symbolic, Control, Info) ; + +complex long Syntax: + + #include "umfpack.h" + void *Symbolic ; + long n_row, n_col, *Ap, *Ai, *Qinit, status ; + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], *Ax, *Az ; + status = umfpack_zl_qsymbolic (n_row, n_col, Ap, Ai, Ax, Az, Qinit, + &Symbolic, Control, Info) ; + +Purpose: + + Given the nonzero pattern of a sparse matrix A in column-oriented form, and + a sparsity preserving column pre-ordering Qinit, umfpack_*_qsymbolic + performs the symbolic factorization of A*Qinit (or A (:,Qinit) in MATLAB + notation). This is identical to umfpack_*_symbolic, except that neither + COLAMD nor AMD are called and the user input column order Qinit is used + instead. Note that in general, the Qinit passed to umfpack_*_qsymbolic + can differ from the final Q found in umfpack_*_numeric. The unsymmetric + strategy will perform a column etree postordering done in + umfpack_*_qsymbolic and sparsity-preserving modifications are made within + each frontal matrix during umfpack_*_numeric. The symmetric and 2-by-2 + strategies will preserve Qinit, unless the matrix is structurally singular. + + See umfpack_*_symbolic for more information. + + *** WARNING *** A poor choice of Qinit can easily cause umfpack_*_numeric + to use a huge amount of memory and do a lot of work. The "default" symbolic + analysis method is umfpack_*_symbolic, not this routine. If you use this + routine, the performance of UMFPACK is your responsibility; UMFPACK will + not try to second-guess a poor choice of Qinit. + +Returns: + + The value of Info [UMFPACK_STATUS]; see umfpack_*_symbolic. + Also returns UMFPACK_ERROR_invalid_permuation if Qinit is not a valid + permutation vector. + +Arguments: + + All arguments are the same as umfpack_*_symbolic, except for the following: + + Int Qinit [n_col] ; Input argument, not modified. + + The user's fill-reducing initial column pre-ordering. This must be a + permutation of 0..n_col-1. If Qinit [k] = j, then column j is the kth + column of the matrix A (:,Qinit) to be factorized. If Qinit is an + (Int *) NULL pointer, then COLAMD or AMD are called instead. + +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_control.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_control.h new file mode 100644 index 0000000000..6aebfdf5b5 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_control.h @@ -0,0 +1,77 @@ +/* ========================================================================== */ +/* === umfpack_report_control =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +void umfpack_di_report_control +( + const double Control [UMFPACK_CONTROL] +) ; + +void umfpack_dl_report_control +( + const double Control [UMFPACK_CONTROL] +) ; + +void umfpack_zi_report_control +( + const double Control [UMFPACK_CONTROL] +) ; + +void umfpack_zl_report_control +( + const double Control [UMFPACK_CONTROL] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + umfpack_di_report_control (Control) ; + +double long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + umfpack_dl_report_control (Control) ; + +complex int Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + umfpack_zi_report_control (Control) ; + +double long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + umfpack_zl_report_control (Control) ; + +Purpose: + + Prints the current control settings. Note that with the default print + level, nothing is printed. Does nothing if Control is (double *) NULL. + +Arguments: + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PRL]: printing level. + + 1 or less: no output + 2 or more: print all of Control + Default: 1 +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_info.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_info.h new file mode 100644 index 0000000000..ccba175018 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_info.h @@ -0,0 +1,87 @@ +/* ========================================================================== */ +/* === umfpack_report_info ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +void umfpack_di_report_info +( + const double Control [UMFPACK_CONTROL], + const double Info [UMFPACK_INFO] +) ; + +void umfpack_dl_report_info +( + const double Control [UMFPACK_CONTROL], + const double Info [UMFPACK_INFO] +) ; + +void umfpack_zi_report_info +( + const double Control [UMFPACK_CONTROL], + const double Info [UMFPACK_INFO] +) ; + +void umfpack_zl_report_info +( + const double Control [UMFPACK_CONTROL], + const double Info [UMFPACK_INFO] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ; + umfpack_di_report_info (Control, Info) ; + +double long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ; + umfpack_dl_report_info (Control, Info) ; + +complex int Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ; + umfpack_zi_report_info (Control, Info) ; + +complex long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ; + umfpack_zl_report_info (Control, Info) ; + +Purpose: + + Reports statistics from the umfpack_*_*symbolic, umfpack_*_numeric, and + umfpack_*_*solve routines. + +Arguments: + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PRL]: printing level. + + 0 or less: no output, even when an error occurs + 1: error messages only + 2 or more: error messages, and print all of Info + Default: 1 + + double Info [UMFPACK_INFO] ; Input argument, not modified. + + Info is an output argument of several UMFPACK routines. + The contents of Info are printed on standard output. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_matrix.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_matrix.h new file mode 100644 index 0000000000..b0225dae30 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_matrix.h @@ -0,0 +1,198 @@ +/* ========================================================================== */ +/* === umfpack_report_matrix ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_matrix +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + int col_form, + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_dl_report_matrix +( + long n_row, + long n_col, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], + long col_form, + const double Control [UMFPACK_CONTROL] +) ; + +int umfpack_zi_report_matrix +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], const double Az [ ], + int col_form, + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_zl_report_matrix +( + long n_row, + long n_col, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], const double Az [ ], + long col_form, + const double Control [UMFPACK_CONTROL] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int n_row, n_col, *Ap, *Ai, status ; + double *Ax, Control [UMFPACK_CONTROL] ; + status = umfpack_di_report_matrix (n_row, n_col, Ap, Ai, Ax, 1, Control) ; +or: + status = umfpack_di_report_matrix (n_row, n_col, Ap, Ai, Ax, 0, Control) ; + +double long Syntax: + + #include "umfpack.h" + long n_row, n_col, *Ap, *Ai, status ; + double *Ax, Control [UMFPACK_CONTROL] ; + status = umfpack_dl_report_matrix (n_row, n_col, Ap, Ai, Ax, 1, Control) ; +or: + status = umfpack_dl_report_matrix (n_row, n_col, Ap, Ai, Ax, 0, Control) ; + +complex int Syntax: + + #include "umfpack.h" + int n_row, n_col, *Ap, *Ai, status ; + double *Ax, *Az, Control [UMFPACK_CONTROL] ; + status = umfpack_zi_report_matrix (n_row, n_col, Ap, Ai, Ax, Az, 1, + Control) ; +or: + status = umfpack_zi_report_matrix (n_row, n_col, Ap, Ai, Ax, Az, 0, + Control) ; + +complex long Syntax: + + #include "umfpack.h" + long n_row, n_col, *Ap, *Ai, status ; + double *Ax, Control [UMFPACK_CONTROL] ; + status = umfpack_zl_report_matrix (n_row, n_col, Ap, Ai, Ax, Az, 1, + Control) ; +or: + status = umfpack_zl_report_matrix (n_row, n_col, Ap, Ai, Ax, Az, 0, + Control) ; + +Purpose: + + Verifies and prints a row or column-oriented sparse matrix. + +Returns: + + UMFPACK_OK if Control [UMFPACK_PRL] <= 2 (the input is not checked). + + Otherwise (where n is n_col for the column form and n_row for row + and let ni be n_row for the column form and n_col for row): + + UMFPACK_OK if the matrix is valid. + + UMFPACK_ERROR_n_nonpositive if n_row <= 0 or n_col <= 0. + UMFPACK_ERROR_argument_missing if Ap and/or Ai are missing. + UMFPACK_ERROR_invalid_matrix if Ap [n] < 0, if Ap [0] is not zero, + if Ap [j+1] < Ap [j] for any j in the range 0 to n-1, + if any row index in Ai is not in the range 0 to ni-1, or + if the row indices in any column are not in + ascending order, or contain duplicates. + UMFPACK_ERROR_out_of_memory if out of memory. + +Arguments: + + Int n_row ; Input argument, not modified. + Int n_col ; Input argument, not modified. + + A is an n_row-by-n_row matrix. Restriction: n_row > 0 and n_col > 0. + + Int Ap [n+1] ; Input argument, not modified. + + n is n_row for a row-form matrix, and n_col for a column-form matrix. + + Ap is an integer array of size n+1. If col_form is true (nonzero), + then on input, it holds the "pointers" for the column form of the + sparse matrix A. The row indices of column j of the matrix A are held + in Ai [(Ap [j]) ... (Ap [j+1]-1)]. Otherwise, Ap holds the + row pointers, and the column indices of row j of the matrix are held + in Ai [(Ap [j]) ... (Ap [j+1]-1)]. + + The first entry, Ap [0], must be zero, and Ap [j] <= Ap [j+1] must hold + for all j in the range 0 to n-1. The value nz = Ap [n] is thus the + total number of entries in the pattern of the matrix A. + + Int Ai [nz] ; Input argument, not modified, of size nz = Ap [n]. + + If col_form is true (nonzero), then the nonzero pattern (row indices) + for column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)]. Row indices + must be in the range 0 to n_row-1 (the matrix is 0-based). + + Otherwise, the nonzero pattern (column indices) for row j is stored in + Ai [(Ap [j]) ... (Ap [j+1]-1)]. Column indices must be in the range 0 + to n_col-1 (the matrix is 0-based). + + double Ax [nz] ; Input argument, not modified, of size nz = Ap [n]. + + The numerical values of the sparse matrix A. + + If col_form is true (nonzero), then the nonzero pattern (row indices) + for column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and the + corresponding (real) numerical values are stored in + Ax [(Ap [j]) ... (Ap [j+1]-1)]. The imaginary parts are stored in + Az [(Ap [j]) ... (Ap [j+1]-1)], for the complex versions. + + Otherwise, the nonzero pattern (column indices) for row j + is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and the corresponding + (real) numerical values are stored in Ax [(Ap [j]) ... (Ap [j+1]-1)]. + The imaginary parts are stored in Az [(Ap [j]) ... (Ap [j+1]-1)], + for the complex versions. + + No numerical values are printed if Ax or Az are (double *) NULL. + + double Az [nz] ; Input argument, not modified, for complex versions. + + The imaginary values of the sparse matrix A. See the description + of Ax, above. No numerical values are printed if Az is NULL. + + Future complex version: if Ax is present and Az is NULL, then both real + and imaginary parts will be contained in Ax[0..2*nz-1], with Ax[2*k] + and Ax[2*k+1] being the real and imaginary part of the kth entry. + + Int col_form ; Input argument, not modified. + + The matrix is in row-oriented form if form is col_form is false (0). + Otherwise, the matrix is in column-oriented form. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PRL]: printing level. + + 2 or less: no output. returns silently without checking anything. + 3: fully check input, and print a short summary of its status + 4: as 3, but print first few entries of the input + 5: as 3, but print all of the input + Default: 1 +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_numeric.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_numeric.h new file mode 100644 index 0000000000..54fd8f7ffe --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_numeric.h @@ -0,0 +1,113 @@ +/* ========================================================================== */ +/* === umfpack_report_numeric =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_numeric +( + void *Numeric, + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_dl_report_numeric +( + void *Numeric, + const double Control [UMFPACK_CONTROL] +) ; + +int umfpack_zi_report_numeric +( + void *Numeric, + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_zl_report_numeric +( + void *Numeric, + const double Control [UMFPACK_CONTROL] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Numeric ; + double Control [UMFPACK_CONTROL] ; + int status ; + status = umfpack_di_report_numeric (Numeric, Control) ; + +double long Syntax: + + #include "umfpack.h" + void *Numeric ; + double Control [UMFPACK_CONTROL] ; + long status ; + status = umfpack_dl_report_numeric (Numeric, Control) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + double Control [UMFPACK_CONTROL] ; + int status ; + status = umfpack_zi_report_numeric (Numeric, Control) ; + +complex long Syntax: + + #include "umfpack.h" + void *Numeric ; + double Control [UMFPACK_CONTROL] ; + long status ; + status = umfpack_zl_report_numeric (Numeric, Control) ; + +Purpose: + + Verifies and prints a Numeric object (the LU factorization, both its pattern + numerical values, and permutation vectors P and Q). This routine checks the + object more carefully than the computational routines. Normally, this check + is not required, since umfpack_*_numeric either returns (void *) NULL, or a + valid Numeric object. However, if you suspect that your own code has + corrupted the Numeric object (by overruning memory bounds, for example), + then this routine might be able to detect a corrupted Numeric object. Since + this is a complex object, not all such user-generated errors are guaranteed + to be caught by this routine. + +Returns: + + UMFPACK_OK if Control [UMFPACK_PRL] <= 2 (the input is not checked). + + Otherwise: + + UMFPACK_OK if the Numeric object is valid. + UMFPACK_ERROR_invalid_Numeric_object if the Numeric object is invalid. + UMFPACK_ERROR_out_of_memory if out of memory. + +Arguments: + + void *Numeric ; Input argument, not modified. + + The Numeric object, which holds the numeric factorization computed by + umfpack_*_numeric. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PRL]: printing level. + + 2 or less: no output. returns silently without checking anything. + 3: fully check input, and print a short summary of its status + 4: as 3, but print first few entries of the input + 5: as 3, but print all of the input + Default: 1 +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_perm.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_perm.h new file mode 100644 index 0000000000..17969dd6e1 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_perm.h @@ -0,0 +1,113 @@ +/* ========================================================================== */ +/* === umfpack_report_perm ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_perm +( + int np, + const int Perm [ ], + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_dl_report_perm +( + long np, + const long Perm [ ], + const double Control [UMFPACK_CONTROL] +) ; + +int umfpack_zi_report_perm +( + int np, + const int Perm [ ], + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_zl_report_perm +( + long np, + const long Perm [ ], + const double Control [UMFPACK_CONTROL] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int np, *Perm, status ; + double Control [UMFPACK_CONTROL] ; + status = umfpack_di_report_perm (np, Perm, Control) ; + +double long Syntax: + + #include "umfpack.h" + long np, *Perm, status ; + double Control [UMFPACK_CONTROL] ; + status = umfpack_dl_report_perm (np, Perm, Control) ; + +complex int Syntax: + + #include "umfpack.h" + int np, *Perm, status ; + double Control [UMFPACK_CONTROL] ; + status = umfpack_zi_report_perm (np, Perm, Control) ; + +complex long Syntax: + + #include "umfpack.h" + long np, *Perm, status ; + double Control [UMFPACK_CONTROL] ; + status = umfpack_zl_report_perm (np, Perm, Control) ; + +Purpose: + + Verifies and prints a permutation vector. + +Returns: + + UMFPACK_OK if Control [UMFPACK_PRL] <= 2 (the input is not checked). + + Otherwise: + UMFPACK_OK if the permutation vector is valid (this includes that case + when Perm is (Int *) NULL, which is not an error condition). + UMFPACK_ERROR_n_nonpositive if np <= 0. + UMFPACK_ERROR_out_of_memory if out of memory. + UMFPACK_ERROR_invalid_permutation if Perm is not a valid permutation vector. + +Arguments: + + Int np ; Input argument, not modified. + + Perm is an integer vector of size np. Restriction: np > 0. + + Int Perm [np] ; Input argument, not modified. + + A permutation vector of size np. If Perm is not present (an (Int *) + NULL pointer), then it is assumed to be the identity permutation. This + is consistent with its use as an input argument to umfpack_*_qsymbolic, + and is not an error condition. If Perm is present, the entries in Perm + must range between 0 and np-1, and no duplicates may exist. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PRL]: printing level. + + 2 or less: no output. returns silently without checking anything. + 3: fully check input, and print a short summary of its status + 4: as 3, but print first few entries of the input + 5: as 3, but print all of the input + Default: 1 +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_status.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_status.h new file mode 100644 index 0000000000..7d826de56f --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_status.h @@ -0,0 +1,91 @@ +/* ========================================================================== */ +/* === umfpack_report_status ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +void umfpack_di_report_status +( + const double Control [UMFPACK_CONTROL], + int status +) ; + +void umfpack_dl_report_status +( + const double Control [UMFPACK_CONTROL], + long status +) ; + +void umfpack_zi_report_status +( + const double Control [UMFPACK_CONTROL], + int status +) ; + +void umfpack_zl_report_status +( + const double Control [UMFPACK_CONTROL], + long status +) ; + +/* +double int Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + int status ; + umfpack_di_report_status (Control, status) ; + +double long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + long status ; + umfpack_dl_report_status (Control, status) ; + +complex int Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + int status ; + umfpack_zi_report_status (Control, status) ; + +complex long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + long status ; + umfpack_zl_report_status (Control, status) ; + +Purpose: + + Prints the status (return value) of other umfpack_* routines. + +Arguments: + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PRL]: printing level. + + 0 or less: no output, even when an error occurs + 1: error messages only + 2 or more: print status, whether or not an error occured + 4 or more: also print the UMFPACK Copyright + 6 or more: also print the UMFPACK License + Default: 1 + + Int status ; Input argument, not modified. + + The return value from another umfpack_* routine. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_symbolic.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_symbolic.h new file mode 100644 index 0000000000..f9d9f12c03 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_symbolic.h @@ -0,0 +1,112 @@ +/* ========================================================================== */ +/* === umfpack_report_symbolic ============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_symbolic +( + void *Symbolic, + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_dl_report_symbolic +( + void *Symbolic, + const double Control [UMFPACK_CONTROL] +) ; + +int umfpack_zi_report_symbolic +( + void *Symbolic, + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_zl_report_symbolic +( + void *Symbolic, + const double Control [UMFPACK_CONTROL] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Symbolic ; + double Control [UMFPACK_CONTROL] ; + int status ; + status = umfpack_di_report_symbolic (Symbolic, Control) ; + +double long Syntax: + + #include "umfpack.h" + void *Symbolic ; + double Control [UMFPACK_CONTROL] ; + long status ; + status = umfpack_dl_report_symbolic (Symbolic, Control) ; + +complex int Syntax: + + #include "umfpack.h" + void *Symbolic ; + double Control [UMFPACK_CONTROL] ; + int status ; + status = umfpack_zi_report_symbolic (Symbolic, Control) ; + +complex long Syntax: + + #include "umfpack.h" + void *Symbolic ; + double Control [UMFPACK_CONTROL] ; + long status ; + status = umfpack_zl_report_symbolic (Symbolic, Control) ; + +Purpose: + + Verifies and prints a Symbolic object. This routine checks the object more + carefully than the computational routines. Normally, this check is not + required, since umfpack_*_*symbolic either returns (void *) NULL, or a valid + Symbolic object. However, if you suspect that your own code has corrupted + the Symbolic object (by overruning memory bounds, for example), then this + routine might be able to detect a corrupted Symbolic object. Since this is + a complex object, not all such user-generated errors are guaranteed to be + caught by this routine. + +Returns: + + UMFPACK_OK if Control [UMFPACK_PRL] is <= 2 (no inputs are checked). + + Otherwise: + + UMFPACK_OK if the Symbolic object is valid. + UMFPACK_ERROR_invalid_Symbolic_object if the Symbolic object is invalid. + UMFPACK_ERROR_out_of_memory if out of memory. + +Arguments: + + void *Symbolic ; Input argument, not modified. + + The Symbolic object, which holds the symbolic factorization computed by + umfpack_*_*symbolic. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PRL]: printing level. + + 2 or less: no output. returns silently without checking anything. + 3: fully check input, and print a short summary of its status + 4: as 3, but print first few entries of the input + 5: as 3, but print all of the input + Default: 1 +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_triplet.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_triplet.h new file mode 100644 index 0000000000..928675400d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_triplet.h @@ -0,0 +1,149 @@ +/* ========================================================================== */ +/* === umfpack_report_triplet =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_triplet +( + int n_row, + int n_col, + int nz, + const int Ti [ ], + const int Tj [ ], + const double Tx [ ], + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_dl_report_triplet +( + long n_row, + long n_col, + long nz, + const long Ti [ ], + const long Tj [ ], + const double Tx [ ], + const double Control [UMFPACK_CONTROL] +) ; + +int umfpack_zi_report_triplet +( + int n_row, + int n_col, + int nz, + const int Ti [ ], + const int Tj [ ], + const double Tx [ ], const double Tz [ ], + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_zl_report_triplet +( + long n_row, + long n_col, + long nz, + const long Ti [ ], + const long Tj [ ], + const double Tx [ ], const double Tz [ ], + const double Control [UMFPACK_CONTROL] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int n_row, n_col, nz, *Ti, *Tj, status ; + double *Tx, Control [UMFPACK_CONTROL] ; + status = umfpack_di_report_triplet (n_row, n_col, nz, Ti, Tj, Tx, Control) ; + +double long Syntax: + + #include "umfpack.h" + long n_row, n_col, nz, *Ti, *Tj, status ; + double *Tx, Control [UMFPACK_CONTROL] ; + status = umfpack_dl_report_triplet (n_row, n_col, nz, Ti, Tj, Tx, Control) ; + +complex int Syntax: + + #include "umfpack.h" + int n_row, n_col, nz, *Ti, *Tj, status ; + double *Tx, *Tz, Control [UMFPACK_CONTROL] ; + status = umfpack_zi_report_triplet (n_row, n_col, nz, Ti, Tj, Tx, Tz, + Control) ; + +complex long Syntax: + + #include "umfpack.h" + long n_row, n_col, nz, *Ti, *Tj, status ; + double *Tx, *Tz, Control [UMFPACK_CONTROL] ; + status = umfpack_zl_report_triplet (n_row, n_col, nz, Ti, Tj, Tx, Tz, + Control) ; + +Purpose: + + Verifies and prints a matrix in triplet form. + +Returns: + + UMFPACK_OK if Control [UMFPACK_PRL] <= 2 (the input is not checked). + + Otherwise: + + UMFPACK_OK if the Triplet matrix is OK. + UMFPACK_ERROR_argument_missing if Ti and/or Tj are missing. + UMFPACK_ERROR_n_nonpositive if n_row <= 0 or n_col <= 0. + UMFPACK_ERROR_invalid_matrix if nz < 0, or + if any row or column index in Ti and/or Tj + is not in the range 0 to n_row-1 or 0 to n_col-1, respectively. + +Arguments: + + Int n_row ; Input argument, not modified. + Int n_col ; Input argument, not modified. + + A is an n_row-by-n_col matrix. + + Int nz ; Input argument, not modified. + + The number of entries in the triplet form of the matrix. + + Int Ti [nz] ; Input argument, not modified. + Int Tj [nz] ; Input argument, not modified. + double Tx [nz] ; Input argument, not modified. + double Tz [nz] ; Input argument, not modified, for complex versions. + + Ti, Tj, Tx (and Tz for complex versions) hold the "triplet" form of a + sparse matrix. The kth nonzero entry is in row i = Ti [k], column + j = Tj [k], the real numerical value of a_ij is Tx [k], and the + imaginary part of a_ij is Tz [k] (for complex versions). The row and + column indices i and j must be in the range 0 to n_row-1 or 0 to + n_col-1, respectively. Duplicate entries may be present. The + "triplets" may be in any order. Tx and Tz are optional; if Tx or Tz are + not present ((double *) NULL pointers), then the numerical values are + not printed. + + Future complex version: if Tx is present and Tz is NULL, then both real + and imaginary parts will be contained in Tx[0..2*nz-1], with Tx[2*k] + and Tx[2*k+1] being the real and imaginary part of the kth entry. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PRL]: printing level. + + 2 or less: no output. returns silently without checking anything. + 3: fully check input, and print a short summary of its status + 4: as 3, but print first few entries of the input + 5: as 3, but print all of the input + Default: 1 +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_vector.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_vector.h new file mode 100644 index 0000000000..66db99d1f8 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_report_vector.h @@ -0,0 +1,134 @@ +/* ========================================================================== */ +/* === umfpack_report_vector ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_vector +( + int n, + const double X [ ], + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_dl_report_vector +( + long n, + const double X [ ], + const double Control [UMFPACK_CONTROL] +) ; + +int umfpack_zi_report_vector +( + int n, + const double Xx [ ], const double Xz [ ], + const double Control [UMFPACK_CONTROL] +) ; + +long umfpack_zl_report_vector +( + long n, + const double Xx [ ], const double Xz [ ], + const double Control [UMFPACK_CONTROL] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int n, status ; + double *X, Control [UMFPACK_CONTROL] ; + status = umfpack_di_report_vector (n, X, Control) ; + +double long Syntax: + + #include "umfpack.h" + long n, status ; + double *X, Control [UMFPACK_CONTROL] ; + status = umfpack_dl_report_vector (n, X, Control) ; + +complex int Syntax: + + #include "umfpack.h" + int n, status ; + double *Xx, *Xz, Control [UMFPACK_CONTROL] ; + status = umfpack_zi_report_vector (n, Xx, Xz, Control) ; + +complex long Syntax: + + #include "umfpack.h" + long n, status ; + double *Xx, *Xz, Control [UMFPACK_CONTROL] ; + status = umfpack_zl_report_vector (n, Xx, Xz, Control) ; + +Purpose: + + Verifies and prints a dense vector. + +Returns: + + UMFPACK_OK if Control [UMFPACK_PRL] <= 2 (the input is not checked). + + Otherwise: + + UMFPACK_OK if the vector is valid. + UMFPACK_ERROR_argument_missing if X or Xx is missing. + UMFPACK_ERROR_n_nonpositive if n <= 0. + +Arguments: + + Int n ; Input argument, not modified. + + X is a real or complex vector of size n. Restriction: n > 0. + + double X [n] ; Input argument, not modified. For real versions. + + A real vector of size n. X must not be (double *) NULL. + + double Xx [n or 2*n] ; Input argument, not modified. For complex versions. + double Xz [n or 0] ; Input argument, not modified. For complex versions. + + A complex vector of size n, in one of two storage formats. + Xx must not be (double *) NULL. + + If Xz is not (double *) NULL, then Xx [i] is the real part of X (i) and + Xz [i] is the imaginary part of X (i). Both vectors are of length n. + This is the "split" form of the complex vector X. + + If Xz is (double *) NULL, then Xx holds both real and imaginary parts, + where Xx [2*i] is the real part of X (i) and Xx [2*i+1] is the imaginary + part of X (i). Xx is of length 2*n doubles. If you have an ANSI C99 + compiler with the intrinsic double _Complex type, then Xx can be of + type double _Complex in the calling routine and typecast to (double *) + when passed to umfpack_*_report_vector (this is untested, however). + This is the "merged" form of the complex vector X. + + Future work: all complex routines in UMFPACK could use this same + strategy for their complex arguments. The split format is useful for + MATLAB, which holds its real and imaginary parts in seperate arrays. + The merged format is compatible with the intrinsic double _Complex + type in ANSI C99, and is also compatible with SuperLU's method of + storing complex matrices. In the current version, only + umfpack_*_report_vector supports both formats. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_PRL]: printing level. + + 2 or less: no output. returns silently without checking anything. + 3: fully check input, and print a short summary of its status + 4: as 3, but print first few entries of the input + 5: as 3, but print all of the input + Default: 1 +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_save_numeric.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_save_numeric.h new file mode 100644 index 0000000000..0e3f57be87 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_save_numeric.h @@ -0,0 +1,93 @@ +/* ========================================================================== */ +/* === umfpack_save_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_save_numeric +( + void *Numeric, + char *filename +) ; + +long umfpack_dl_save_numeric +( + void *Numeric, + char *filename +) ; + +int umfpack_zi_save_numeric +( + void *Numeric, + char *filename +) ; + +long umfpack_zl_save_numeric +( + void *Numeric, + char *filename +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int status ; + char *filename ; + void *Numeric ; + status = umfpack_di_save_numeric (Numeric, filename) ; + +double long Syntax: + + #include "umfpack.h" + long status ; + char *filename ; + void *Numeric ; + status = umfpack_dl_save_numeric (Numeric, filename) ; + +complex int Syntax: + + #include "umfpack.h" + int status ; + char *filename ; + void *Numeric ; + status = umfpack_zi_save_numeric (Numeric, filename) ; + +complex long Syntax: + + #include "umfpack.h" + long status ; + char *filename ; + void *Numeric ; + status = umfpack_zl_save_numeric (Numeric, filename) ; + +Purpose: + + Saves a Numeric object to a file, which can later be read by + umfpack_*_load_numeric. The Numeric object is not modified. You need + to call umfpack_*_free_numeric if you to delete the Numeric object after + saving it to a file. + +Returns: + + UMFPACK_OK if successful. + UMFPACK_ERROR_invalid_Numeric_object if Numeric is not valid. + UMFPACK_ERROR_file_IO if an I/O error occurred. + +Arguments: + + void *Numeric ; Input argument, not modified. + + Numeric must point to a valid Numeric object, computed by + umfpack_*_numeric or loaded by umfpack_*_load_numeric. + + char *filename ; Input argument, not modified. + + A string that contains the filename to which the Numeric + object is written. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_save_symbolic.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_save_symbolic.h new file mode 100644 index 0000000000..a183ede9b2 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_save_symbolic.h @@ -0,0 +1,93 @@ +/* ========================================================================== */ +/* === umfpack_save_symbolic================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_save_symbolic +( + void *Symbolic, + char *filename +) ; + +long umfpack_dl_save_symbolic +( + void *Symbolic, + char *filename +) ; + +int umfpack_zi_save_symbolic +( + void *Symbolic, + char *filename +) ; + +long umfpack_zl_save_symbolic +( + void *Symbolic, + char *filename +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int status ; + char *filename ; + void *Symbolic ; + status = umfpack_di_save_symbolic (Symbolic, filename) ; + +double long Syntax: + + #include "umfpack.h" + long status ; + char *filename ; + void *Symbolic ; + status = umfpack_dl_save_symbolic (Symbolic, filename) ; + +complex int Syntax: + + #include "umfpack.h" + int status ; + char *filename ; + void *Symbolic ; + status = umfpack_zi_save_symbolic (Symbolic, filename) ; + +complex long Syntax: + + #include "umfpack.h" + long status ; + char *filename ; + void *Symbolic ; + status = umfpack_zl_save_symbolic (Symbolic, filename) ; + +Purpose: + + Saves a Symbolic object to a file, which can later be read by + umfpack_*_load_symbolic. The Symbolic object is not modified. You need + to call umfpack_*_free_symbolic if you to delete the Symbolic object after + saving it to a file. + +Returns: + + UMFPACK_OK if successful. + UMFPACK_ERROR_invalid_Symbolic_object if Symbolic is not valid. + UMFPACK_ERROR_file_IO if an I/O error occurred. + +Arguments: + + void *Symbolic ; Input argument, not modified. + + Symbolic must point to a valid Symbolic object, computed by + umfpack_*_symbolic or loaded by umfpack_*_load_symbolic. + + char *filename ; Input argument, not modified. + + A string that contains the filename to which the Symbolic + object is written. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_scale.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_scale.h new file mode 100644 index 0000000000..8e254f8f2e --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_scale.h @@ -0,0 +1,105 @@ +/* ========================================================================== */ +/* === umfpack_scale ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_scale +( + double X [ ], + const double B [ ], + void *Numeric +) ; + +long umfpack_dl_scale +( + double X [ ], + const double B [ ], + void *Numeric +) ; + +int umfpack_zi_scale +( + double Xx [ ], double Xz [ ], + const double Bx [ ], const double Bz [ ], + void *Numeric +) ; + +long umfpack_zl_scale +( + double Xx [ ], double Xz [ ], + const double Bx [ ], const double Bz [ ], + void *Numeric +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Numeric ; + double *B, *X ; + status = umfpack_di_scale (X, B, Numeric) ; + +double long Syntax: + + #include "umfpack.h" + void *Numeric ; + double *B, *X ; + status = umfpack_dl_scale (X, B, Numeric) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + double *Bx, *Bz, *Xx, *Xz ; + status = umfpack_zi_scale (Xx, Xz, Bx, Bz, Numeric) ; + +complex long Syntax: + + #include "umfpack.h" + void *Numeric ; + double *Bx, *Bz, *Xx, *Xz ; + status = umfpack_zl_scale (Xx, Xz, Bx, Bz, Numeric) ; + +Purpose: + + Given LU factors computed by umfpack_*_numeric (PAQ=LU, PRAQ=LU, or + P(R\A)Q=LU), and a vector B, this routine computes X = B, X = R*B, or + X = R\B, as appropriate. X and B must be vectors equal in length to the + number of rows of A. + +Returns: + + The status code is returned. UMFPACK_OK is returned if successful. + UMFPACK_ERROR_invalid_Numeric_object is returned in the Numeric + object is invalid. UMFPACK_ERROR_argument_missing is returned if + any of the input vectors are missing (X and B for the real version, + and Xx, Xz, Bx, and Bz for the complex version). + +Arguments: + + double X [n_row] ; Output argument. + or: + double Xx [n_row] ; Output argument, real part. + double Xz [n_row] ; Output argument, imaginary part. + + The output vector X. + + double B [n_row] ; Input argument, not modified. + or: + double Bx [n_row] ; Input argument, not modified, real part. + double Bz [n_row] ; Input argument, not modified, imaginary part. + + The input vector B. + + void *Numeric ; Input argument, not modified. + + Numeric must point to a valid Numeric object, computed by + umfpack_*_numeric. + +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_solve.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_solve.h new file mode 100644 index 0000000000..d6a0a96106 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_solve.h @@ -0,0 +1,302 @@ +/* ========================================================================== */ +/* === umfpack_solve ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_solve +( + int sys, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + double X [ ], + const double B [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +long umfpack_dl_solve +( + long sys, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], + double X [ ], + const double B [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +int umfpack_zi_solve +( + int sys, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], const double Az [ ], + double Xx [ ], double Xz [ ], + const double Bx [ ], const double Bz [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +long umfpack_zl_solve +( + long sys, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], const double Az [ ], + double Xx [ ], double Xz [ ], + const double Bx [ ], const double Bz [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status, *Ap, *Ai, sys ; + double *B, *X, *Ax, Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ; + status = umfpack_di_solve (sys, Ap, Ai, Ax, X, B, Numeric, Control, Info) ; + +double long Syntax: + + #include "umfpack.h" + void *Numeric ; + long status, *Ap, *Ai, sys ; + double *B, *X, *Ax, Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ; + status = umfpack_dl_solve (sys, Ap, Ai, Ax, X, B, Numeric, Control, Info) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status, *Ap, *Ai, sys ; + double *Bx, *Bz, *Xx, *Xz, *Ax, *Az, Info [UMFPACK_INFO], + Control [UMFPACK_CONTROL] ; + status = umfpack_zi_solve (sys, Ap, Ai, Ax, Az, Xx, Xz, Bx, Bz, Numeric, + Control, Info) ; + +complex long Syntax: + + #include "umfpack.h" + void *Numeric ; + long status, *Ap, *Ai, sys ; + double *Bx, *Bz, *Xx, *Xz, *Ax, *Az, Info [UMFPACK_INFO], + Control [UMFPACK_CONTROL] ; + status = umfpack_zl_solve (sys, Ap, Ai, Ax, Az, Xx, Xz, Bx, Bz, Numeric, + Control, Info) ; + +Purpose: + + Given LU factors computed by umfpack_*_numeric (PAQ=LU, PRAQ=LU, or + P(R\A)Q=LU) and the right-hand-side, B, solve a linear system for the + solution X. Iterative refinement is optionally performed. Only square + systems are handled. Singular matrices result in a divide-by-zero for all + systems except those involving just the matrix L. Iterative refinement is + not performed for singular matrices. In the discussion below, n is equal + to n_row and n_col, because only square systems are handled. + +Returns: + + The status code is returned. See Info [UMFPACK_STATUS], below. + +Arguments: + + Int sys ; Input argument, not modified. + + Defines which system to solve. (') is the linear algebraic transpose + (complex conjugate if A is complex), and (.') is the array transpose. + + sys value system solved + UMFPACK_A Ax=b + UMFPACK_At A'x=b + UMFPACK_Aat A.'x=b + UMFPACK_Pt_L P'Lx=b + UMFPACK_L Lx=b + UMFPACK_Lt_P L'Px=b + UMFPACK_Lat_P L.'Px=b + UMFPACK_Lt L'x=b + UMFPACK_U_Qt UQ'x=b + UMFPACK_U Ux=b + UMFPACK_Q_Ut QU'x=b + UMFPACK_Q_Uat QU.'x=b + UMFPACK_Ut U'x=b + UMFPACK_Uat U.'x=b + + Iterative refinement can be optionally performed when sys is any of + the following: + + UMFPACK_A Ax=b + UMFPACK_At A'x=b + UMFPACK_Aat A.'x=b + + For the other values of the sys argument, iterative refinement is not + performed (Control [UMFPACK_IRSTEP], Ap, Ai, Ax, and Az are ignored). + + Earlier versions used a string argument for sys. It was changed to an + integer to make it easier for a Fortran code to call UMFPACK. + + Int Ap [n+1] ; Input argument, not modified. + Int Ai [nz] ; Input argument, not modified. + double Ax [nz] ; Input argument, not modified. + double Az [nz] ; Input argument, not modified, for complex versions. + + If iterative refinement is requested (Control [UMFPACK_IRSTEP] >= 1, + Ax=b, A'x=b, or A.'x=b is being solved, and A is nonsingular), then + these arrays must be identical to the same ones passed to + umfpack_*_numeric. The umfpack_*_solve routine does not check the + contents of these arguments, so the results are undefined if Ap, Ai, Ax, + and/or Az are modified between the calls the umfpack_*_numeric and + umfpack_*_solve. These three arrays do not need to be present (NULL + pointers can be passed) if Control [UMFPACK_IRSTEP] is zero, or if a + system other than Ax=b, A'x=b, or A.'x=b is being solved, or if A is + singular, since in each of these cases A is not accessed. + + Future complex version: if Ax is present and Az is NULL, then both real + and imaginary parts will be contained in Ax[0..2*nz-1], with Ax[2*k] + and Ax[2*k+1] being the real and imaginary part of the kth entry. + + double X [n] ; Output argument. + or: + double Xx [n] ; Output argument, real part. + double Xz [n] ; Output argument, imaginary part. + + The solution to the linear system, where n = n_row = n_col is the + dimension of the matrices A, L, and U. + + Future complex version: if Xx is present and Xz is NULL, then both real + and imaginary parts will be returned in Xx[0..2*n-1], with Xx[2*k] and + Xx[2*k+1] being the real and imaginary part of the kth entry. + + double B [n] ; Input argument, not modified. + or: + double Bx [n] ; Input argument, not modified, real part. + double Bz [n] ; Input argument, not modified, imaginary part. + + The right-hand side vector, b, stored as a conventional array of size n + (or two arrays of size n for complex versions). This routine does not + solve for multiple right-hand-sides, nor does it allow b to be stored in + a sparse-column form. + + Future complex version: if Bx is present and Bz is NULL, then both real + and imaginary parts will be contained in Bx[0..2*n-1], with Bx[2*k] + and Bx[2*k+1] being the real and imaginary part of the kth entry. + + void *Numeric ; Input argument, not modified. + + Numeric must point to a valid Numeric object, computed by + umfpack_*_numeric. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used. Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_IRSTEP]: The maximum number of iterative refinement + steps to attempt. A value less than zero is treated as zero. If + less than 1, or if Ax=b, A'x=b, or A.'x=b is not being solved, or + if A is singular, then the Ap, Ai, Ax, and Az arguments are not + accessed. Default: 2. + + double Info [UMFPACK_INFO] ; Output argument. + + Contains statistics about the solution factorization. If a + (double *) NULL pointer is passed, then no statistics are returned in + Info (this is not an error condition). The following statistics are + computed in umfpack_*_solve: + + Info [UMFPACK_STATUS]: status code. This is also the return value, + whether or not Info is present. + + UMFPACK_OK + + The linear system was successfully solved. + + UMFPACK_WARNING_singular_matrix + + A divide-by-zero occured. Your solution will contain Inf's + and/or NaN's. Some parts of the solution may be valid. For + example, solving Ax=b with + + A = [2 0] b = [ 1 ] returns x = [ 0.5 ] + [0 0] [ 0 ] [ Inf ] + + UMFPACK_ERROR_out_of_memory + + Insufficient memory to solve the linear system. + + UMFPACK_ERROR_argument_missing + + One or more required arguments are missing. The B, X, (or + Bx, Bz, Xx and Xz for the complex versions) arguments + are always required. Info and Control are not required. Ap, + Ai, Ax (and Az for complex versions) are required if Ax=b, + A'x=b, A.'x=b is to be solved, the (default) iterative + refinement is requested, and the matrix A is nonsingular. + + UMFPACK_ERROR_invalid_system + + The sys argument is not valid, or the matrix A is not square. + + UMFPACK_ERROR_invalid_Numeric_object + + The Numeric object is not valid. + + Info [UMFPACK_NROW], Info [UMFPACK_NCOL]: + The dimensions of the matrix A (L is n_row-by-n_inner and + U is n_inner-by-n_col, with n_inner = min(n_row,n_col)). + + Info [UMFPACK_NZ]: the number of entries in the input matrix, Ap [n], + if iterative refinement is requested (Ax=b, A'x=b, or A.'x=b is + being solved, Control [UMFPACK_IRSTEP] >= 1, and A is nonsingular). + + Info [UMFPACK_IR_TAKEN]: The number of iterative refinement steps + effectively taken. The number of steps attempted may be one more + than this; the refinement algorithm backtracks if the last + refinement step worsens the solution. + + Info [UMFPACK_IR_ATTEMPTED]: The number of iterative refinement steps + attempted. The number of times a linear system was solved is one + more than this (once for the initial Ax=b, and once for each Ay=r + solved for each iterative refinement step attempted). + + Info [UMFPACK_OMEGA1]: sparse backward error estimate, omega1, if + iterative refinement was performed, or -1 if iterative refinement + not performed. + + Info [UMFPACK_OMEGA2]: sparse backward error estimate, omega2, if + iterative refinement was performed, or -1 if iterative refinement + not performed. + + Info [UMFPACK_SOLVE_FLOPS]: the number of floating point operations + performed to solve the linear system. This includes the work + taken for all iterative refinement steps, including the backtrack + (if any). + + Info [UMFPACK_SOLVE_TIME]: The time taken, in seconds. + + ------------------------------------------------------------------------ + The following statistic was added to Version 4.1: + ------------------------------------------------------------------------ + + Info [UMFPACK_SOLVE_WALLTIME]: The wallclock time taken, in seconds. + + Only the above listed Info [...] entries are accessed. The remaining + entries of Info are not accessed or modified by umfpack_*_solve. + Future versions might modify different parts of Info. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_symbolic.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_symbolic.h new file mode 100644 index 0000000000..ed09ca5d1d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_symbolic.h @@ -0,0 +1,542 @@ +/* ========================================================================== */ +/* === umfpack_symbolic ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_symbolic +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +long umfpack_dl_symbolic +( + long n_row, + long n_col, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +int umfpack_zi_symbolic +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], const double Az [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +long umfpack_zl_symbolic +( + long n_row, + long n_col, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], const double Az [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Symbolic ; + int n_row, n_col, *Ap, *Ai, status ; + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], *Ax ; + status = umfpack_di_symbolic (n_row, n_col, Ap, Ai, Ax, + &Symbolic, Control, Info) ; + +double long Syntax: + + #include "umfpack.h" + void *Symbolic ; + long n_row, n_col, *Ap, *Ai, status ; + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], *Ax ; + status = umfpack_dl_symbolic (n_row, n_col, Ap, Ai, Ax, + &Symbolic, Control, Info) ; + +complex int Syntax: + + #include "umfpack.h" + void *Symbolic ; + int n_row, n_col, *Ap, *Ai, status ; + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], *Ax, *Az ; + status = umfpack_zi_symbolic (n_row, n_col, Ap, Ai, Ax, Az, + &Symbolic, Control, Info) ; + +complex long Syntax: + + #include "umfpack.h" + void *Symbolic ; + long n_row, n_col, *Ap, *Ai, status ; + double Control [UMFPACK_CONTROL], Info [UMFPACK_INFO], *Ax, *Az ; + status = umfpack_zl_symbolic (n_row, n_col, Ap, Ai, Ax, Az, + &Symbolic, Control, Info) ; + +Purpose: + + Given nonzero pattern of a sparse matrix A in column-oriented form, + umfpack_*_symbolic performs a column pre-ordering to reduce fill-in + (using COLAMD or AMD) and a symbolic factorization. This is required + before the matrix can be numerically factorized with umfpack_*_numeric. + If you wish to bypass the COLAMD or AMD pre-ordering and provide your own + ordering, use umfpack_*_qsymbolic instead. + + Since umfpack_*_symbolic and umfpack_*_qsymbolic are very similar, options + for both routines are discussed below. + + For the following discussion, let S be the submatrix of A obtained after + eliminating all pivots of zero Markowitz cost. S has dimension + (n_row-n1-nempty_row) -by- (n_col-n1-nempty_col), where + n1 = Info [UMFPACK_COL_SINGLETONS] + Info [UMFPACK_ROW_SINGLETONS], + nempty_row = Info [UMFPACK_NEMPTY_ROW] and + nempty_col = Info [UMFPACK_NEMPTY_COL]. + +Returns: + + The status code is returned. See Info [UMFPACK_STATUS], below. + +Arguments: + + Int n_row ; Input argument, not modified. + Int n_col ; Input argument, not modified. + + A is an n_row-by-n_col matrix. Restriction: n_row > 0 and n_col > 0. + + Int Ap [n_col+1] ; Input argument, not modified. + + Ap is an integer array of size n_col+1. On input, it holds the + "pointers" for the column form of the sparse matrix A. Column j of + the matrix A is held in Ai [(Ap [j]) ... (Ap [j+1]-1)]. The first + entry, Ap [0], must be zero, and Ap [j] <= Ap [j+1] must hold for all + j in the range 0 to n_col-1. The value nz = Ap [n_col] is thus the + total number of entries in the pattern of the matrix A. nz must be + greater than or equal to zero. + + Int Ai [nz] ; Input argument, not modified, of size nz = Ap [n_col]. + + The nonzero pattern (row indices) for column j is stored in + Ai [(Ap [j]) ... (Ap [j+1]-1)]. The row indices in a given column j + must be in ascending order, and no duplicate row indices may be present. + Row indices must be in the range 0 to n_row-1 (the matrix is 0-based). + See umfpack_*_triplet_to_col for how to sort the columns of a matrix + and sum up the duplicate entries. See umfpack_*_report_matrix for how + to print the matrix A. + + double Ax [nz] ; Optional input argument, not modified. + + The numerical values of the sparse matrix A. The nonzero pattern (row + indices) for column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and + the corresponding numerical values are stored in + Ax [(Ap [j]) ... (Ap [j+1]-1)]. Used only by the 2-by-2 strategy to + determine whether entries are "large" or "small". You do not have to + pass the same numerical values to umfpack_*_numeric. If Ax is not + present (a (double *) NULL pointer), then any entry in A is assumed to + be "large". + + double Az [nz] ; Optional input argument, not modified, for complex + versions. + + For the complex versions, this holds the imaginary part of A. The + imaginary part of column j is held in Az [(Ap [j]) ... (Ap [j+1]-1)]. + + Future complex version: if Ax is present and Az is NULL, then both real + and imaginary parts will be contained in Ax[0..2*nz-1], with Ax[2*k] + and Ax[2*k+1] being the real and imaginary part of the kth entry. + + Used by the 2-by-2 strategy only. See the description of Ax, above. + + void **Symbolic ; Output argument. + + **Symbolic is the address of a (void *) pointer variable in the user's + calling routine (see Syntax, above). On input, the contents of this + variable are not defined. On output, this variable holds a (void *) + pointer to the Symbolic object (if successful), or (void *) NULL if + a failure occurred. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If a (double *) NULL pointer is passed, then the default control + settings are used (the defaults are suitable for all matrices, + ranging from those with highly unsymmetric nonzero pattern, to + symmetric matrices). Otherwise, the settings are determined from the + Control array. See umfpack_*_defaults on how to fill the Control + array with the default settings. If Control contains NaN's, the + defaults are used. The following Control parameters are used: + + Control [UMFPACK_STRATEGY]: This is the most important control + parameter. It determines what kind of ordering and pivoting + strategy that UMFPACK should use. It is new to Version 4.1 + There are 4 options: + + UMFPACK_STRATEGY_AUTO: This is the default. The input matrix is + analyzed to determine how symmetric the nonzero pattern is, and + how many entries there are on the diagonal. It then selects one + of the following strategies. Refer to the User Guide for a + description of how the strategy is automatically selected. + + UMFPACK_STRATEGY_UNSYMMETRIC: Use the unsymmetric strategy. COLAMD + is used to order the columns of A, followed by a postorder of + the column elimination tree. No attempt is made to perform + diagonal pivoting. The column ordering is refined during + factorization. This strategy was the only one provided with + UMFPACK V4.0. + + In the numerical factorization, the + Control [UMFPACK_SYM_PIVOT_TOLERANCE] parameter is ignored. A + pivot is selected if its magnitude is >= + Control [UMFPACK_PIVOT_TOLERANCE] (default 0.1) times the + largest entry in its column. + + UMFPACK_STRATEGY_SYMMETRIC: Use the symmetric strategy (new to + Version 4.1). In this method, the approximate minimum degree + ordering (AMD) is applied to A+A', followed by a postorder of + the elimination tree of A+A'. UMFPACK attempts to perform + diagonal pivoting during numerical factorization. No refinement + of the column pre-ordering is performed during factorization. + + In the numerical factorization, a nonzero entry on the diagonal + is selected as the pivot if its magnitude is >= Control + [UMFPACK_SYM_PIVOT_TOLERANCE] (default 0.001) times the largest + entry in its column. If this is not acceptable, then an + off-diagonal pivot is selected with magnitude >= Control + [UMFPACK_PIVOT_TOLERANCE] (default 0.1) times the largest entry + in its column. + + UMFPACK_STRATEGY_2BY2: a row permutation P2 is found that places + large entries on the diagonal. The matrix P2*A is then + factorized using the symmetric strategy, described above. + Refer to the User Guide for more information. + + Control [UMFPACK_DENSE_COL]: + If COLAMD is used, columns with more than + max (16, Control [UMFPACK_DENSE_COL] * 16 * sqrt (n_row)) entries + are placed placed last in the column pre-ordering. Default: 0.2. + + Control [UMFPACK_DENSE_ROW]: + Rows with more than max (16, Control [UMFPACK_DENSE_ROW] * 16 * + sqrt (n_col)) entries are treated differently in the COLAMD + pre-ordering, and in the internal data structures during the + subsequent numeric factorization. Default: 0.2. + + Control [UMFPACK_AMD_DENSE]: rows/columns in A+A' with more than + max (16, Control [UMFPACK_AMD_DENSE] * sqrt (n)) entries + (where n = n_row = n_col) are ignored in the AMD pre-ordering. + Default: 10. + + Control [UMFPACK_BLOCK_SIZE]: the block size to use for Level-3 BLAS + in the subsequent numerical factorization (umfpack_*_numeric). + A value less than 1 is treated as 1. Default: 32. Modifying this + parameter affects when updates are applied to the working frontal + matrix, and can indirectly affect fill-in and operation count. + As long as the block size is large enough (8 or so), this parameter + has a modest effect on performance. + + Control [UMFPACK_2BY2_TOLERANCE]: a diagonal entry S (k,k) is + considered "small" if it is < tol * max (abs (S (:,k))), where S a + submatrix of the scaled input matrix, with pivots of zero Markowitz + cost removed. + + Control [UMFPACK_SCALE]: This parameter is new to V4.1. See + umfpack_numeric.h for a description. Only affects the 2-by-2 + strategy. Default: UMFPACK_SCALE_SUM. + + Control [UMFPACK_FIXQ]: If > 0, then the pre-ordering Q is not modified + during numeric factorization. If < 0, then Q may be modified. If + zero, then this is controlled automatically (the unsymmetric + strategy modifies Q, the others do not). Default: 0. + + Control [UMFPACK_AGGRESSIVE]: If nonzero, aggressive absorption is used + in COLAMD and AMD. Default: 1. + + double Info [UMFPACK_INFO] ; Output argument, not defined on input. + + Contains statistics about the symbolic analysis. If a (double *) NULL + pointer is passed, then no statistics are returned in Info (this is not + an error condition). The entire Info array is cleared (all entries set + to -1) and then the following statistics are computed: + + Info [UMFPACK_STATUS]: status code. This is also the return value, + whether or not Info is present. + + UMFPACK_OK + + Each column of the input matrix contained row indices + in increasing order, with no duplicates. Only in this case + does umfpack_*_symbolic compute a valid symbolic factorization. + For the other cases below, no Symbolic object is created + (*Symbolic is (void *) NULL). + + UMFPACK_ERROR_n_nonpositive + + n is less than or equal to zero. + + UMFPACK_ERROR_invalid_matrix + + Number of entries in the matrix is negative, Ap [0] is nonzero, + a column has a negative number of entries, a row index is out of + bounds, or the columns of input matrix were jumbled (unsorted + columns or duplicate entries). + + UMFPACK_ERROR_out_of_memory + + Insufficient memory to perform the symbolic analysis. If the + analysis requires more than 2GB of memory and you are using + the 32-bit ("int") version of UMFPACK, then you are guaranteed + to run out of memory. Try using the 64-bit version of UMFPACK. + + UMFPACK_ERROR_argument_missing + + One or more required arguments is missing. + + UMFPACK_ERROR_internal_error + + Something very serious went wrong. This is a bug. + Please contact the author (davis@cise.ufl.edu). + + Note that the UMFPACK_ERROR_problem_too_large error code is no + longer returned (it was in Version 4.0). + + Info [UMFPACK_NROW]: the value of the input argument n_row. + + Info [UMFPACK_NCOL]: the value of the input argument n_col. + + Info [UMFPACK_NZ]: the number of entries in the input matrix + (Ap [n_col]). + + Info [UMFPACK_SIZE_OF_UNIT]: the number of bytes in a Unit, + for memory usage statistics below. + + Info [UMFPACK_SIZE_OF_INT]: the number of bytes in an int. + + Info [UMFPACK_SIZE_OF_LONG]: the number of bytes in a long. + + Info [UMFPACK_SIZE_OF_POINTER]: the number of bytes in a void * + pointer. + + Info [UMFPACK_SIZE_OF_ENTRY]: the number of bytes in a numerical entry. + + Info [UMFPACK_NDENSE_ROW]: number of "dense" rows in A. These rows are + ignored when the column pre-ordering is computed in COLAMD. They + are also treated differently during numeric factorization. If > 0, + then the matrix had to be re-analyzed by UMF_analyze, which does + not ignore these rows. + + Info [UMFPACK_NEMPTY_ROW]: number of "empty" rows in A, as determined + These are rows that either have no entries, or whose entries are + all in pivot columns of zero-Markowitz-cost pivots. + + Info [UMFPACK_NDENSE_COL]: number of "dense" columns in A. COLAMD + orders these columns are ordered last in the factorization, but + before "empty" columns. + + Info [UMFPACK_NEMPTY_COL]: number of "empty" columns in A. These are + columns that either have no entries, or whose entries are all in + pivot rows of zero-Markowitz-cost pivots. These columns are + ordered last in the factorization, to the right of "dense" columns. + + Info [UMFPACK_SYMBOLIC_DEFRAG]: number of garbage collections + performed during ordering and symbolic pre-analysis. + + Info [UMFPACK_SYMBOLIC_PEAK_MEMORY]: the amount of memory (in Units) + required for umfpack_*_symbolic to complete. This count includes + the size of the Symbolic object itself, which is also reported in + Info [UMFPACK_SYMBOLIC_SIZE]. + + Info [UMFPACK_SYMBOLIC_SIZE]: the final size of the Symbolic object (in + Units). This is fairly small, roughly 2*n to 13*n integers, + depending on the matrix. + + Info [UMFPACK_VARIABLE_INIT_ESTIMATE]: the Numeric object contains two + parts. The first is fixed in size (O (n_row+n_col)). The + second part holds the sparse LU factors and the contribution blocks + from factorized frontal matrices. This part changes in size during + factorization. Info [UMFPACK_VARIABLE_INIT_ESTIMATE] is the exact + size (in Units) required for this second variable-sized part in + order for the numerical factorization to start. + + Info [UMFPACK_VARIABLE_PEAK_ESTIMATE]: the estimated peak size (in + Units) of the variable-sized part of the Numeric object. This is + usually an upper bound, but that is not guaranteed. + + Info [UMFPACK_VARIABLE_FINAL_ESTIMATE]: the estimated final size (in + Units) of the variable-sized part of the Numeric object. This is + usually an upper bound, but that is not guaranteed. It holds just + the sparse LU factors. + + Info [UMFPACK_NUMERIC_SIZE_ESTIMATE]: an estimate of the final size (in + Units) of the entire Numeric object (both fixed-size and variable- + sized parts), which holds the LU factorization (including the L, U, + P and Q matrices). + + Info [UMFPACK_PEAK_MEMORY_ESTIMATE]: an estimate of the total amount of + memory (in Units) required by umfpack_*_symbolic and + umfpack_*_numeric to perform both the symbolic and numeric + factorization. This is the larger of the amount of memory needed + in umfpack_*_numeric itself, and the amount of memory needed in + umfpack_*_symbolic (Info [UMFPACK_SYMBOLIC_PEAK_MEMORY]). The + count includes the size of both the Symbolic and Numeric objects + themselves. It can be a very loose upper bound, particularly when + the symmetric or 2-by-2 strategies are used. + + Info [UMFPACK_FLOPS_ESTIMATE]: an estimate of the total floating-point + operations required to factorize the matrix. This is a "true" + theoretical estimate of the number of flops that would be performed + by a flop-parsimonious sparse LU algorithm. It assumes that no + extra flops are performed except for what is strictly required to + compute the LU factorization. It ignores, for example, the flops + performed by umfpack_di_numeric to add contribution blocks of + frontal matrices together. If L and U are the upper bound on the + pattern of the factors, then this flop count estimate can be + represented in MATLAB (for real matrices, not complex) as: + + Lnz = full (sum (spones (L))) - 1 ; % nz in each col of L + Unz = full (sum (spones (U')))' - 1 ; % nz in each row of U + flops = 2*Lnz*Unz + sum (Lnz) ; + + The actual "true flop" count found by umfpack_*_numeric will be + less than this estimate. + + For the real version, only (+ - * /) are counted. For the complex + version, the following counts are used: + + operation flops + c = 1/b 6 + c = a*b 6 + c -= a*b 8 + + Info [UMFPACK_LNZ_ESTIMATE]: an estimate of the number of nonzeros in + L, including the diagonal. Since L is unit-diagonal, the diagonal + of L is not stored. This estimate is a strict upper bound on the + actual nonzeros in L to be computed by umfpack_*_numeric. + + Info [UMFPACK_UNZ_ESTIMATE]: an estimate of the number of nonzeros in + U, including the diagonal. This estimate is a strict upper bound on + the actual nonzeros in U to be computed by umfpack_*_numeric. + + Info [UMFPACK_MAX_FRONT_SIZE_ESTIMATE]: estimate of the size of the + largest frontal matrix (# of entries), for arbitrary partial + pivoting during numerical factorization. + + Info [UMFPACK_SYMBOLIC_TIME]: The CPU time taken, in seconds. + + ------------------------------------------------------------------------ + The rest of the statistics are new to Version 4.1: + ------------------------------------------------------------------------ + + Info [UMFPACK_SYMBOLIC_WALLTIME]: The wallclock time taken, in seconds. + + Info [UMFPACK_STRATEGY_USED]: The ordering strategy used: + UMFPACK_STRATEGY_SYMMETRIC, UMFPACK_STRATEGY_UNSYMMETRIC, or + UMFPACK_STRATEGY_2BY2. + + Info [UMFPACK_ORDERING_USED]: The ordering method used: + UMFPACK_ORDERING_COLAMD or UMFPACK_ORDERING_AMD. It can be + UMFPACK_ORDERING_GIVEN for umfpack_*_qsymbolic. + + Info [UMFPACK_QFIXED]: 1 if the column pre-ordering will be refined + during numerical factorization, 0 if not. + + Info [UMFPACK_DIAG_PREFERED]: 1 if diagonal pivoting will be attempted, + 0 if not. + + Info [UMFPACK_COL_SINGLETONS]: the matrix A is analyzed by first + eliminating all pivots with zero Markowitz cost. This count is the + number of these pivots with exactly one nonzero in their pivot + column. + + Info [UMFPACK_ROW_SINGLETONS]: the number of zero-Markowitz-cost + pivots with exactly one nonzero in their pivot row. + + Info [UMFPACK_PATTERN_SYMMETRY]: the symmetry of the pattern of S. + + Info [UMFPACK_NZ_A_PLUS_AT]: the number of off-diagonal entries in S+S'. + + Info [UMFPACK_NZDIAG]: the number of entries on the diagonal of S. + + Info [UMFPACK_N2]: if S is square, and nempty_row = nempty_col, this + is equal to n_row - n1 - nempty_row. + + Info [UMFPACK_S_SYMMETRIC]: 1 if S is square and its diagonal has been + preserved, 0 otherwise. + + + Info [UMFPACK_MAX_FRONT_NROWS_ESTIMATE]: estimate of the max number of + rows in any frontal matrix, for arbitrary partial pivoting. + + Info [UMFPACK_MAX_FRONT_NCOLS_ESTIMATE]: estimate of the max number of + columns in any frontal matrix, for arbitrary partial pivoting. + + ------------------------------------------------------------------------ + The next four statistics are computed only if AMD is used: + ------------------------------------------------------------------------ + + Info [UMFPACK_SYMMETRIC_LUNZ]: The number of nonzeros in L and U, + assuming no pivoting during numerical factorization, and assuming a + zero-free diagonal of U. Excludes the entries on the diagonal of + L. If the matrix has a purely symmetric nonzero pattern, this is + often a lower bound on the nonzeros in the actual L and U computed + in the numerical factorization, for matrices that fit the criteria + for the "symmetric" strategy. + + Info [UMFPACK_SYMMETRIC_FLOPS]: The floating-point operation count in + the numerical factorization phase, assuming no pivoting. If the + pattern of the matrix is symmetric, this is normally a lower bound + on the floating-point operation count in the actual numerical + factorization, for matrices that fit the criteria for the symmetric + or 2-by-2 strategies + + Info [UMFPACK_SYMMETRIC_NDENSE]: The number of "dense" rows/columns of + S+S' that were ignored during the AMD ordering. These are placed + last in the output order. If > 0, then the + Info [UMFPACK_SYMMETRIC_*] statistics, above are rough upper bounds. + + Info [UMFPACK_SYMMETRIC_DMAX]: The maximum number of nonzeros in any + column of L, if no pivoting is performed during numerical + factorization. Excludes the part of the LU factorization for + pivots with zero Markowitz cost. + + ------------------------------------------------------------------------ + The following statistics are computed only if the 2-by-2 strategy is + used or attempted: + ------------------------------------------------------------------------ + + Info [UMFPACK_2BY2_NWEAK]: the number of small diagonal entries in S. + + Info [UMFPACK_2BY2_UNMATCHED]: the number of small diagonal entries + in P2*S. + + Info [UMFPACK_2BY2_PATTERN_SYMMETRY]: the symmetry of P2*S. + + Info [UMFPACK_2BY2_NZ_PA_PLUS_AT]: the number of off-diagonal entries + in (P2*S)+(P2*S)'. + + Info [UMFPACK_2BY2_NZDIAG]: the number of nonzero entries on the + diagonal of P2*S. + + + At the start of umfpack_*_symbolic, all of Info is set of -1, and then + after that only the above listed Info [...] entries are accessed. + Future versions might modify different parts of Info. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_tictoc.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_tictoc.h new file mode 100644 index 0000000000..550d7d5d5d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_tictoc.h @@ -0,0 +1,61 @@ +/* ========================================================================== */ +/* === umfpack_tictoc ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +void umfpack_tic (double stats [2]) ; + +void umfpack_toc (double stats [2]) ; + + +/* +Syntax (for all versions: di, dl, zi, and zl): + + #include "umfpack.h" + double stats [2] ; + umfpack_tic (stats) ; + ... + umfpack_toc (stats) ; + +Purpose: + + umfpack_tic returns the CPU time and wall clock time used by the process. + The CPU time includes both "user" and "system" time (the latter is time + spent by the system on behalf of the process, and is thus charged to the + process). umfpack_toc returns the CPU time and wall clock time since the + last call to umfpack_tic with the same stats array. + + Typical usage: + + umfpack_tic (stats) ; + ... do some work ... + umfpack_toc (stats) ; + + then stats [1] contains the time in seconds used by the code between + umfpack_tic and umfpack_toc, and stats [0] contains the wall clock time + elapsed between the umfpack_tic and umfpack_toc. These two routines act + just like tic and toc in MATLAB, except that the both process time and + wall clock time are returned. + + This routine normally uses the sysconf and times routines in the POSIX + standard. If -DNPOSIX is defined at compile time, then the ANSI C clock + routine is used instead, and only the CPU time is returned (stats [0] + is set to zero). + + umfpack_tic and umfpack_toc are the routines used internally in UMFPACK + to time the symbolic analysis, numerical factorization, and the forward/ + backward solve. + +Arguments: + + double stats [2]: + + stats [0]: wall clock time, in seconds + stats [1]: CPU time, in seconds +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_timer.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_timer.h new file mode 100644 index 0000000000..a29c2f4dac --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_timer.h @@ -0,0 +1,41 @@ +/* ========================================================================== */ +/* === umfpack_timer ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +double umfpack_timer ( void ) ; + +/* +Syntax (for all versions: di, dl, zi, and zl): + + #include "umfpack.h" + double t ; + t = umfpack_timer ( ) ; + +Purpose: + + Returns the CPU time used by the process. Includes both "user" and "system" + time (the latter is time spent by the system on behalf of the process, and + is thus charged to the process). It does not return the wall clock time. + This was the timer used internally in UMFPACK V4.0. See umfpack_tic and + umfpack_toc (the file umfpack_tictoc.h) for the timer used internally by + UMFPACK V4.1. + + This routine uses the Unix getrusage routine, if available. It is less + subject to overflow than the ANSI C clock routine. If getrusage is not + available, the portable ANSI C clock routine is used instead. + Unfortunately, clock ( ) overflows if the CPU time exceeds 2147 seconds + (about 36 minutes) when sizeof (clock_t) is 4 bytes. If you have getrusage, + be sure to compile UMFPACK with the -DGETRUSAGE flag set; see umf_config.h + and the User Guide for details. Even the getrusage routine can overlow. + +Arguments: + + None. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_transpose.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_transpose.h new file mode 100644 index 0000000000..f9d34cbb29 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_transpose.h @@ -0,0 +1,217 @@ +/* ========================================================================== */ +/* === umfpack_transpose ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_transpose +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + const int P [ ], + const int Q [ ], + int Rp [ ], + int Ri [ ], + double Rx [ ] +) ; + +long umfpack_dl_transpose +( + long n_row, + long n_col, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], + const long P [ ], + const long Q [ ], + long Rp [ ], + long Ri [ ], + double Rx [ ] +) ; + +int umfpack_zi_transpose +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], const double Az [ ], + const int P [ ], + const int Q [ ], + int Rp [ ], + int Ri [ ], + double Rx [ ], double Rz [ ], + int do_conjugate +) ; + +long umfpack_zl_transpose +( + long n_row, + long n_col, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], const double Az [ ], + const long P [ ], + const long Q [ ], + long Rp [ ], + long Ri [ ], + double Rx [ ], double Rz [ ], + long do_conjugate +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int n_row, n_col, status, *Ap, *Ai, *P, *Q, *Rp, *Ri ; + double *Ax, *Rx ; + status = umfpack_di_transpose (n_row, n_col, Ap, Ai, Ax, P, Q, Rp, Ri, Rx) ; + +double long Syntax: + + #include "umfpack.h" + long n_row, n_col, status, *Ap, *Ai, *P, *Q, *Rp, *Ri ; + double *Ax, *Rx ; + status = umfpack_dl_transpose (n_row, n_col, Ap, Ai, Ax, P, Q, Rp, Ri, Rx) ; + +complex int Syntax: + + #include "umfpack.h" + int n_row, n_col, status, *Ap, *Ai, *P, *Q, *Rp, *Ri, do_conjugate ; + double *Ax, *Az, *Rx, *Rz ; + status = umfpack_zi_transpose (n_row, n_col, Ap, Ai, Ax, Az, P, Q, + Rp, Ri, Rx, Rz, do_conjugate) ; + +complex long Syntax: + + #include "umfpack.h" + long n_row, n_col, status, *Ap, *Ai, *P, *Q, *Rp, *Ri, do_conjugate ; + double *Ax, *Az, *Rx, *Rz ; + status = umfpack_zl_transpose (n_row, n_col, Ap, Ai, Ax, Az, P, Q, + Rp, Ri, Rx, Rz, do_conjugate) ; + +Purpose: + + Transposes and optionally permutes a sparse matrix in row or column-form, + R = (PAQ)'. In MATLAB notation, R = (A (P,Q))' or R = (A (P,Q)).' doing + either the linear algebraic transpose or the array transpose. Alternatively, + this routine can be viewed as converting A (P,Q) from column-form to + row-form, or visa versa (for the array transpose). Empty rows and columns + may exist. The matrix A may be singular and/or rectangular. + + umfpack_*_transpose is useful if you want to factorize A' or A.' instead of + A. Factorizing A' or A.' instead of A can be much better, particularly if + AA' is much sparser than A'A. You can still solve Ax=b if you factorize + A' or A.', by solving with the sys argument UMFPACK_At or UMFPACK_Aat, + respectively, in umfpack_*_*solve. The umfpack mexFunction (umfpackmex.c) + is one example. To compute x = A/b, it computes x = (A.'\b.').' instead, + by factorizing A.'. It then uses the regular solve, since b.' and x.' are + stored identically as b and x, respectively (both b.' and b are dense + vectors). If b and x were arrays, the umfpack mexFunction would need to + first compute b.' and then transpose the resulting solution. + +Returns: + + UMFPACK_OK if successful. + UMFPACK_ERROR_out_of_memory if umfpack_*_transpose fails to allocate a + size-max (n_row,n_col) workspace. + UMFPACK_ERROR_argument_missing if Ai, Ap, Ri, and/or Rp are missing. + UMFPACK_ERROR_n_nonpositive if n_row <= 0 or n_col <= 0 + UMFPACK_ERROR_invalid_permutation if P and/or Q are invalid. + UMFPACK_ERROR_invalid_matrix if Ap [n_col] < 0, if Ap [0] != 0, + if Ap [j] > Ap [j+1] for any j in the range 0 to n_col-1, + if any row index i is < 0 or >= n_row, or if the row indices + in any column are not in ascending order. + +Arguments: + + Int n_row ; Input argument, not modified. + Int n_col ; Input argument, not modified. + + A is an n_row-by-n_col matrix. Restriction: n_row > 0 and n_col > 0. + + Int Ap [n_col+1] ; Input argument, not modified. + + The column pointers of the column-oriented form of the matrix A. See + umfpack_*_symbolic for a description. The number of entries in + the matrix is nz = Ap [n_col]. Ap [0] must be zero, Ap [n_col] must be + => 0, and Ap [j] <= Ap [j+1] and Ap [j] <= Ap [n_col] must be true for + all j in the range 0 to n_col-1. Empty columns are OK (that is, Ap [j] + may equal Ap [j+1] for any j in the range 0 to n_col-1). + + Int Ai [nz] ; Input argument, not modified, of size nz = Ap [n_col]. + + The nonzero pattern (row indices) for column j is stored in + Ai [(Ap [j]) ... (Ap [j+1]-1)]. The row indices in a given column j + must be in ascending order, and no duplicate row indices may be present. + Row indices must be in the range 0 to n_row-1 (the matrix is 0-based). + + double Ax [nz] ; Input argument, not modified, of size nz = Ap [n_col]. + double Az [nz] ; Input argument, not modified, for complex versions. + + If present, these are the numerical values of the sparse matrix A. + The nonzero pattern (row indices) for column j is stored in + Ai [(Ap [j]) ... (Ap [j+1]-1)], and the corresponding real numerical + values are stored in Ax [(Ap [j]) ... (Ap [j+1]-1)]. The imaginary + values are stored in Az [(Ap [j]) ... (Ap [j+1]-1)]. The values are + transposed only if Ax and Rx are present (for the real version), and + only if all four (Ax, Az, Rx, and Rz) are present for the complex + version. These are not an error conditions; you are able to transpose + and permute just the pattern of a matrix. + + Future complex version: if Ax is present and Az is NULL, then both real + and imaginary parts will be contained in Ax[0..2*nz-1], with Ax[2*k] + and Ax[2*k+1] being the real and imaginary part of the kth entry. + + Int P [n_row] ; Input argument, not modified. + + The permutation vector P is defined as P [k] = i, where the original + row i of A is the kth row of PAQ. If you want to use the identity + permutation for P, simply pass (Int *) NULL for P. This is not an error + condition. P is a complete permutation of all the rows of A; this + routine does not support the creation of a transposed submatrix of A + (R = A (1:3,:)' where A has more than 3 rows, for example, cannot be + done; a future version might support this operation). + + Int Q [n_col] ; Input argument, not modified. + + The permutation vector Q is defined as Q [k] = j, where the original + column j of A is the kth column of PAQ. If you want to use the identity + permutation for Q, simply pass (Int *) NULL for Q. This is not an error + condition. Q is a complete permutation of all the columns of A; this + routine does not support the creation of a transposed submatrix of A. + + Int Rp [n_row+1] ; Output argument. + + The column pointers of the matrix R = (A (P,Q))' or (A (P,Q)).', in the + same form as the column pointers Ap for the matrix A. + + Int Ri [nz] ; Output argument. + + The row indices of the matrix R = (A (P,Q))' or (A (P,Q)).' , in the + same form as the row indices Ai for the matrix A. + + double Rx [nz] ; Output argument. + double Rz [nz] ; Output argument, imaginary part for complex versions. + + If present, these are the numerical values of the sparse matrix R, + in the same form as the values Ax and Az of the matrix A. + + Future complex version: if Rx is present and Rz is NULL, then both real + and imaginary parts will be contained in Rx[0..2*nz-1], with Rx[2*k] + and Rx[2*k+1] being the real and imaginary part of the kth entry. + + Int do_conjugate ; Input argument for complex versions only. + + If true, and if Ax, Az, Rx, and Rz are all present, then the linear + algebraic transpose is computed (complex conjugate). If false, the + array transpose is computed instead. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_triplet_to_col.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_triplet_to_col.h new file mode 100644 index 0000000000..ea55157117 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_triplet_to_col.h @@ -0,0 +1,261 @@ +/* ========================================================================== */ +/* === umfpack_triplet_to_col =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_triplet_to_col +( + int n_row, + int n_col, + int nz, + const int Ti [ ], + const int Tj [ ], + const double Tx [ ], + int Ap [ ], + int Ai [ ], + double Ax [ ], + int Map [ ] +) ; + +long umfpack_dl_triplet_to_col +( + long n_row, + long n_col, + long nz, + const long Ti [ ], + const long Tj [ ], + const double Tx [ ], + long Ap [ ], + long Ai [ ], + double Ax [ ], + long Map [ ] +) ; + +int umfpack_zi_triplet_to_col +( + int n_row, + int n_col, + int nz, + const int Ti [ ], + const int Tj [ ], + const double Tx [ ], const double Tz [ ], + int Ap [ ], + int Ai [ ], + double Ax [ ], double Az [ ], + int Map [ ] +) ; + +long umfpack_zl_triplet_to_col +( + long n_row, + long n_col, + long nz, + const long Ti [ ], + const long Tj [ ], + const double Tx [ ], const double Tz [ ], + long Ap [ ], + long Ai [ ], + double Ax [ ], double Az [ ], + long Map [ ] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + int n_row, n_col, nz, *Ti, *Tj, *Ap, *Ai, status, *Map ; + double *Tx, *Ax ; + status = umfpack_di_triplet_to_col (n_row, n_col, nz, Ti, Tj, Tx, + Ap, Ai, Ax, Map) ; + +double long Syntax: + + #include "umfpack.h" + long n_row, n_col, nz, *Ti, *Tj, *Ap, *Ai, status, *Map ; + double *Tx, *Ax ; + status = umfpack_dl_triplet_to_col (n_row, n_col, nz, Ti, Tj, Tx, + Ap, Ai, Ax, Map) ; + +complex int Syntax: + + #include "umfpack.h" + int n_row, n_col, nz, *Ti, *Tj, *Ap, *Ai, status, *Map ; + double *Tx, *Tz, *Ax, *Az ; + status = umfpack_zi_triplet_to_col (n_row, n_col, nz, Ti, Tj, Tx, Tz, + Ap, Ai, Ax, Az, Map) ; + +long Syntax: + + #include "umfpack.h" + long n_row, n_col, nz, *Ti, *Tj, *Ap, *Ai, status, *Map ; + double *Tx, *Tz, *Ax, *Az ; + status = umfpack_zl_triplet_to_col (n_row, n_col, nz, Ti, Tj, Tx, Tz, + Ap, Ai, Ax, Az, Map) ; + +Purpose: + + Converts a sparse matrix from "triplet" form to compressed-column form. + Analogous to A = spconvert (Ti, Tj, Tx + Tx*1i) in MATLAB, except that + zero entries present in the triplet form are present in A. + + The triplet form of a matrix is a very simple data structure for basic + sparse matrix operations. For example, suppose you wish to factorize a + matrix A coming from a finite element method, in which A is a sum of + dense submatrices, A = E1 + E2 + E3 + ... . The entries in each element + matrix Ei can be concatenated together in the three triplet arrays, and + any overlap between the elements will be correctly summed by + umfpack_*_triplet_to_col. + + Transposing a matrix in triplet form is simple; just interchange the + use of Ti and Tj. You can construct the complex conjugate transpose by + negating Tz, for the complex versions. + + Permuting a matrix in triplet form is also simple. If you want the matrix + PAQ, or A (P,Q) in MATLAB notation, where P [k] = i means that row i of + A is the kth row of PAQ and Q [k] = j means that column j of A is the kth + column of PAQ, then do the following. First, create inverse permutations + Pinv and Qinv such that Pinv [i] = k if P [k] = i and Qinv [j] = k if + Q [k] = j. Next, for the mth triplet (Ti [m], Tj [m], Tx [m], Tz [m]), + replace Ti [m] with Pinv [Ti [m]] and replace Tj [m] with Qinv [Tj [m]]. + + If you have a column-form matrix with duplicate entries or unsorted + columns, you can sort it and sum up the duplicates by first converting it + to triplet form with umfpack_*_col_to_triplet, and then converting it back + with umfpack_*_triplet_to_col. + + Constructing a submatrix is also easy. Just scan the triplets and remove + those entries outside the desired subset of 0...n_row-1 and 0...n_col-1, + and renumber the indices according to their position in the subset. + + You can do all these operations on a column-form matrix by first + converting it to triplet form with umfpack_*_col_to_triplet, doing the + operation on the triplet form, and then converting it back with + umfpack_*_triplet_to_col. + + The only operation not supported easily in the triplet form is the + multiplication of two sparse matrices (UMFPACK does not provide this + operation). + + You can print the input triplet form with umfpack_*_report_triplet, and + the output matrix with umfpack_*_report_matrix. + + The matrix may be singular (nz can be zero, and empty rows and/or columns + may exist). It may also be rectangular and/or complex. + +Returns: + + UMFPACK_OK if successful. + UMFPACK_ERROR_argument_missing if Ap, Ai, Ti, and/or Tj are missing. + UMFPACK_ERROR_n_nonpositive if n_row <= 0 or n_col <= 0. + UMFPACK_ERROR_invalid_matrix if nz < 0, or if for any k, Ti [k] and/or + Tj [k] are not in the range 0 to n_row-1 or 0 to n_col-1, respectively. + UMFPACK_ERROR_out_of_memory if unable to allocate sufficient workspace. + +Arguments: + + Int n_row ; Input argument, not modified. + Int n_col ; Input argument, not modified. + + A is an n_row-by-n_col matrix. Restriction: n_row > 0 and n_col > 0. + All row and column indices in the triplet form must be in the range + 0 to n_row-1 and 0 to n_col-1, respectively. + + Int nz ; Input argument, not modified. + + The number of entries in the triplet form of the matrix. Restriction: + nz >= 0. + + Int Ti [nz] ; Input argument, not modified. + Int Tj [nz] ; Input argument, not modified. + double Tx [nz] ; Input argument, not modified. + double Tz [nz] ; Input argument, not modified, for complex versions. + + Ti, Tj, Tx, and Tz hold the "triplet" form of a sparse matrix. The kth + nonzero entry is in row i = Ti [k], column j = Tj [k], and the real part + of a_ij is Tx [k]. The imaginary part of a_ij is Tz [k], for complex + versions. The row and column indices i and j must be in the range 0 to + n_row-1 and 0 to n_col-1, respectively. Duplicate entries may be + present; they are summed in the output matrix. This is not an error + condition. The "triplets" may be in any order. Tx, Tz, Ax, and Az + are optional. For the real version, Ax is computed only if both Ax + and Tx are present (not (double *) NULL). For the complex version, Ax + and Az are computed only if Tx, Tz, Ax, and Az are all present. These + are not error conditions; the routine can create just the pattern of + the output matrix from the pattern of the triplets. + + Future complex version: if Tx is present and Tz is NULL, then both real + and imaginary parts will be contained in Tx[0..2*nz-1], with Tx[2*k] + and Tx[2*k+1] being the real and imaginary part of the kth entry. + + Int Ap [n_col+1] ; Output argument. + + Ap is an integer array of size n_col+1 on input. On output, Ap holds + the "pointers" for the column form of the sparse matrix A. Column j of + the matrix A is held in Ai [(Ap [j]) ... (Ap [j+1]-1)]. The first + entry, Ap [0], is zero, and Ap [j] <= Ap [j+1] holds for all j in the + range 0 to n_col-1. The value nz2 = Ap [n_col] is thus the total + number of entries in the pattern of the matrix A. Equivalently, the + number of duplicate triplets is nz - Ap [n_col]. + + Int Ai [nz] ; Output argument. + + Ai is an integer array of size nz on input. Note that only the first + Ap [n_col] entries are used. + + The nonzero pattern (row indices) for column j is stored in + Ai [(Ap [j]) ... (Ap [j+1]-1)]. The row indices in a given column j + are in ascending order, and no duplicate row indices are present. + Row indices are in the range 0 to n_col-1 (the matrix is 0-based). + + double Ax [nz] ; Output argument. + double Az [nz] ; Output argument for complex versions. + + Ax and Az (for the complex versions) are double arrays of size nz on + input. Note that only the first Ap [n_col] entries are used + in both arrays. + + Ax is optional; if Tx and/or Ax are not present (a (double *) NULL + pointer), then Ax is not computed. Az is also optional; if Tz and/or + Az are not present, then Az is not computed. If present, Ax holds the + numerical values of the the real part of the sparse matrix A and Az + holds the imaginary parts. The nonzero pattern (row indices) for + column j is stored in Ai [(Ap [j]) ... (Ap [j+1]-1)], and the + corresponding numerical values are stored in + Ax [(Ap [j]) ... (Ap [j+1]-1)]. The imaginary parts are stored in + Az [(Ap [j]) ... (Ap [j+1]-1)], for the complex versions. + + Future complex version: if Ax is present and Az is NULL, then both real + and imaginary parts will be returned in Ax[0..2*nz2-1], with Ax[2*k] + and Ax[2*k+1] being the real and imaginary part of the kth entry. + + int Map [nz] ; Optional output argument. + + If Map is present (a non-NULL pointer to an Int array of size nz), then + on output it holds the position of the triplets in the column-form + matrix. That is, suppose p = Map [k], and the k-th triplet is i=Ti[k], + j=Tj[k], and aij=Tx[k]. Then i=Ai[p], and aij will have been summed + into Ax[p] (or simply aij=Ax[p] if there were no duplicate entries also + in row i and column j). Also, Ap[j] <= p < Ap[j+1]. The Map array is + not computed if it is (Int *) NULL. The Map array is useful for + converting a subsequent triplet form matrix with the same pattern as the + first one, without calling this routine. If Ti and Tj do not change, + then Ap, and Ai can be reused from the prior call to + umfpack_*_triplet_to_col. You only need to recompute Ax (and Az for the + complex version). This code excerpt properly sums up all duplicate + values (for the real version): + + for (p = 0 ; p < Ap [n_col] ; p++) Ax [p] = 0 ; + for (k = 0 ; k < nz ; k++) Ax [Map [k]] += Tx [k] ; + + This feature is useful (along with the reuse of the Symbolic object) if + you need to factorize a sequence of triplet matrices with identical + nonzero pattern (the order of the triplets in the Ti,Tj,Tx arrays must + also remain unchanged). It is faster than calling this routine for + each matrix, and requires no workspace. +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_wsolve.h b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_wsolve.h new file mode 100644 index 0000000000..7eda906edc --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Include/umfpack_wsolve.h @@ -0,0 +1,166 @@ +/* ========================================================================== */ +/* === umfpack_wsolve ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_wsolve +( + int sys, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + double X [ ], + const double B [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO], + int Wi [ ], + double W [ ] +) ; + +long umfpack_dl_wsolve +( + long sys, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], + double X [ ], + const double B [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO], + long Wi [ ], + double W [ ] +) ; + +int umfpack_zi_wsolve +( + int sys, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], const double Az [ ], + double Xx [ ], double Xz [ ], + const double Bx [ ], const double Bz [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO], + int Wi [ ], + double W [ ] +) ; + +long umfpack_zl_wsolve +( + long sys, + const long Ap [ ], + const long Ai [ ], + const double Ax [ ], const double Az [ ], + double Xx [ ], double Xz [ ], + const double Bx [ ], const double Bz [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO], + long Wi [ ], + double W [ ] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status, *Ap, *Ai, *Wi, sys ; + double *B, *X, *Ax, *W, Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ; + status = umfpack_di_wsolve (sys, Ap, Ai, Ax, X, B, Numeric, + Control, Info, Wi, W) ; + +double long Syntax: + + #include "umfpack.h" + void *Numeric ; + long status, *Ap, *Ai, *Wi, sys ; + double *B, *X, *Ax, *W, Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ; + status = umfpack_dl_wsolve (sys, Ap, Ai, Ax, X, B, Numeric, + Control, Info, Wi, W) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status, *Ap, *Ai, *Wi, sys ; + double *Bx, *Bz, *Xx, *Xz, *Ax, *Az, *W, + Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ; + status = umfpack_zi_wsolve (sys, Ap, Ai, Ax, Az, Xx, Xz, Bx, Bz, Numeric, + Control, Info, Wi, W) ; + +complex long Syntax: + + #include "umfpack.h" + void *Numeric ; + long status, *Ap, *Ai, *Wi, sys ; + double *Bx, *Bz, *Xx, *Xz, *Ax, *Az, *W, + Info [UMFPACK_INFO], Control [UMFPACK_CONTROL] ; + status = umfpack_zl_wsolve (sys, Ap, Ai, Ax, Az, Xx, Xz, Bx, Bz, Numeric, + Control, Info, Wi, W) ; + +Purpose: + + Given LU factors computed by umfpack_*_numeric (PAQ=LU) and the + right-hand-side, B, solve a linear system for the solution X. Iterative + refinement is optionally performed. This routine is identical to + umfpack_*_solve, except that it does not dynamically allocate any workspace. + When you have many linear systems to solve, this routine is faster than + umfpack_*_solve, since the workspace (Wi, W) needs to be allocated only + once, prior to calling umfpack_*_wsolve. + +Returns: + + The status code is returned. See Info [UMFPACK_STATUS], below. + +Arguments: + + Int sys ; Input argument, not modified. + Int Ap [n+1] ; Input argument, not modified. + Int Ai [nz] ; Input argument, not modified. + double Ax [nz] ; Input argument, not modified. + double X [n] ; Output argument. + double B [n] ; Input argument, not modified. + void *Numeric ; Input argument, not modified. + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + double Info [UMFPACK_INFO] ; Output argument. + + for complex versions: + double Az [nz] ; Input argument, not modified, imaginary part + double Xx [n] ; Output argument, real part. + double Xz [n] ; Output argument, imaginary part + double Bx [n] ; Input argument, not modified, real part + double Bz [n] ; Input argument, not modified, imaginary part + + The above arguments are identical to umfpack_*_solve, except that the + error code UMFPACK_ERROR_out_of_memory will not be returned in + Info [UMFPACK_STATUS], since umfpack_*_wsolve does not allocate any + memory. + + Int Wi [n] ; Workspace. + double W [c*n] ; Workspace, where c is defined below. + + The Wi and W arguments are workspace used by umfpack_*_wsolve. They + need not be initialized on input, and their contents are undefined on + output. The size of W depends on whether or not iterative refinement is + used, and which version (real or complex) is called. Iterative + refinement is performed if Ax=b, A'x=b, or A.'x=b is being solved, + Control [UMFPACK_IRSTEP] > 0, and A is nonsingular. The size of W is + given below: + + no iter. with iter. + refinement refinement + umfpack_di_wsolve n 5*n + umfpack_dl_wsolve n 5*n + umfpack_zi_wsolve 4*n 10*n + umfpack_zl_wsolve 4*n 10*n +*/ diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/Contents.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/Contents.m new file mode 100644 index 0000000000..c6657b2375 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/Contents.m @@ -0,0 +1,22 @@ +%Contents of the UMFPACK sparse matrix toolbox: +% +% umfpack computes x=A\b, x=A/b, or lu (A) for a sparse matrix A +% umfpack_make to compile umfpack for use in MATLAB +% umfpack_details details on all the options for using umfpack in MATLAB +% umfpack_report prints optional control settings and statistics +% umfpack_demo a long demo +% umfpack_simple a simple demo +% umfpack_btf factorize A using a block triangular form +% umfpack_solve x = A\b or x = b/A +% lu_normest estimates norm (L*U-A, 1) without forming L*U-A +% luflop given L and U, computes # of flops required to compute them +% +% See also: +% amd symmetric minimum degree ordering +% colamd unsymmetric column approx minimum degree ordering +% symamd symmetric approx minimum degree ordering, based on colamd +% +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. Type umfpack_details for License. + +help Contents diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/GNUmakefile b/deal.II/contrib/umfpack/UMFPACK/MATLAB/GNUmakefile new file mode 100644 index 0000000000..90edfec8dd --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/GNUmakefile @@ -0,0 +1,232 @@ +#------------------------------------------------------------------------------- +# UMFPACK GNUmakefile for the UMFPACK MATLAB mexFunction (GNU "make" only) +#------------------------------------------------------------------------------- + +# UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +# Davis. All Rights Reserved. See ../README for License. + +all: umfpack luflop + +include ../Make/Make.include + +MX = $(MEX) -I../Include -I../Source -I../../AMD/Include -I../../AMD/Source + +#------------------------------------------------------------------------------- +# source files +#------------------------------------------------------------------------------- + +# non-user-callable umf_*.[ch] files: +UMFCH = umf_assemble umf_blas3_update \ + umf_build_tuples umf_create_element \ + umf_dump umf_extend_front umf_garbage_collection \ + umf_get_memory umf_init_front umf_kernel \ + umf_kernel_init umf_kernel_wrapup \ + umf_local_search umf_lsolve umf_ltsolve \ + umf_mem_alloc_element umf_mem_alloc_head_block \ + umf_mem_alloc_tail_block umf_mem_free_tail_block \ + umf_mem_init_memoryspace \ + umf_report_vector umf_row_search umf_scale_column \ + umf_set_stats umf_solve umf_symbolic_usage umf_transpose \ + umf_tuple_lengths umf_usolve umf_utsolve umf_valid_numeric \ + umf_valid_symbolic umf_grow_front umf_start_front umf_2by2 \ + umf_store_lu umf_scale + +# non-user-callable umf_*.[ch] files, int/long versions only (no real/complex): +UMFINT = umf_analyze umf_apply_order umf_colamd umf_free umf_fsize \ + umf_is_permutation umf_malloc umf_realloc umf_report_perm \ + umf_singletons + +# non-user-callable and user-callable amd_*.[ch] files (int/long versions only): +AMD = amd_aat amd_1 amd_2 amd_dump amd_postorder amd_post_tree amd_defaults \ + amd_order amd_control amd_info amd_valid + +# non-user-callable, created from umf_ltsolve.c, umf_utsolve.c, +# umf_triplet.c, and umf_assemble.c , with int/long and real/complex versions: +UMF_CREATED = umf_lhsolve umf_uhsolve umf_triplet_map_nox \ + umf_triplet_nomap_x umf_triplet_nomap_nox umf_triplet_map_x \ + umf_assemble_fixq umf_store_lu_drop + +# non-user-callable, int/long and real/complex versions: +UMF = $(UMF_CREATED) $(UMFCH) + +# user-callable umfpack_*.[ch] files (int/long and real/complex): +UMFPACK = umfpack_col_to_triplet umfpack_defaults umfpack_free_numeric \ + umfpack_free_symbolic umfpack_get_numeric umfpack_get_lunz \ + umfpack_get_symbolic umfpack_numeric umfpack_qsymbolic \ + umfpack_report_control umfpack_report_info umfpack_report_matrix \ + umfpack_report_numeric umfpack_report_perm umfpack_report_status \ + umfpack_report_symbolic umfpack_report_triplet \ + umfpack_report_vector umfpack_solve umfpack_symbolic \ + umfpack_transpose umfpack_triplet_to_col umfpack_scale \ + umfpack_load_numeric umfpack_save_numeric \ + umfpack_load_symbolic umfpack_save_symbolic + +# user-callable, created from umfpack_solve.c (umfpack_wsolve.h exists, though): +# with int/long and real/complex versions: +UMFPACKW = umfpack_wsolve + +USER = $(UMFPACKW) $(UMFPACK) + +# user-callable, only one version for int/long, real/complex, *.[ch] files: +GENERIC = umfpack_timer umfpack_tictoc + +#------------------------------------------------------------------------------- +# include files: +#------------------------------------------------------------------------------- + +AMDH = ../../AMD/Source/amd_internal.h ../../AMD/Include/amd.h + +INC1 = umf_config.h umf_version.h umf_internal.h umf_triplet.h + +INC = ../Include/umfpack.h \ + $(addprefix ../Source/, $(INC1)) \ + $(addprefix ../Source/, $(addsuffix .h,$(UMFCH))) \ + $(addprefix ../Source/, $(addsuffix .h,$(UMFINT))) \ + $(addprefix ../Include/, $(addsuffix .h,$(USER))) \ + $(addprefix ../Include/, $(addsuffix .h,$(GENERIC))) \ + $(AMDH) + +#------------------------------------------------------------------------------- +# Create the umfpack and amd mexFunctions for MATLAB (int versions only) +#------------------------------------------------------------------------------- + +MEXI = $(addsuffix .o, $(subst umf_,umf_m_,$(UMFINT))) +MEXDI = $(addsuffix .o, $(subst umf_,umf_md_,$(UMF)) $(subst umfpack_,umfpack_md_,$(USER))) +MEXZI = $(addsuffix .o, $(subst umf_,umf_mz_,$(UMF)) $(subst umfpack_,umfpack_mz_,$(USER)) ) +MEXAMD = $(addsuffix .o, $(subst amd_,amd_m_,$(AMD))) +MEXGN = $(addsuffix .o, $(subst umfpack_,umfpack_m_,$(GENERIC))) + +MEXUMFPACK = $(MEXI) $(MEXDI) $(MEXZI) $(MEXGN) + +# Note that mex has no "-o" option, thus the need for $(MV) commands. +# If it did, then the rules would be much simpler: +# $(MX) -DDINT -c $< -o $@ + +#---------------------------------------- +# integer-only routines (no real/complex): +#---------------------------------------- + +amd_m_%.o: ../../AMD/Source/amd_%.c $(AMDH) + $(MX) -DDINT -c $< + - $(MV) amd_$*.o $@ + +umf_m_%.o: ../Source/umf_%.c $(INC) + $(MX) -DDINT -c $< + - $(MV) umf_$*.o $@ + +#---------------------------------------- +# Double precision, int version, for MATLAB +#---------------------------------------- + +umf_md_%.o: ../Source/umf_%.c $(INC) + $(MX) -DDINT -c $< + - $(MV) umf_$*.o $@ + +umf_md_%hsolve.o: ../Source/umf_%tsolve.c $(INC) + $(MX) -DDINT -DCONJUGATE_SOLVE -c $< + - $(MV) umf_$*tsolve.o $@ + +umf_md_triplet_map_x.o: ../Source/umf_triplet.c $(INC) + $(MX) -DDINT -DDO_MAP -DDO_VALUES -c $< + - $(MV) umf_triplet.o $@ + +umf_md_triplet_map_nox.o: ../Source/umf_triplet.c $(INC) + $(MX) -DDINT -DDO_MAP -c $< + - $(MV) umf_triplet.o $@ + +umf_md_triplet_nomap_x.o: ../Source/umf_triplet.c $(INC) + $(MX) -DDINT -DDO_VALUES -c $< + - $(MV) umf_triplet.o $@ + +umf_md_triplet_nomap_nox.o: ../Source/umf_triplet.c $(INC) + $(MX) -DDINT -c $< + - $(MV) umf_triplet.o $@ + +umf_md_assemble_fixq.o: ../Source/umf_assemble.c $(INC) + $(MX) -DDINT -DFIXQ -c $< + - $(MV) umf_assemble.o $@ + +umf_md_store_lu_drop.o: ../Source/umf_store_lu.c $(INC) + $(MX) -DDINT -DDROP -c $< + - $(MV) umf_store_lu.o $@ + +umfpack_md_wsolve.o: ../Source/umfpack_solve.c $(INC) + $(MX) -DDINT -DWSOLVE -c $< + - $(MV) umfpack_solve.o $@ + +umfpack_md_%.o: ../Source/umfpack_%.c $(INC) + $(MX) -DDINT -c $< + - $(MV) umfpack_$*.o $@ + +#---------------------------------------- +# Complex double precision, int version, for MATLAB +#---------------------------------------- + +umf_mz_%.o: ../Source/umf_%.c $(INC) + $(MX) -DZINT -c $< + - $(MV) umf_$*.o $@ + +umf_mz_%hsolve.o: ../Source/umf_%tsolve.c $(INC) + $(MX) -DZINT -DCONJUGATE_SOLVE -c $< + - $(MV) umf_$*tsolve.o $@ + +umf_mz_triplet_map_x.o: ../Source/umf_triplet.c $(INC) + $(MX) -DZINT -DDO_MAP -DDO_VALUES -c $< + - $(MV) umf_triplet.o $@ + +umf_mz_triplet_map_nox.o: ../Source/umf_triplet.c $(INC) + $(MX) -DZINT -DDO_MAP -c $< + - $(MV) umf_triplet.o $@ + +umf_mz_triplet_nomap_x.o: ../Source/umf_triplet.c $(INC) + $(MX) -DZINT -DDO_VALUES -c $< + - $(MV) umf_triplet.o $@ + +umf_mz_triplet_nomap_nox.o: ../Source/umf_triplet.c $(INC) + $(MX) -DZINT -c $< + - $(MV) umf_triplet.o $@ + +umf_mz_assemble_fixq.o: ../Source/umf_assemble.c $(INC) + $(MX) -DZINT -DFIXQ -c $< + - $(MV) umf_assemble.o $@ + +umf_mz_store_lu_drop.o: ../Source/umf_store_lu.c $(INC) + $(MX) -DZINT -DDROP -c $< + - $(MV) umf_store_lu.o $@ + +umfpack_mz_wsolve.o: ../Source/umfpack_solve.c $(INC) + $(MX) -DZINT -DWSOLVE -c $< + - $(MV) umfpack_solve.o $@ + +umfpack_mz_%.o: ../Source/umfpack_%.c $(INC) + $(MX) -DZINT -c $< + - $(MV) umfpack_$*.o $@ + +#---------------------------------------- +# Generic routines for MATLAB +#---------------------------------------- + +umfpack_m_timer.o: ../Source/umfpack_timer.c $(INC) + $(MX) -c $< + - $(MV) umfpack_timer.o $@ + +umfpack_m_tictoc.o: ../Source/umfpack_tictoc.c $(INC) + $(MX) -c $< + - $(MV) umfpack_tictoc.o $@ + +#---------------------------------------- +# umfpack mexFunction +#---------------------------------------- + +umfpack: umfpackmex.c $(MEXUMFPACK) $(MEXAMD) + $(MX) -output umfpack umfpackmex.c $(MEXUMFPACK) $(MEXAMD) + +luflop: luflopmex.c + $(MX) -output luflop luflopmex.c + +#------------------------------------------------------------------------------- +# Remove all but the files in the original distribution +#------------------------------------------------------------------------------- + +purge: clean + - $(RM) *.mex* *.dll diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/Makefile b/deal.II/contrib/umfpack/UMFPACK/MATLAB/Makefile new file mode 100644 index 0000000000..4501ba33a5 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/Makefile @@ -0,0 +1,436 @@ +#------------------------------------------------------------------------------- +# UMFPACK Makefile for the UMFPACK MATLAB mexFunction (old "make" only) +#------------------------------------------------------------------------------- + +# UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +# Davis. All Rights Reserved. See ../README for License. + +# This is a very ugly Makefile, and is only provided for those who do not +# have GNU make. Note that it is not used if you have GNU make. It ignores +# dependency checking and just compiles everything. It was created +# automatically, via make -n using the GNUmakefile. That way, I don't have +# maintain two Makefiles. + +all: umfpack luflop + +include ../Make/Make.include + +MX = $(MEX) -I../Include -I../Source -I../../AMD/Include -I../../AMD/Source + +umfpack: + $(MX) -DDINT -c ../Source/umf_analyze.c + $(MV) -f umf_analyze.o umf_m_analyze.o + $(MX) -DDINT -c ../Source/umf_apply_order.c + $(MV) -f umf_apply_order.o umf_m_apply_order.o + $(MX) -DDINT -c ../Source/umf_colamd.c + $(MV) -f umf_colamd.o umf_m_colamd.o + $(MX) -DDINT -c ../Source/umf_free.c + $(MV) -f umf_free.o umf_m_free.o + $(MX) -DDINT -c ../Source/umf_fsize.c + $(MV) -f umf_fsize.o umf_m_fsize.o + $(MX) -DDINT -c ../Source/umf_is_permutation.c + $(MV) -f umf_is_permutation.o umf_m_is_permutation.o + $(MX) -DDINT -c ../Source/umf_malloc.c + $(MV) -f umf_malloc.o umf_m_malloc.o + $(MX) -DDINT -c ../Source/umf_realloc.c + $(MV) -f umf_realloc.o umf_m_realloc.o + $(MX) -DDINT -c ../Source/umf_report_perm.c + $(MV) -f umf_report_perm.o umf_m_report_perm.o + $(MX) -DDINT -c ../Source/umf_singletons.c + $(MV) -f umf_singletons.o umf_m_singletons.o + $(MX) -DDINT -DCONJUGATE_SOLVE -c ../Source/umf_ltsolve.c + $(MV) -f umf_ltsolve.o umf_md_lhsolve.o + $(MX) -DDINT -DCONJUGATE_SOLVE -c ../Source/umf_utsolve.c + $(MV) -f umf_utsolve.o umf_md_uhsolve.o + $(MX) -DDINT -DDO_MAP -c ../Source/umf_triplet.c + $(MV) -f umf_triplet.o umf_md_triplet_map_nox.o + $(MX) -DDINT -DDO_VALUES -c ../Source/umf_triplet.c + $(MV) -f umf_triplet.o umf_md_triplet_nomap_x.o + $(MX) -DDINT -c ../Source/umf_triplet.c + $(MV) -f umf_triplet.o umf_md_triplet_nomap_nox.o + $(MX) -DDINT -DDO_MAP -DDO_VALUES -c ../Source/umf_triplet.c + $(MV) -f umf_triplet.o umf_md_triplet_map_x.o + $(MX) -DDINT -DFIXQ -c ../Source/umf_assemble.c + $(MV) -f umf_assemble.o umf_md_assemble_fixq.o + $(MX) -DDINT -DDROP -c ../Source/umf_store_lu.c + $(MV) -f umf_store_lu.o umf_md_store_lu_drop.o + $(MX) -DDINT -c ../Source/umf_assemble.c + $(MV) -f umf_assemble.o umf_md_assemble.o + $(MX) -DDINT -c ../Source/umf_blas3_update.c + $(MV) -f umf_blas3_update.o umf_md_blas3_update.o + $(MX) -DDINT -c ../Source/umf_build_tuples.c + $(MV) -f umf_build_tuples.o umf_md_build_tuples.o + $(MX) -DDINT -c ../Source/umf_create_element.c + $(MV) -f umf_create_element.o umf_md_create_element.o + $(MX) -DDINT -c ../Source/umf_dump.c + $(MV) -f umf_dump.o umf_md_dump.o + $(MX) -DDINT -c ../Source/umf_extend_front.c + $(MV) -f umf_extend_front.o umf_md_extend_front.o + $(MX) -DDINT -c ../Source/umf_garbage_collection.c + $(MV) -f umf_garbage_collection.o umf_md_garbage_collection.o + $(MX) -DDINT -c ../Source/umf_get_memory.c + $(MV) -f umf_get_memory.o umf_md_get_memory.o + $(MX) -DDINT -c ../Source/umf_init_front.c + $(MV) -f umf_init_front.o umf_md_init_front.o + $(MX) -DDINT -c ../Source/umf_kernel.c + $(MV) -f umf_kernel.o umf_md_kernel.o + $(MX) -DDINT -c ../Source/umf_kernel_init.c + $(MV) -f umf_kernel_init.o umf_md_kernel_init.o + $(MX) -DDINT -c ../Source/umf_kernel_wrapup.c + $(MV) -f umf_kernel_wrapup.o umf_md_kernel_wrapup.o + $(MX) -DDINT -c ../Source/umf_local_search.c + $(MV) -f umf_local_search.o umf_md_local_search.o + $(MX) -DDINT -c ../Source/umf_lsolve.c + $(MV) -f umf_lsolve.o umf_md_lsolve.o + $(MX) -DDINT -c ../Source/umf_ltsolve.c + $(MV) -f umf_ltsolve.o umf_md_ltsolve.o + $(MX) -DDINT -c ../Source/umf_mem_alloc_element.c + $(MV) -f umf_mem_alloc_element.o umf_md_mem_alloc_element.o + $(MX) -DDINT -c ../Source/umf_mem_alloc_head_block.c + $(MV) -f umf_mem_alloc_head_block.o umf_md_mem_alloc_head_block.o + $(MX) -DDINT -c ../Source/umf_mem_alloc_tail_block.c + $(MV) -f umf_mem_alloc_tail_block.o umf_md_mem_alloc_tail_block.o + $(MX) -DDINT -c ../Source/umf_mem_free_tail_block.c + $(MV) -f umf_mem_free_tail_block.o umf_md_mem_free_tail_block.o + $(MX) -DDINT -c ../Source/umf_mem_init_memoryspace.c + $(MV) -f umf_mem_init_memoryspace.o umf_md_mem_init_memoryspace.o + $(MX) -DDINT -c ../Source/umf_report_vector.c + $(MV) -f umf_report_vector.o umf_md_report_vector.o + $(MX) -DDINT -c ../Source/umf_row_search.c + $(MV) -f umf_row_search.o umf_md_row_search.o + $(MX) -DDINT -c ../Source/umf_scale_column.c + $(MV) -f umf_scale_column.o umf_md_scale_column.o + $(MX) -DDINT -c ../Source/umf_set_stats.c + $(MV) -f umf_set_stats.o umf_md_set_stats.o + $(MX) -DDINT -c ../Source/umf_solve.c + $(MV) -f umf_solve.o umf_md_solve.o + $(MX) -DDINT -c ../Source/umf_symbolic_usage.c + $(MV) -f umf_symbolic_usage.o umf_md_symbolic_usage.o + $(MX) -DDINT -c ../Source/umf_transpose.c + $(MV) -f umf_transpose.o umf_md_transpose.o + $(MX) -DDINT -c ../Source/umf_tuple_lengths.c + $(MV) -f umf_tuple_lengths.o umf_md_tuple_lengths.o + $(MX) -DDINT -c ../Source/umf_usolve.c + $(MV) -f umf_usolve.o umf_md_usolve.o + $(MX) -DDINT -c ../Source/umf_utsolve.c + $(MV) -f umf_utsolve.o umf_md_utsolve.o + $(MX) -DDINT -c ../Source/umf_valid_numeric.c + $(MV) -f umf_valid_numeric.o umf_md_valid_numeric.o + $(MX) -DDINT -c ../Source/umf_valid_symbolic.c + $(MV) -f umf_valid_symbolic.o umf_md_valid_symbolic.o + $(MX) -DDINT -c ../Source/umf_grow_front.c + $(MV) -f umf_grow_front.o umf_md_grow_front.o + $(MX) -DDINT -c ../Source/umf_start_front.c + $(MV) -f umf_start_front.o umf_md_start_front.o + $(MX) -DDINT -c ../Source/umf_2by2.c + $(MV) -f umf_2by2.o umf_md_2by2.o + $(MX) -DDINT -c ../Source/umf_store_lu.c + $(MV) -f umf_store_lu.o umf_md_store_lu.o + $(MX) -DDINT -c ../Source/umf_scale.c + $(MV) -f umf_scale.o umf_md_scale.o + $(MX) -DDINT -DWSOLVE -c ../Source/umfpack_solve.c + $(MV) -f umfpack_solve.o umfpack_md_wsolve.o + $(MX) -DDINT -c ../Source/umfpack_col_to_triplet.c + $(MV) -f umfpack_col_to_triplet.o umfpack_md_col_to_triplet.o + $(MX) -DDINT -c ../Source/umfpack_defaults.c + $(MV) -f umfpack_defaults.o umfpack_md_defaults.o + $(MX) -DDINT -c ../Source/umfpack_free_numeric.c + $(MV) -f umfpack_free_numeric.o umfpack_md_free_numeric.o + $(MX) -DDINT -c ../Source/umfpack_free_symbolic.c + $(MV) -f umfpack_free_symbolic.o umfpack_md_free_symbolic.o + $(MX) -DDINT -c ../Source/umfpack_get_numeric.c + $(MV) -f umfpack_get_numeric.o umfpack_md_get_numeric.o + $(MX) -DDINT -c ../Source/umfpack_get_lunz.c + $(MV) -f umfpack_get_lunz.o umfpack_md_get_lunz.o + $(MX) -DDINT -c ../Source/umfpack_get_symbolic.c + $(MV) -f umfpack_get_symbolic.o umfpack_md_get_symbolic.o + $(MX) -DDINT -c ../Source/umfpack_numeric.c + $(MV) -f umfpack_numeric.o umfpack_md_numeric.o + $(MX) -DDINT -c ../Source/umfpack_qsymbolic.c + $(MV) -f umfpack_qsymbolic.o umfpack_md_qsymbolic.o + $(MX) -DDINT -c ../Source/umfpack_report_control.c + $(MV) -f umfpack_report_control.o umfpack_md_report_control.o + $(MX) -DDINT -c ../Source/umfpack_report_info.c + $(MV) -f umfpack_report_info.o umfpack_md_report_info.o + $(MX) -DDINT -c ../Source/umfpack_report_matrix.c + $(MV) -f umfpack_report_matrix.o umfpack_md_report_matrix.o + $(MX) -DDINT -c ../Source/umfpack_report_numeric.c + $(MV) -f umfpack_report_numeric.o umfpack_md_report_numeric.o + $(MX) -DDINT -c ../Source/umfpack_report_perm.c + $(MV) -f umfpack_report_perm.o umfpack_md_report_perm.o + $(MX) -DDINT -c ../Source/umfpack_report_status.c + $(MV) -f umfpack_report_status.o umfpack_md_report_status.o + $(MX) -DDINT -c ../Source/umfpack_report_symbolic.c + $(MV) -f umfpack_report_symbolic.o umfpack_md_report_symbolic.o + $(MX) -DDINT -c ../Source/umfpack_report_triplet.c + $(MV) -f umfpack_report_triplet.o umfpack_md_report_triplet.o + $(MX) -DDINT -c ../Source/umfpack_report_vector.c + $(MV) -f umfpack_report_vector.o umfpack_md_report_vector.o + $(MX) -DDINT -c ../Source/umfpack_solve.c + $(MV) -f umfpack_solve.o umfpack_md_solve.o + $(MX) -DDINT -c ../Source/umfpack_symbolic.c + $(MV) -f umfpack_symbolic.o umfpack_md_symbolic.o + $(MX) -DDINT -c ../Source/umfpack_transpose.c + $(MV) -f umfpack_transpose.o umfpack_md_transpose.o + $(MX) -DDINT -c ../Source/umfpack_triplet_to_col.c + $(MV) -f umfpack_triplet_to_col.o umfpack_md_triplet_to_col.o + $(MX) -DDINT -c ../Source/umfpack_scale.c + $(MV) -f umfpack_scale.o umfpack_md_scale.o + $(MX) -DDINT -c ../Source/umfpack_load_numeric.c + $(MV) -f umfpack_load_numeric.o umfpack_md_load_numeric.o + $(MX) -DDINT -c ../Source/umfpack_save_numeric.c + $(MV) -f umfpack_save_numeric.o umfpack_md_save_numeric.o + $(MX) -DDINT -c ../Source/umfpack_load_symbolic.c + $(MV) -f umfpack_load_symbolic.o umfpack_md_load_symbolic.o + $(MX) -DDINT -c ../Source/umfpack_save_symbolic.c + $(MV) -f umfpack_save_symbolic.o umfpack_md_save_symbolic.o + $(MX) -DZINT -DCONJUGATE_SOLVE -c ../Source/umf_ltsolve.c + $(MV) -f umf_ltsolve.o umf_mz_lhsolve.o + $(MX) -DZINT -DCONJUGATE_SOLVE -c ../Source/umf_utsolve.c + $(MV) -f umf_utsolve.o umf_mz_uhsolve.o + $(MX) -DZINT -DDO_MAP -c ../Source/umf_triplet.c + $(MV) -f umf_triplet.o umf_mz_triplet_map_nox.o + $(MX) -DZINT -DDO_VALUES -c ../Source/umf_triplet.c + $(MV) -f umf_triplet.o umf_mz_triplet_nomap_x.o + $(MX) -DZINT -c ../Source/umf_triplet.c + $(MV) -f umf_triplet.o umf_mz_triplet_nomap_nox.o + $(MX) -DZINT -DDO_MAP -DDO_VALUES -c ../Source/umf_triplet.c + $(MV) -f umf_triplet.o umf_mz_triplet_map_x.o + $(MX) -DZINT -DFIXQ -c ../Source/umf_assemble.c + $(MV) -f umf_assemble.o umf_mz_assemble_fixq.o + $(MX) -DZINT -DDROP -c ../Source/umf_store_lu.c + $(MV) -f umf_store_lu.o umf_mz_store_lu_drop.o + $(MX) -DZINT -c ../Source/umf_assemble.c + $(MV) -f umf_assemble.o umf_mz_assemble.o + $(MX) -DZINT -c ../Source/umf_blas3_update.c + $(MV) -f umf_blas3_update.o umf_mz_blas3_update.o + $(MX) -DZINT -c ../Source/umf_build_tuples.c + $(MV) -f umf_build_tuples.o umf_mz_build_tuples.o + $(MX) -DZINT -c ../Source/umf_create_element.c + $(MV) -f umf_create_element.o umf_mz_create_element.o + $(MX) -DZINT -c ../Source/umf_dump.c + $(MV) -f umf_dump.o umf_mz_dump.o + $(MX) -DZINT -c ../Source/umf_extend_front.c + $(MV) -f umf_extend_front.o umf_mz_extend_front.o + $(MX) -DZINT -c ../Source/umf_garbage_collection.c + $(MV) -f umf_garbage_collection.o umf_mz_garbage_collection.o + $(MX) -DZINT -c ../Source/umf_get_memory.c + $(MV) -f umf_get_memory.o umf_mz_get_memory.o + $(MX) -DZINT -c ../Source/umf_init_front.c + $(MV) -f umf_init_front.o umf_mz_init_front.o + $(MX) -DZINT -c ../Source/umf_kernel.c + $(MV) -f umf_kernel.o umf_mz_kernel.o + $(MX) -DZINT -c ../Source/umf_kernel_init.c + $(MV) -f umf_kernel_init.o umf_mz_kernel_init.o + $(MX) -DZINT -c ../Source/umf_kernel_wrapup.c + $(MV) -f umf_kernel_wrapup.o umf_mz_kernel_wrapup.o + $(MX) -DZINT -c ../Source/umf_local_search.c + $(MV) -f umf_local_search.o umf_mz_local_search.o + $(MX) -DZINT -c ../Source/umf_lsolve.c + $(MV) -f umf_lsolve.o umf_mz_lsolve.o + $(MX) -DZINT -c ../Source/umf_ltsolve.c + $(MV) -f umf_ltsolve.o umf_mz_ltsolve.o + $(MX) -DZINT -c ../Source/umf_mem_alloc_element.c + $(MV) -f umf_mem_alloc_element.o umf_mz_mem_alloc_element.o + $(MX) -DZINT -c ../Source/umf_mem_alloc_head_block.c + $(MV) -f umf_mem_alloc_head_block.o umf_mz_mem_alloc_head_block.o + $(MX) -DZINT -c ../Source/umf_mem_alloc_tail_block.c + $(MV) -f umf_mem_alloc_tail_block.o umf_mz_mem_alloc_tail_block.o + $(MX) -DZINT -c ../Source/umf_mem_free_tail_block.c + $(MV) -f umf_mem_free_tail_block.o umf_mz_mem_free_tail_block.o + $(MX) -DZINT -c ../Source/umf_mem_init_memoryspace.c + $(MV) -f umf_mem_init_memoryspace.o umf_mz_mem_init_memoryspace.o + $(MX) -DZINT -c ../Source/umf_report_vector.c + $(MV) -f umf_report_vector.o umf_mz_report_vector.o + $(MX) -DZINT -c ../Source/umf_row_search.c + $(MV) -f umf_row_search.o umf_mz_row_search.o + $(MX) -DZINT -c ../Source/umf_scale_column.c + $(MV) -f umf_scale_column.o umf_mz_scale_column.o + $(MX) -DZINT -c ../Source/umf_set_stats.c + $(MV) -f umf_set_stats.o umf_mz_set_stats.o + $(MX) -DZINT -c ../Source/umf_solve.c + $(MV) -f umf_solve.o umf_mz_solve.o + $(MX) -DZINT -c ../Source/umf_symbolic_usage.c + $(MV) -f umf_symbolic_usage.o umf_mz_symbolic_usage.o + $(MX) -DZINT -c ../Source/umf_transpose.c + $(MV) -f umf_transpose.o umf_mz_transpose.o + $(MX) -DZINT -c ../Source/umf_tuple_lengths.c + $(MV) -f umf_tuple_lengths.o umf_mz_tuple_lengths.o + $(MX) -DZINT -c ../Source/umf_usolve.c + $(MV) -f umf_usolve.o umf_mz_usolve.o + $(MX) -DZINT -c ../Source/umf_utsolve.c + $(MV) -f umf_utsolve.o umf_mz_utsolve.o + $(MX) -DZINT -c ../Source/umf_valid_numeric.c + $(MV) -f umf_valid_numeric.o umf_mz_valid_numeric.o + $(MX) -DZINT -c ../Source/umf_valid_symbolic.c + $(MV) -f umf_valid_symbolic.o umf_mz_valid_symbolic.o + $(MX) -DZINT -c ../Source/umf_grow_front.c + $(MV) -f umf_grow_front.o umf_mz_grow_front.o + $(MX) -DZINT -c ../Source/umf_start_front.c + $(MV) -f umf_start_front.o umf_mz_start_front.o + $(MX) -DZINT -c ../Source/umf_2by2.c + $(MV) -f umf_2by2.o umf_mz_2by2.o + $(MX) -DZINT -c ../Source/umf_store_lu.c + $(MV) -f umf_store_lu.o umf_mz_store_lu.o + $(MX) -DZINT -c ../Source/umf_scale.c + $(MV) -f umf_scale.o umf_mz_scale.o + $(MX) -DZINT -DWSOLVE -c ../Source/umfpack_solve.c + $(MV) -f umfpack_solve.o umfpack_mz_wsolve.o + $(MX) -DZINT -c ../Source/umfpack_col_to_triplet.c + $(MV) -f umfpack_col_to_triplet.o umfpack_mz_col_to_triplet.o + $(MX) -DZINT -c ../Source/umfpack_defaults.c + $(MV) -f umfpack_defaults.o umfpack_mz_defaults.o + $(MX) -DZINT -c ../Source/umfpack_free_numeric.c + $(MV) -f umfpack_free_numeric.o umfpack_mz_free_numeric.o + $(MX) -DZINT -c ../Source/umfpack_free_symbolic.c + $(MV) -f umfpack_free_symbolic.o umfpack_mz_free_symbolic.o + $(MX) -DZINT -c ../Source/umfpack_get_numeric.c + $(MV) -f umfpack_get_numeric.o umfpack_mz_get_numeric.o + $(MX) -DZINT -c ../Source/umfpack_get_lunz.c + $(MV) -f umfpack_get_lunz.o umfpack_mz_get_lunz.o + $(MX) -DZINT -c ../Source/umfpack_get_symbolic.c + $(MV) -f umfpack_get_symbolic.o umfpack_mz_get_symbolic.o + $(MX) -DZINT -c ../Source/umfpack_numeric.c + $(MV) -f umfpack_numeric.o umfpack_mz_numeric.o + $(MX) -DZINT -c ../Source/umfpack_qsymbolic.c + $(MV) -f umfpack_qsymbolic.o umfpack_mz_qsymbolic.o + $(MX) -DZINT -c ../Source/umfpack_report_control.c + $(MV) -f umfpack_report_control.o umfpack_mz_report_control.o + $(MX) -DZINT -c ../Source/umfpack_report_info.c + $(MV) -f umfpack_report_info.o umfpack_mz_report_info.o + $(MX) -DZINT -c ../Source/umfpack_report_matrix.c + $(MV) -f umfpack_report_matrix.o umfpack_mz_report_matrix.o + $(MX) -DZINT -c ../Source/umfpack_report_numeric.c + $(MV) -f umfpack_report_numeric.o umfpack_mz_report_numeric.o + $(MX) -DZINT -c ../Source/umfpack_report_perm.c + $(MV) -f umfpack_report_perm.o umfpack_mz_report_perm.o + $(MX) -DZINT -c ../Source/umfpack_report_status.c + $(MV) -f umfpack_report_status.o umfpack_mz_report_status.o + $(MX) -DZINT -c ../Source/umfpack_report_symbolic.c + $(MV) -f umfpack_report_symbolic.o umfpack_mz_report_symbolic.o + $(MX) -DZINT -c ../Source/umfpack_report_triplet.c + $(MV) -f umfpack_report_triplet.o umfpack_mz_report_triplet.o + $(MX) -DZINT -c ../Source/umfpack_report_vector.c + $(MV) -f umfpack_report_vector.o umfpack_mz_report_vector.o + $(MX) -DZINT -c ../Source/umfpack_solve.c + $(MV) -f umfpack_solve.o umfpack_mz_solve.o + $(MX) -DZINT -c ../Source/umfpack_symbolic.c + $(MV) -f umfpack_symbolic.o umfpack_mz_symbolic.o + $(MX) -DZINT -c ../Source/umfpack_transpose.c + $(MV) -f umfpack_transpose.o umfpack_mz_transpose.o + $(MX) -DZINT -c ../Source/umfpack_triplet_to_col.c + $(MV) -f umfpack_triplet_to_col.o umfpack_mz_triplet_to_col.o + $(MX) -DZINT -c ../Source/umfpack_scale.c + $(MV) -f umfpack_scale.o umfpack_mz_scale.o + $(MX) -DZINT -c ../Source/umfpack_load_numeric.c + $(MV) -f umfpack_load_numeric.o umfpack_mz_load_numeric.o + $(MX) -DZINT -c ../Source/umfpack_save_numeric.c + $(MV) -f umfpack_save_numeric.o umfpack_mz_save_numeric.o + $(MX) -DZINT -c ../Source/umfpack_load_symbolic.c + $(MV) -f umfpack_load_symbolic.o umfpack_mz_load_symbolic.o + $(MX) -DZINT -c ../Source/umfpack_save_symbolic.c + $(MV) -f umfpack_save_symbolic.o umfpack_mz_save_symbolic.o + $(MX) -c ../Source/umfpack_timer.c + $(MV) -f umfpack_timer.o umfpack_m_timer.o + $(MX) -c ../Source/umfpack_tictoc.c + $(MV) -f umfpack_tictoc.o umfpack_m_tictoc.o + $(MX) -DDINT -c ../../AMD/Source/amd_aat.c + $(MV) -f amd_aat.o amd_m_aat.o + $(MX) -DDINT -c ../../AMD/Source/amd_1.c + $(MV) -f amd_1.o amd_m_1.o + $(MX) -DDINT -c ../../AMD/Source/amd_2.c + $(MV) -f amd_2.o amd_m_2.o + $(MX) -DDINT -c ../../AMD/Source/amd_dump.c + $(MV) -f amd_dump.o amd_m_dump.o + $(MX) -DDINT -c ../../AMD/Source/amd_postorder.c + $(MV) -f amd_postorder.o amd_m_postorder.o + $(MX) -DDINT -c ../../AMD/Source/amd_post_tree.c + $(MV) -f amd_post_tree.o amd_m_post_tree.o + $(MX) -DDINT -c ../../AMD/Source/amd_defaults.c + $(MV) -f amd_defaults.o amd_m_defaults.o + $(MX) -DDINT -c ../../AMD/Source/amd_order.c + $(MV) -f amd_order.o amd_m_order.o + $(MX) -DDINT -c ../../AMD/Source/amd_control.c + $(MV) -f amd_control.o amd_m_control.o + $(MX) -DDINT -c ../../AMD/Source/amd_info.c + $(MV) -f amd_info.o amd_m_info.o + $(MX) -DDINT -c ../../AMD/Source/amd_valid.c + $(MV) -f amd_valid.o amd_m_valid.o + $(MX) -output umfpack umfpackmex.c \ + umf_m_analyze.o umf_m_apply_order.o umf_m_colamd.o umf_m_free.o \ + umf_m_fsize.o umf_m_is_permutation.o umf_m_malloc.o \ + umf_m_realloc.o umf_m_report_perm.o umf_m_singletons.o \ + umf_md_lhsolve.o umf_md_uhsolve.o umf_md_triplet_map_nox.o \ + umf_md_triplet_nomap_x.o umf_md_triplet_nomap_nox.o \ + umf_md_triplet_map_x.o umf_md_assemble_fixq.o \ + umf_md_store_lu_drop.o umf_md_assemble.o umf_md_blas3_update.o \ + umf_md_build_tuples.o umf_md_create_element.o umf_md_dump.o \ + umf_md_extend_front.o umf_md_garbage_collection.o \ + umf_md_get_memory.o umf_md_init_front.o umf_md_kernel.o \ + umf_md_kernel_init.o umf_md_kernel_wrapup.o umf_md_local_search.o \ + umf_md_lsolve.o umf_md_ltsolve.o umf_md_mem_alloc_element.o \ + umf_md_mem_alloc_head_block.o umf_md_mem_alloc_tail_block.o \ + umf_md_mem_free_tail_block.o umf_md_mem_init_memoryspace.o \ + umf_md_report_vector.o umf_md_row_search.o umf_md_scale_column.o \ + umf_md_set_stats.o umf_md_solve.o umf_md_symbolic_usage.o \ + umf_md_transpose.o umf_md_tuple_lengths.o umf_md_usolve.o \ + umf_md_utsolve.o umf_md_valid_numeric.o umf_md_valid_symbolic.o \ + umf_md_grow_front.o umf_md_start_front.o umf_md_2by2.o \ + umf_md_store_lu.o umf_md_scale.o umfpack_md_wsolve.o \ + umfpack_md_col_to_triplet.o umfpack_md_defaults.o \ + umfpack_md_free_numeric.o umfpack_md_free_symbolic.o \ + umfpack_md_get_numeric.o umfpack_md_get_lunz.o \ + umfpack_md_get_symbolic.o umfpack_md_numeric.o \ + umfpack_md_qsymbolic.o umfpack_md_report_control.o \ + umfpack_md_report_info.o umfpack_md_report_matrix.o \ + umfpack_md_report_numeric.o umfpack_md_report_perm.o \ + umfpack_md_report_status.o umfpack_md_report_symbolic.o \ + umfpack_md_report_triplet.o umfpack_md_report_vector.o \ + umfpack_md_solve.o umfpack_md_symbolic.o umfpack_md_transpose.o \ + umfpack_md_triplet_to_col.o umfpack_md_scale.o \ + umfpack_md_load_numeric.o umfpack_md_save_numeric.o \ + umfpack_md_load_symbolic.o umfpack_md_save_symbolic.o \ + umf_mz_lhsolve.o umf_mz_uhsolve.o umf_mz_triplet_map_nox.o \ + umf_mz_triplet_nomap_x.o umf_mz_triplet_nomap_nox.o \ + umf_mz_triplet_map_x.o umf_mz_assemble_fixq.o \ + umf_mz_store_lu_drop.o umf_mz_assemble.o umf_mz_blas3_update.o \ + umf_mz_build_tuples.o umf_mz_create_element.o umf_mz_dump.o \ + umf_mz_extend_front.o umf_mz_garbage_collection.o \ + umf_mz_get_memory.o umf_mz_init_front.o umf_mz_kernel.o \ + umf_mz_kernel_init.o umf_mz_kernel_wrapup.o umf_mz_local_search.o \ + umf_mz_lsolve.o umf_mz_ltsolve.o umf_mz_mem_alloc_element.o \ + umf_mz_mem_alloc_head_block.o umf_mz_mem_alloc_tail_block.o \ + umf_mz_mem_free_tail_block.o umf_mz_mem_init_memoryspace.o \ + umf_mz_report_vector.o umf_mz_row_search.o umf_mz_scale_column.o \ + umf_mz_set_stats.o umf_mz_solve.o umf_mz_symbolic_usage.o \ + umf_mz_transpose.o umf_mz_tuple_lengths.o umf_mz_usolve.o \ + umf_mz_utsolve.o umf_mz_valid_numeric.o umf_mz_valid_symbolic.o \ + umf_mz_grow_front.o umf_mz_start_front.o umf_mz_2by2.o \ + umf_mz_store_lu.o umf_mz_scale.o umfpack_mz_wsolve.o \ + umfpack_mz_col_to_triplet.o umfpack_mz_defaults.o \ + umfpack_mz_free_numeric.o umfpack_mz_free_symbolic.o \ + umfpack_mz_get_numeric.o umfpack_mz_get_lunz.o \ + umfpack_mz_get_symbolic.o umfpack_mz_numeric.o \ + umfpack_mz_qsymbolic.o umfpack_mz_report_control.o \ + umfpack_mz_report_info.o umfpack_mz_report_matrix.o \ + umfpack_mz_report_numeric.o umfpack_mz_report_perm.o \ + umfpack_mz_report_status.o umfpack_mz_report_symbolic.o \ + umfpack_mz_report_triplet.o umfpack_mz_report_vector.o \ + umfpack_mz_solve.o umfpack_mz_symbolic.o umfpack_mz_transpose.o \ + umfpack_mz_triplet_to_col.o umfpack_mz_scale.o \ + umfpack_mz_load_numeric.o umfpack_mz_save_numeric.o \ + umfpack_mz_load_symbolic.o umfpack_mz_save_symbolic.o \ + umfpack_m_timer.o umfpack_m_tictoc.o \ + amd_m_aat.o amd_m_1.o amd_m_2.o amd_m_dump.o \ + amd_m_postorder.o amd_m_post_tree.o amd_m_defaults.o amd_m_order.o \ + amd_m_control.o amd_m_info.o amd_m_valid.o + +luflop: luflopmex.c + $(MX) -output luflop luflopmex.c + +#------------------------------------------------------------------------------- +# Remove all but the files in the original distribution +#------------------------------------------------------------------------------- + +purge: clean + - $(RM) *.mex* *.dll diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/lu_normest.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/lu_normest.m new file mode 100644 index 0000000000..ea3490cfca --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/lu_normest.m @@ -0,0 +1,106 @@ +function rho = lu_normest (A, L, U) +% LU_NORMEST: estimate the 1-norm of A-L*U without computing L*U +% +% Usage: +% +% rho = lu_normest (A, L, U) +% +% which estimates the computation of the 1-norm: +% +% rho = norm (A-L*U, 1) +% +% Authors: William W. Hager, Math Dept., Univ. of Florida +% Timothy A. Davis, CISE Dept., Univ. of Florida +% Gainesville, FL, 32611, USA. +% based on normest1, contributed on November, 1997 +% +% This code can be quite easily adapted to estimate the 1-norm of any +% matrix E, where E itself is dense or not explicitly represented, but the +% computation of E (and E') times a vector is easy. In this case, our matrix +% of interest is: +% +% E = A-L*U +% +% That is, L*U is the LU factorization of A, where A, L and U +% are sparse. This code works for dense matrices A and L too, +% but it would not be needed in that case, since E is easy to compute +% explicitly. For sparse A, L, and U, computing E explicitly would be quite +% expensive, and thus normest (A-L*U) would be prohibitive. +% +% For a detailed description, see Davis, T. A. and Hager, W. W., +% Modifying a sparse Cholesky factorization, SIAM J. Matrix Analysis and +% Applications, 1999, vol. 20, no. 3, 606-627. + +% The three places that the matrix-vector multiply E*x is used are highlighted. +% Note that E is never formed explicity. + +[m n] = size (A) ; + +if (m ~= n) + % pad A, L, and U with zeros so that they are all square + if (m < n) + U = [ U ; (sparse (n-m,n)) ] ; + L = [ L , (sparse (m,n-m)) ; (sparse (n-m,n)) ] ; + A = [ A ; (sparse (n-m,n)) ] ; + else + U = [ U , (sparse (n,m-n)) ; (sparse (m-n,m)) ] ; + L = [ L , (sparse (m,m-n)) ] ; + A = [ A , (sparse (m,m-n)) ] ; + end +end + +[m n] = size (A) ; + +notvisited = ones (m, 1) ; % nonvisited(j) is zero if j is visited, 1 otherwise +rho = 0 ; % the global rho + +At = A' ; +Lt = L' ; + +for trial = 1:3 % { + + x = notvisited ./ sum (notvisited) ; + rho1 = 0 ; % the current rho for this trial + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%% COMPUTE Ex1 = E*x EFFICIENTLY: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Ex1 = (A*x) - L*(U*x) ; + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + rho2 = norm (Ex1, 1) ; + + while rho2 > rho1 % { + + rho1 = rho2 ; + y = 2*(Ex1 >= 0) - 1 ; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%% COMPUTE z = E'*y EFFICIENTLY: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + z = (A'*y) - U'*(L'*y) ; + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + [zj, j] = max (abs (z .* notvisited)) ; + j = j (1) ; + if (abs (z (j)) > z'*x) % { + x = zeros (m, 1) ; + x (j) = 1 ; + notvisited (j) = 0 ; + else % } { + break ; + end % } + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%% COMPUTE Ex1 = E*x EFFICIENTLY: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Ex1 = (A*x) - L*(U*x) ; + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + rho2 = norm (Ex1, 1) ; + + end % } + + rho = max (rho, rho1) ; + +end % } diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/luflop.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/luflop.m new file mode 100644 index 0000000000..dcc080b55e --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/luflop.m @@ -0,0 +1,24 @@ +function f = luflop (L, U) +%LUFLOP +% +% f = luflop (L, U) +% +% Given an LU factorization, compute how many flops took to compute it. This +% is the same as (assuming U has a zero-free diagonal): +% +% Lnz = full (sum (spones (L))) - 1 ; +% Unz = full (sum (spones (U')))' - 1 ; +% f = 2*Lnz*Unz + sum (Lnz) ; +% +% except that no extra workspace is allocated for spones (L) and spones (U). +% L and U must be sparse. +% +% Note: the above expression has a subtle undercount when exact numerical +% cancelation occurs. Try [L,U,P] = lu (sparse (ones (10))) and then +% luflop (L,U). + +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. Type umfpack_details for License. + +help luflop +error ('luflop mexFunction not found! Use umfpack_make to compile luflop.') ; diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/luflopmex.c b/deal.II/contrib/umfpack/UMFPACK/MATLAB/luflopmex.c new file mode 100644 index 0000000000..5342856ae8 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/luflopmex.c @@ -0,0 +1,121 @@ +/* ========================================================================== */ +/* === luflop mexFunction ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + f = luflop (L, U) ; + + Given L and U, compute: + + Lnz = full (sum (spones (L))) - 1 ; + Unz = full (sum (spones (U')))' - 1 ; + f = 2*Lnz*Unz + sum (Lnz) ; + + without allocating O (lunz) space. + +*/ + +#include "mex.h" +#include "matrix.h" + +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef FALSE +#define FALSE (0) +#endif + +void mexFunction +( + int nlhs, /* number of left-hand sides */ + mxArray *plhs [ ], /* left-hand side matrices */ + int nrhs, /* number of right--hand sides */ + const mxArray *prhs [ ] /* right-hand side matrices */ +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + int *Lp, *Li, *Up, *Ui, *Unz, n, k, row, col, p, Lnz_k, Unz_k ; + mxArray *Lmatrix, *Umatrix ; + double flop_count, *pflop ; + + /* ---------------------------------------------------------------------- */ + /* get inputs L, U */ + /* ---------------------------------------------------------------------- */ + + if (nrhs != 2) + { + mexErrMsgTxt ("Usage: f = luflop (L, U)") ; + } + + Lmatrix = (mxArray *) prhs [0] ; + Umatrix = (mxArray *) prhs [1] ; + + n = mxGetM (Lmatrix) ; + if (n != mxGetN (Lmatrix) || n != mxGetM (Umatrix) || n != mxGetN (Umatrix)) + { + mexErrMsgTxt ("Usage: f = luflop (L, U) ; L and U must be square") ; + } + + if (!mxIsSparse (Lmatrix) || !mxIsSparse (Umatrix)) + { + mexErrMsgTxt ("Usage: f = luflop (L, U) ; L and U must be sparse") ; + } + + Lp = mxGetJc (Lmatrix) ; + Li = mxGetIr (Lmatrix) ; + + Up = mxGetJc (Umatrix) ; + Ui = mxGetIr (Umatrix) ; + + Unz = (int *) mxMalloc (n * sizeof (int)) ; + + /* ---------------------------------------------------------------------- */ + /* count the nonzeros in each row of U */ + /* ---------------------------------------------------------------------- */ + + for (row = 0 ; row < n ; row++) + { + Unz [row] = 0 ; + } + for (col = 0 ; col < n ; col++) + { + for (p = Up [col] ; p < Up [col+1] ; p++) + { + row = Ui [p] ; + Unz [row]++ ; + } + } + + /* ---------------------------------------------------------------------- */ + /* count the flops */ + /* ---------------------------------------------------------------------- */ + + flop_count = 0.0 ; + for (k = 0 ; k < n ; k++) + { + /* off-diagonal nonzeros in column k of L: */ + Lnz_k = Lp [k+1] - Lp [k] - 1 ; + Unz_k = Unz [k] - 1 ; + flop_count += (2 * Lnz_k * Unz_k) + Lnz_k ; + } + + /* ---------------------------------------------------------------------- */ + /* return the result */ + /* ---------------------------------------------------------------------- */ + + plhs [0] = mxCreateDoubleMatrix (1, 1, mxREAL) ; + pflop = mxGetPr (plhs [0]) ; + pflop [0] = flop_count ; +} + diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack.m new file mode 100644 index 0000000000..3285bb6935 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack.m @@ -0,0 +1,44 @@ +function [out1, out2, out3, out4, out5] = umfpack (in1, in2, in3, in4, in5) +% UMFPACK v4.3 is a MATLAB mexFunction for solving sparse linear systems. +% +% UMFPACK v4.3: | MATLAB approximate equivalent: +% --------------------------------------------------------------------- +% x = umfpack (A, '\', b) ; | x = A \ b +% | +% x = umfpack (b, '/', A) ; | x = b / A +% | +% [L,U,P,Q] = umfpack (A) ; | [m,n] = size (A) ; +% | I = speye (n) ; +% | Q = I (:, colamd (A)) ; +% | [L,U,P] = lu (A*Q) ; +% | +% [L,U,P,Q,R] = umfpack (A) ; | [m,n] = size (A) ; +% | I = speye (n) ; +% | Q = I (:, colamd (A)) ; +% | r = full (sum (abs (A), 2)) ; +% | r (find (r == 0)) = 1 ; +% | R = spdiags (r, 0, m, m) ; +% | [L,U,P] = lu ((R\A)*Q) ; +% | +% [P,Q,F,C] = umfpack (A, 'symbolic') | [m,n] = size (A) ; +% | I = speye (n) ; +% | Q = I (:, colamd (A)) ; +% | [count,h,parent,post] = ... +% | symbfact (A*Q, 'col') ; +% +% A must be sparse. It can be complex, singular, and/or rectangular. A must be +% square for '/' or '\'. b must be a full real or complex vector. For +% [L,U,P,Q,R] = umfpack (A), the factorization is L*U = P*(R\A)*Q. If A has a +% mostly symmetric nonzero pattern, then replace "colamd" with "amd" in the +% MATLAB-equivalent column in the table above. Type umfpack_details for more +% information. +% +% See also: lu_normest, colamd, amd. +% To use UMFPACK for an arbitrary b, see umfpack_solve. + +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. Type umfpack_details for License. + +help umfpack +error ('umfpack mexFunction not found! Use umfpack_make to compile umfpack.') ; + diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_btf.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_btf.m new file mode 100644 index 0000000000..a9da270f6b --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_btf.m @@ -0,0 +1,129 @@ +function x = umfpack_btf (A, b, Control) +% UMFPACK_BTF +% +% x = umfpack_btf (A, b, Control) +% +% solve Ax=b by first permuting the matrix A to block triangular form via dmperm +% and then using UMFPACK to factorize each diagonal block. Adjacent 1-by-1 +% blocks are merged into a single upper triangular block, and solved via +% MATLAB's \ operator. The Control parameter is optional (Type umfpack_details +% and umfpack_report for details on its use). A must be square. +% +% See also: umfpack, umfpack_factorize, umfpack_details, dmperm + +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. Type umfpack_details for License. + +if (nargin < 2) + help umfpack_btf + error ('Usage: x = umfpack_btf (A, b, Control)') ; +end + +[m n] = size (A) ; +if (m ~= n) + help umfpack_btf + error ('umfpack_btf: A must be square') ; +end +[m1 n1] = size (b) ; +if (m1 ~= n) + help umfpack_btf + error ('umfpack_btf: b has the wrong dimensions') ; +end + +if (nargin < 3) + Control = umfpack ; +end + +%------------------------------------------------------------------------------- +% find the block triangular form +%------------------------------------------------------------------------------- + +[p,q,r] = dmperm (A) ; +nblocks = length (r) - 1 ; + +%------------------------------------------------------------------------------- +% solve the system +%------------------------------------------------------------------------------- + +if (nblocks == 1 | sprank (A) < n) + + %--------------------------------------------------------------------------- + % matrix is irreducible or structurally singular + %--------------------------------------------------------------------------- + + x = umfpack_solve (A, '\', b, Control) ; + +else + + %--------------------------------------------------------------------------- + % A (p,q) is in block triangular form + %--------------------------------------------------------------------------- + + b = b (p,:) ; + A = A (p,q) ; + x = zeros (size (b)) ; + + %--------------------------------------------------------------------------- + % merge adjacent singletons into a single upper triangular block + %--------------------------------------------------------------------------- + + [r, nblocks, is_triangular] = merge_singletons (r) ; + + %--------------------------------------------------------------------------- + % solve the system: x (q) = A\b + %--------------------------------------------------------------------------- + + for k = nblocks:-1:1 + + % get the kth block + k1 = r (k) ; + k2 = r (k+1) - 1 ; + + % solve the system + x (k1:k2,:) = solver (A (k1:k2, k1:k2), b (k1:k2,:), ... + is_triangular (k), Control) ; + + % off-diagonal block back substitution + b (1:k1-1,:) = b (1:k1-1,:) - A (1:k1-1, k1:k2) * x (k1:k2,:) ; + + end + + x (q,:) = x ; + +end + +%------------------------------------------------------------------------------- +% merge_singletons +%------------------------------------------------------------------------------- + +function [r, nblocks, is_triangular] = merge_singletons (r) +% +% Given r from [p,q,r] = dmperm (A), where A is square, return a modified r that +% reflects the merger of adjacent singletons into a single upper triangular +% block. is_triangular (k) is 1 if the kth block is upper triangular. nblocks +% is the number of new blocks. + +nblocks = length (r) - 1 ; +bsize = r (2:nblocks+1) - r (1:nblocks) ; +t = [0 (bsize == 1)] ; +z = (t (1:nblocks) == 0 & t (2:nblocks+1) == 1) | t (2:nblocks+1) == 0 ; +y = [(find (z)) nblocks+1] ; +r = r (y) ; +nblocks = length (y) - 1 ; +is_triangular = y (2:nblocks+1) - y (1:nblocks) > 1 ; + +%------------------------------------------------------------------------------- +% solve Ax=b, but check for small and/or triangular systems +%------------------------------------------------------------------------------- + +function x = solver (A, b, is_triangular, Control) +if (is_triangular) + % back substitution only + x = A \ b ; +elseif (size (A,1) < 4) + % a very small matrix, solve it as a dense linear system + x = full (A) \ b ; +else + % solve it as a sparse linear system + x = umfpack_solve (A, '\', b, Control) ; +end diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_demo.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_demo.m new file mode 100644 index 0000000000..422a32ffa1 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_demo.m @@ -0,0 +1,195 @@ +function umfpack_demo +% UMFPACK DEMO +% +% A demo of UMFPACK for MATLAB. +% +% See also umfpack, umfpack_make, umfpack_details, umfpack_report, +% and umfpack_simple. + +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. Type umfpack_details for License. + +%------------------------------------------------------------------------------- +% get default control parameters +%------------------------------------------------------------------------------- + +control = umfpack ; +fprintf ('\nEnter the printing level for UMFPACK''s output statistics:\n') ; +fprintf ('0: none, 1: errors only, 2: statistics, 4: print some of outputs\n') ; +c = input ('5: print all output [default is 1]: ') ; +if (isempty (c)) + c = 1 ; +end +control (1) = c ; + +%------------------------------------------------------------------------------- +% solve a simple system +%------------------------------------------------------------------------------- + +fprintf ('\n--------------------------------------------------------------\n') ; +fprintf ('Factor and solve a small system, Ax=b, using default parameters\n') ; +if (control (1) > 1) + fprintf ('(except for verbose printing enabled)\n') ; +end + +load west0067 +A = Problem.A ; +n = size (A, 1) ; +b = rand (n, 1) ; + +fprintf ('Solving Ax=b via UMFPACK:\n') ; +[xu, info] = umfpack (A, '\', b, control) ; +x = xu ; + +fprintf ('Solving Ax=b via MATLAB:\n') ; +xm = A\b ; +x = xm ; + +fprintf ('Difference between UMFPACK and MATLAB solution: %g\n', ... + norm (xu - xm, Inf)) ; + +%------------------------------------------------------------------------------- +% spy the results +%------------------------------------------------------------------------------- + +figure (1) +clf + +subplot (2,3,1) +spy (A) +title ('The matrix A') ; + +subplot (2,3,2) +[P1, Q1, Fr, Ch, Info] = umfpack (A, 'symbolic') ; +treeplot (Fr (1:end-1,2)') ; +title ('Supernodal column elimination tree') ; + +subplot (2,3,3) +spy (P1 * A * Q1) +title ('A, with initial row and column order') ; + +subplot (2,3,4) +fprintf ('\n--------------------------------------------------------------\n') ; +fprintf ('\nFactorizing [L, U, P, Q, R] = umfpack (A)\n') ; +[L, U, P, Q, R] = umfpack (A) ; +spy (P*A*Q) +title ('A, with final row/column order') ; + +fprintf ('\nP * (R\\A) * Q - L*U should be zero:\n') ; +fprintf ('norm (P*(R\\A)*Q - L*U, 1) = %g (exact) %g (estimated)\n', ... + norm (P * (R\A) * Q - L*U, 1), lu_normest (P * (R\A) * Q, L, U)) ; + +fprintf ('\nSolution to Ax=b via UMFPACK factorization:\n') ; +fprintf ('x = Q * (U \\ (L \\ (P * (R \\ b))))\n') ; +xu = Q * (U \ (L \ (P * (R \ b)))) ; +x = xu ; + +fprintf ('\nUMFPACK flop count: %d\n', luflop (L, U)) ; + +subplot (2,3,5) +spy (spones (L) + spones (U)) +title ('UMFPACK LU factors') ; + +subplot (2,3,6) +fprintf ('\nFactorizing [L, U, P] = lu (A (:, q))\n') ; +fprintf ('If you are using a version of MATLAB prior to V6.0, then the\n') ; +fprintf ('following statement (q = colamd (A)) may fail. Either download\n'); +fprintf ('colamd from http://www.cise.ufl.edu/research/sparse, upgrade to\n') ; +fprintf ('MATLAB V6.0 or later, or replace the statement with\n') ; +fprintf ('q = colmmd (A) ;\n') ; +try + q = colamd (A) ; +catch + fprintf ('\n *** colamd not found, using colmmd instead *** \n') ; + q = colmmd (A) ; +end +[L, U, P] = lu (A (:,q)) ; +spy (spones (L) + spones (U)) +title ('MATLAB LU factors') ; + +fprintf ('\nSolution to Ax=b via MATLAB factorization:\n') ; +fprintf ('x = U \\ (L \\ (P * b)) ; x (q) = x ;\n') ; +xm = U \ (L \ (P * b)) ; +xm (q) = xm ; + +fprintf ('Difference between UMFPACK and MATLAB solution: %g\n', ... + norm (xu - xm, Inf)) ; + +fprintf ('\nMATLAB LU flop count: %d\n', luflop (L, U)) ; + +%------------------------------------------------------------------------------- +% solve A'x=b +%------------------------------------------------------------------------------- + +fprintf ('\n--------------------------------------------------------------\n') ; +fprintf ('Solve A''x=b:\n') ; + +fprintf ('Solving A''x=b via UMFPACK:\n') ; +[xu, info] = umfpack (b', '/', A, control) ; +xu = xu' ; + +fprintf ('Solving A''x=b via MATLAB:\n') ; +xm = (b'/A)' ; +x = xm ; + +fprintf ('Difference between UMFPACK and MATLAB solution: %g\n', ... + norm (xu - xm, Inf)) ; + +%------------------------------------------------------------------------------- +% factor A' and then solve Ax=b using the factors of A' +%------------------------------------------------------------------------------- + +fprintf ('\n--------------------------------------------------------------\n') ; +fprintf ('Compute C = A'', and compute the LU factorization of C.\n') ; +fprintf ('Factorizing A'' can sometimes be better than factorizing A itself\n'); +fprintf ('(less work and memory usage). Solve C''x=b; the solution is the\n') ; +fprintf ('same as the solution to Ax=b for the original A.\n'); + +C = A' ; + +% factorize C (P,Q) = L*U +[L, U, P, Q, R, info] = umfpack (C, control) ; + +fprintf ('\nP * (R\\C) * Q - L*U should be zero:\n') ; +fprintf ('norm (P*(R\\C)*Q - L*U, 1) = %g (exact) %g (estimated)\n', ... + norm (P * (R\C) * Q - L*U, 1), lu_normest (P * (R\C) * Q, L, U)) ; + +fprintf ('\nSolution to Ax=b via UMFPACK, using the factors of C:\n') ; +fprintf ('x = R \\ (P'' * (L'' \\ (U'' \\ (Q'' * b)))) ;\n') ; +xu = R \ (P' * (L' \ (U' \ (Q' * b)))) ; +x = xu ; + +fprintf ('Solution to Ax=b via MATLAB:\n') ; +xm = A\b ; +x = xm ; + +fprintf ('Difference between UMFPACK and MATLAB solution: %g\n', ... + norm (xu - xm, Inf)) ; + +%------------------------------------------------------------------------------- +% solve Ax=B +%------------------------------------------------------------------------------- + +fprintf ('\n--------------------------------------------------------------\n') ; +fprintf ('\nSolve AX=B, where B is n-by-10, and sparse\n') ; +B = sprandn (n, 10, 0.05) ; +XU = umfpack_solve (A, '\', B, control) ; +XM = A\B ; + +fprintf ('Difference between UMFPACK and MATLAB solution: %g\n', ... + norm (XU - XM, Inf)) ; + +fprintf ('\n--------------------------------------------------------------\n') ; +fprintf ('\nSolve AX=B, where B is n-by-10, and sparse, using umfpack_btf\n') ; +XU = umfpack_btf (A, B, control) ; + +fprintf ('Difference between UMFPACK and MATLAB solution: %g\n', ... + norm (XU - XM, Inf)) ; + +fprintf ('\n--------------------------------------------------------------\n') ; +fprintf ('\nSolve A''X=B, where B is n-by-10, and sparse\n') ; +XU = umfpack_solve (B', '/', A, control) ; +XM = B'/A ; + +fprintf ('Difference between UMFPACK and MATLAB solution: %g\n', ... + norm (XU - XM, Inf)) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_demo.m.out b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_demo.m.out new file mode 100644 index 0000000000..10adebe433 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_demo.m.out @@ -0,0 +1,72 @@ +>> umfpack_demo + +Enter the printing level for UMFPACK's output statistics: +0: none, 1: errors only, 2: statistics, 4: print some of outputs +5: print all output [default is 1]: + +-------------------------------------------------------------- +Factor and solve a small system, Ax=b, using default parameters +Solving Ax=b via UMFPACK: +Solving Ax=b via MATLAB: +Difference between UMFPACK and MATLAB solution: 1.24345e-14 + +-------------------------------------------------------------- + +Factorizing [L, U, P, Q, R] = umfpack (A) + +P * (R\A) * Q - L*U should be zero: +norm (P*(R\A)*Q - L*U, 1) = 4.2068e-16 (exact) 3.74627e-16 (estimated) + +Solution to Ax=b via UMFPACK factorization: +x = Q * (U \ (L \ (P * (R \ b)))) + +UMFPACK flop count: 2362 + +Factorizing [L, U, P] = lu (A (:, q)) +If you are using a version of MATLAB prior to V6.0, then the +following statement (q = colamd (A)) may fail. Either download +colamd from http://www.cise.ufl.edu/research/sparse, upgrade to +MATLAB V6.0 or later, or replace the statement with +q = colmmd (A) ; + +Solution to Ax=b via MATLAB factorization: +x = U \ (L \ (P * b)) ; x (q) = x ; +Difference between UMFPACK and MATLAB solution: 1.37668e-14 + +MATLAB LU flop count: 3164 + +-------------------------------------------------------------- +Solve A'x=b: +Solving A'x=b via UMFPACK: +Solving A'x=b via MATLAB: +Difference between UMFPACK and MATLAB solution: 3.10862e-15 + +-------------------------------------------------------------- +Compute C = A', and compute the LU factorization of C. +Factorizing A' can sometimes be better than factorizing A itself +(less work and memory usage). Solve C'x=b; the solution is the +same as the solution to Ax=b for the original A. + +P * (R\C) * Q - L*U should be zero: +norm (P*(R\C)*Q - L*U, 1) = 1.31839e-16 (exact) 6.41848e-17 (estimated) + +Solution to Ax=b via UMFPACK, using the factors of C: +x = R \ (P' * (L' \ (U' \ (Q' * b)))) ; +Solution to Ax=b via MATLAB: +Difference between UMFPACK and MATLAB solution: 1.77636e-14 + +-------------------------------------------------------------- + +Solve AX=B, where B is n-by-10, and sparse +Difference between UMFPACK and MATLAB solution: 2.88198e-14 + +-------------------------------------------------------------- + +Solve AX=B, where B is n-by-10, and sparse, using umfpack_btf +Difference between UMFPACK and MATLAB solution: 9.79736e-14 + +-------------------------------------------------------------- + +Solve A'X=B, where B is n-by-10, and sparse +Difference between UMFPACK and MATLAB solution: 1.05244e-13 +>> diary off diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_details.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_details.m new file mode 100644 index 0000000000..315de174cc --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_details.m @@ -0,0 +1,155 @@ +function [out1, out2, out3, out4, out5] = umfpack (in1, in2, in3, in4, in5) +% UMFPACK v4.3: details on each usage. +% +% Factor or solve a sparse linear system, returning either the solution x to +% Ax=b or A'x'=b', the factorization LU=PAQ, or LU=P(R\A)Q. A must be sparse. +% For the solve, A must be square and b must be a dense n-by-1 vector. For LU +% factorization, A can be rectangular. In both cases, A and/or b can be real +% or complex. +% +% UMFPACK analyzes the matrix and selects one of three strategies to factorize +% the matrix. It first finds a set of k initial pivot entries of zero Markowitz +% cost. This forms the first k rows and columns of L and U. The remaining +% submatrix S is then analyzed, based on the symmetry of the nonzero pattern of +% the submatrix and the values on the diagaonal. The strategies include: +% +% (1) unsymmetric: use a COLAMD pre-ordering, a column elimination tree +% post-ordering, refine the column ordering during factorization, +% and make no effort at selecting pivots on the diagonal. +% (2) 2-by-2: like the symmetric strategy (see below), except that local +% row permutations are first made to attempt to place large entries +% on the diagonal. +% (3) symmetric: use an AMD pre-ordering on the matrix S+S', an +% elimination tree post-ordering, do not refine the column ordering +% during factorization, and attempt to select pivots on the diagonal. +% +% Each of the following uses of umfpack (except for "Control = umfpack") is +% stand-alone. That is, no call to umfpack is required for any subsequent +% call. In each usage, the Info output argument is optional. +% +% Usage: +% +% [x, Info] = umfpack (A, '\', b) ; +% [x, Info] = umfpack (A, '\', b, Control) ; +% [x, Info] = umfpack (A, Qinit, '\', b, Control) ; +% [x, Info] = umfpack (A, Qinit, '\', b) ; +% +% Solves Ax=b (similar to x = A\b in MATLAB). +% +% [x, Info] = umfpack (b, '/', A) ; +% [x, Info] = umfpack (b, '/', A, Control) ; +% [x, Info] = umfpack (b, '/', A, Qinit) ; +% [x, Info] = umfpack (b, '/', A, Qinit, Control) ; +% +% Solves A'x'=b' (similar to x = b/A in MATLAB). +% +% [L, U, P, Q, R, Info] = umfpack (A) ; +% [L, U, P, Q, R, Info] = umfpack (A, Control) ; +% [L, U, P, Q, R, Info] = umfpack (A, Qinit) ; +% [L, U, P, Q, R, Info] = umfpack (A, Qinit, Control) ; +% +% Returns the LU factorization of A. P and Q are returned as permutation +% matrices. R is a diagonal sparse matrix of scale factors for the rows +% of A, L is lower triangular, and U is upper triangular. The +% factorization is L*U = P*(R\A)*Q. You can turn off scaling by setting +% Control (17) to zero (in which case R = speye (m)), or by using the +% following syntaxes (in which case Control (17) is ignored): +% +% [L, U, P, Q] = umfpack (A) ; +% [L, U, P, Q] = umfpack (A, Control) ; +% [L, U, P, Q] = umfpack (A, Qinit) ; +% [L, U, P, Q] = umfpack (A, Qinit, Control) ; +% +% Same as above, except that no row scaling is performed. The Info array +% is not returned, either. +% +% [P1, Q1, Fr, Ch, Info] = umfpack (A, 'symbolic') ; +% [P1, Q1, Fr, Ch, Info] = umfpack (A, 'symbolic', Control) ; +% [P1, Q1, Fr, Ch, Info] = umfpack (A, Qinit, 'symbolic') ; +% [P1, Q1, Fr, Ch, Info] = umfpack (A, Qinit, 'symbolic', Control); +% +% Performs only the fill-reducing column pre-ordering (including the +% elimination tree post-ordering) and symbolic factorization. Q1 is the +% initial column permutation (either from colamd, amd, or the input +% ordering Qinit), possibly followed by a column elimination tree post- +% ordering or a symmetric elimination tree post-ordering, depending on +% the strategy used. +% +% For the unsymmetric strategy, P1 is the row ordering induced by Q1 +% (row-merge order). For the 2-by-2 strategy, P1 is the row ordering that +% places large entries on the diagonal of P1*A*Q1. For the symmetric +% strategy, P1 = Q1. +% +% Fr is a (nfr+1)-by-4 array containing information about each frontal +% matrix, where nfr <= n is the number of frontal matrices. Fr (:,1) is +% the number of pivot columns in each front, and Fr (:,2) is the parent +% of each front in the supercolumn elimination tree. Fr (k,2) is zero if +% k is a root. The first Fr (1,1) columns of P1*A*Q1 are the pivot +% columns for the first front, the next Fr (2,1) columns of P1*A*Q1 +% are the pivot columns for the second front, and so on. +% +% For the unsymmetric strategy, Fr (:,3) is the row index of the first +% row in P1*A*Q1 whose leftmost nonzero entry is in a pivot column for +% the kth front. Fr (:,4) is the leftmost descendent of the kth front. +% Rows in the range Fr (Fr (k,4),3) to Fr (k+1,3)-1 form the entire set +% of candidate pivot rows for the kth front (some of these will typically +% have been selected as pivot rows of fronts Fr (k,3) to k-1, before the +% factorization reaches the kth front. If front k is a leaf node, then +% Fr (k,4) is k. +% +% Ch is a (nchains+1)-by-3 array containing information about each "chain" +% (unifrontal sequence) of frontal matrices, and where nchains <= nfr +% is the number of chains. The ith chain consists of frontal matrices. +% Chain (i,1) to Chain (i+1,1)-1, and the largest front in chain i is +% Chain (i,2)-by-Chain (i,3). +% +% This use of umfpack is not required to factor or solve a linear system +% in MATLAB. It analyzes the matrix A and provides information only. +% The MATLAB statement "treeplot (Fr (:,2)')" plots the column elimination +% tree. +% +% Control = umfpack ; +% +% Returns a 20-by-1 vector of default parameter settings for umfpack. +% +% umfpack_report (Control, Info) ; +% +% Prints the current Control settings, and Info +% +% If present, Qinit is a user-supplied 1-by-n permutation vector. It is an +% initial fill-reducing column pre-ordering for A; if not present, then colamd +% or amd are used instead. If present, Control is a user-supplied 20-by-1 +% array. Control and Info are optional; if Control is not present, defaults +% are used. If a Control entry is NaN, then the default is used for that entry. +% +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. +% +% UMFPACK License: +% +% Your use or distribution of UMFPACK or any modified version of +% UMFPACK implies that you agree to this License. +% +% THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY +% EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +% +% Permission is hereby granted to use or copy this program, provided +% that the Copyright, this License, and the Availability of the original +% version is retained on all copies. User documentation of any code that +% uses UMFPACK or any modified version of UMFPACK code must cite the +% Copyright, this License, the Availability note, and "Used by permission." +% Permission to modify the code and to distribute modified code is granted, +% provided the Copyright, this License, and the Availability note are +% retained, and a notice that the code was modified is included. This +% software was developed with support from the National Science Foundation, +% and is provided to you free of charge. +% +% Availability: http://www.cise.ufl.edu/research/sparse/umfpack +% +% See also umfpack, umfpack_make, umfpack_report, +% umfpack_demo, and umfpack_simple. + +more on +help umfpack_details +more off + diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_make.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_make.m new file mode 100644 index 0000000000..ca239a9fe3 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_make.m @@ -0,0 +1,356 @@ +function umfpack_make +% UMFPACK_MAKE +% +% Compiles the UMFPACK mexFunction and then runs a simple demo. +% +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. Type umfpack_details for License. +% +% See also: umfpack, umfpack_details, umfpack_report, umfpack_demo, and +% umfpack_simple. + +help umfpack_make + +fprintf ('\n--------------------------------------------------------------\n') ; +fprintf ('Now compiling the UMFPACK and AMD mexFunctions.\n') ; +fprintf ('--------------------------------------------------------------\n') ; + +try + % ispc does not appear in MATLAB 5.3 + pc = ispc ; +catch + % if ispc fails, assume we aren't on a Windows PC. + pc = 0 ; +end + +obj = 'o' ; +blas_lib = '' ; +if (pc) + obj = 'obj' ; +end + +%------------------------------------------------------------------------------- +% BLAS option +%------------------------------------------------------------------------------- + +msg = [ ... + '\nUsing the BLAS is faster, but might not compile correctly.\n', ... + 'If you get an error stating that dgemm, dgemv, dger, zgemm,\n', ... + 'zgemv, and/or zger are not defined, then recompile without the\n', ... + 'BLAS. You can ignore warnings that these routines are implicitly\n', ... + 'declared.\n\nPlease select one of the following options: \n', ... + ' 1: attempt to compile with the BLAS (default)\n', ... + ' 2: do not use the BLAS\n'] ; +fprintf (msg) ; +blas = input (': ') ; +if (isempty (blas)) + blas = 1 ; +end +if (blas == 1) + % try to link to MATLAB's built-in BLAS + blas = '' ; + if (pc) + % the default lcc compiler needs this library to access the BLAS + blas_lib = ' libmwlapack.lib' ; + msg = [ ... + '\nCheck to see if you have a file called libmwlapack.lib in the\n', ... + '\\extern\\lib\\win32\\lcc\\ directory, where is ', ... + 'the\ndirectory where MATLAB is installed. If a file of that ', ... + 'name is already\nthere, then you don''t have to do anything. ', ... + 'Otherwise, you must first\ncopy the libmwlapack.lib file from ', ... + 'the umfpack\\lcc_lib\\ directory to the\n', ... + '\\extern\\lib\\win32\\lcc\\ directory. Next, type\n\n', ... + ' mex -setup\n\n', ... + 'at the MATLAB prompt, and ask MATLAB to select the lcc compiler. ',... + 'You can skip\nall of this if you have already done it, or have ', ... + 'configured mex to use\na different compiler. If you are using ', ... + 'Norton anti-virus software on Windows\n98SE, then you need to ', ... + 'exit MATLAB, turn off virus checking, and restart MATLAB\n', ... + 'before you can use the mex command or compile UMFPACK.\n', ... + 'You may also need to turn off virus checking in other cases.\n', ... + '\nHit enter to continue, or type control-C if you do not wish to '] ; + fprintf (msg) ; + input ('proceed: ') ; + end + fprintf ('\nUsing the BLAS (recommended).\n') ; +else + % No BLAS + fprintf ('\nNot using the BLAS. UMFPACK will be slow.\n') ; + blas = ' -DNBLAS' ; +end + +%------------------------------------------------------------------------------- +% -DNUTIL option (using utMalloc or mxMalloc) +%------------------------------------------------------------------------------- + +utils = '' ; + +if (~pc) + msg = [ ... + '--------------------------------------------------------------\n', ... + '\nUMFPACK uses MATLAB''s memory allocation routines. The internal', ... + '\nutMalloc, utFree, and utRealloc allow for better use of memory,', ... + '\nbut they are internal utility routines that are not documented.\n', ... + 'Thus, they might not always work. Using mxMalloc, mxFree, and\n', ... + 'mxRealloc works, but UMFPACK might run out of memory when solving\n', ... + 'problems that it could otherwise solve. Try using the default.\n', ... + 'If you get an error stating that utMalloc, utFree, and/or\n', ... + 'utRealloc are not defined, then recompile with the mx* routines.\n', ... + '\nPlease select one of the following options:\n', ... + ' 1: attempt to use the ut* routines (default)\n', ... + ' 2: use the standard mx* routines\n'] ; + fprintf (msg) ; + utils = input (': ') ; + if (isempty (utils)) + utils = 1 ; + end + if (utils == 2) + fprintf ('\nNot using utMalloc, utFree, or utRealloc\n') ; + utils = ' -DNUTIL' ; + else + fprintf ('\nUsing utMalloc, utFree, and utRealloc\n') ; + utils = '' ; + end +end + +%------------------------------------------------------------------------------- +% -DNPOSIX option (for sysconf and times timer routines) +%------------------------------------------------------------------------------- + +posix = '' ; + +if (~pc) + msg = [ ... + '--------------------------------------------------------------\n', ... + '\nUMFPACK can use the POSIX routines sysconf () and times ()\n', ... + 'to provide CPU time and wallclock time statistics. If you do not\n', ... + 'have a POSIX-compliant operating system, then UMFPACK won''t\n', ... + 'compile. If you don''t know which option to pick, try the\n', ... + 'default. If you get an error saying that sysconf and/or times\n', ... + 'are not defined, then recompile with the non-POSIX option.\n', ... + '\nPlease select one of the following options:\n', ... + ' 1: use POSIX sysconf and times routines (default)\n', ... + ' 2: do not use POSIX routines\n'] ; + fprintf (msg) ; + posix = input (': ') ; + if (isempty (posix)) + posix = 1 ; + end + if (posix == 2) + fprintf ('\nNot using POSIX sysconf and times routines.\n') ; + posix = ' -DNPOSIX' ; + else + fprintf ('\nUsing POSIX sysconf and times routines.\n') ; + posix = '' ; + end +end + +%------------------------------------------------------------------------------- +% mex command +%------------------------------------------------------------------------------- + +umfdir = sprintf ('..%sSource%s', filesep, filesep) ; +amddir = sprintf ('..%s..%sAMD%sSource%s', filesep, filesep, filesep, filesep) ; +incdir = sprintf ( ... +' -I..%sInclude -I..%sSource -I..%s..%sAMD%sInclude -I..%s..%sAMD%sSource', ... +filesep,filesep, filesep, filesep, filesep, filesep, filesep, filesep) ; + +mx = sprintf ('mex -inline -O%s%s%s%s', blas, utils, posix, incdir) ; +msg = [ ... + '--------------------------------------------------------------\n', ... + '\nCompile options:\n%s\nNow compiling. Please wait.\n'] ; +fprintf (msg, mx) ; + +% The following is adapted from GNUmakefile + +%------------------------------------------------------------------------------- +% source files +%------------------------------------------------------------------------------- + +% non-user-callable umf_*.[ch] files: +umfch = { 'assemble', 'blas3_update', ... + 'build_tuples', 'create_element', ... + 'dump', 'extend_front', 'garbage_collection', ... + 'get_memory', 'init_front', 'kernel', ... + 'kernel_init', 'kernel_wrapup', ... + 'local_search', 'lsolve', 'ltsolve', ... + 'mem_alloc_element', 'mem_alloc_head_block', ... + 'mem_alloc_tail_block', 'mem_free_tail_block', ... + 'mem_init_memoryspace', ... + 'report_vector', 'row_search', 'scale_column', ... + 'set_stats', 'solve', 'symbolic_usage', 'transpose', ... + 'tuple_lengths', 'usolve', 'utsolve', 'valid_numeric', ... + 'valid_symbolic', 'grow_front', 'start_front', '2by2', ... + 'store_lu', 'scale' } ; + +% non-user-callable umf_*.[ch] files, int versions only (no real/complex): +umfint = { 'analyze', 'apply_order', 'colamd', 'free', 'fsize', ... + 'is_permutation', 'malloc', 'realloc', 'report_perm', ... + 'singletons' } ; + +% non-user-callable and user-callable amd_*.[ch] files (int versions only): +amd = { 'aat', '1', '2', 'dump', 'postorder', 'post_tree', 'defaults', ... + 'order', 'control', 'info', 'valid' } ; + +% user-callable umfpack_*.[ch] files (real/complex): +user = { 'col_to_triplet', 'defaults', 'free_numeric', ... + 'free_symbolic', 'get_numeric', 'get_lunz', ... + 'get_symbolic', 'numeric', 'qsymbolic', ... + 'report_control', 'report_info', 'report_matrix', ... + 'report_numeric', 'report_perm', 'report_status', ... + 'report_symbolic', 'report_triplet', ... + 'report_vector', 'solve', 'symbolic', ... + 'transpose', 'triplet_to_col', 'scale' ... + 'load_numeric', 'save_numeric', 'load_symbolic', 'save_symbolic' } ; + +% user-callable umfpack_*.[ch], only one version +generic = { 'timer', 'tictoc' } ; + +M = cell (0) ; + +%------------------------------------------------------------------------------- +% Create the umfpack and amd mexFunctions for MATLAB (int versions only) +%------------------------------------------------------------------------------- + +for k = 1:length(umfint) + M = make (M, '%s -DDINT -c %sumf_%s.c', 'umf_%s.%s', 'umf_%s_%s.%s', ... + mx, umfint {k}, umfint {k}, 'm', obj, umfdir) ; +end + +rules = { [mx ' -DDINT'] , [mx ' -DZINT'] } ; +kinds = { 'md', 'mz' } ; + +for what = 1:2 + + rule = rules {what} ; + kind = kinds {what} ; + + M = make (M, '%s -DCONJUGATE_SOLVE -c %sumf_%s.c', 'umf_%s.%s', ... + 'umf_%s_%s.%s', rule, 'ltsolve', 'lhsolve', kind, obj, umfdir) ; + + M = make (M, '%s -DCONJUGATE_SOLVE -c %sumf_%s.c', 'umf_%s.%s', ... + 'umf_%s_%s.%s', rule, 'utsolve', 'uhsolve', kind, obj, umfdir) ; + + M = make (M, '%s -DDO_MAP -c %sumf_%s.c', 'umf_%s.%s', ... + 'umf_%s_%s_map_nox.%s', rule, 'triplet', 'triplet', kind, obj, umfdir) ; + + M = make (M, '%s -DDO_VALUES -c %sumf_%s.c', 'umf_%s.%s', ... + 'umf_%s_%s_nomap_x.%s', rule, 'triplet', 'triplet', kind, obj, umfdir) ; + + M = make (M, '%s -c %sumf_%s.c', 'umf_%s.%s', ... + 'umf_%s_%s_nomap_nox.%s', rule, 'triplet', 'triplet', kind, obj, ... + umfdir) ; + + M = make (M, '%s -DDO_MAP -DDO_VALUES -c %sumf_%s.c', 'umf_%s.%s', ... + 'umf_%s_%s_map_x.%s', rule, 'triplet', 'triplet', kind, obj, umfdir) ; + + M = make (M, '%s -DFIXQ -c %sumf_%s.c', 'umf_%s.%s', ... + 'umf_%s_%s_fixq.%s', rule, 'assemble', 'assemble', kind, obj, umfdir) ; + + M = make (M, '%s -DDROP -c %sumf_%s.c', 'umf_%s.%s', ... + 'umf_%s_%s_drop.%s', rule, 'store_lu', 'store_lu', kind, obj, umfdir) ; + + for k = 1:length(umfch) + M = make (M, '%s -c %sumf_%s.c', 'umf_%s.%s', 'umf_%s_%s.%s', ... + rule, umfch {k}, umfch {k}, kind, obj, umfdir) ; + end + + M = make (M, '%s -DWSOLVE -c %sumfpack_%s.c', 'umfpack_%s.%s', ... + 'umfpack_%s_w%s.%s', rule, 'solve', 'solve', kind, obj, umfdir) ; + + for k = 1:length(user) + M = make (M, '%s -c %sumfpack_%s.c', 'umfpack_%s.%s', ... + 'umfpack_%s_%s.%s', rule, user {k}, user {k}, kind, obj, umfdir) ; + end +end + +for k = 1:length(generic) + M = make (M, '%s -c %sumfpack_%s.c', 'umfpack_%s.%s', ... + 'umfpack_%s_%s.%s', mx, generic {k}, generic {k}, 'm', obj, umfdir) ; +end + +%---------------------------------------- +% AMD routines (int only) +%---------------------------------------- + +for k = 1:length(amd) + M = make (M, '%s -DDINT -c %samd_%s.c', 'amd_%s.%s', 'amd_%s_%s.%s', ... + mx, amd {k}, amd {k}, 'm', obj, amddir) ; +end + +%---------------------------------------- +% compile the umfpack mexFunction +%---------------------------------------- + +C = sprintf ('%s -output umfpack umfpackmex.c', mx) ; +for i = 1:length (M) + C = [C ' ' (M {i})] ; +end +C = [C ' ' blas_lib] ; +cmd (C) ; + +%---------------------------------------- +% delete the object files +%---------------------------------------- + +for i = 1:length (M) + rmfile (M {i}) ; +end + +%---------------------------------------- +% compile the luflop mexFunction +%---------------------------------------- + +cmd (sprintf ('%s -output luflop luflopmex.c', mx)) ; + +fprintf ('\n\nCompilation has completed. Now trying the umfpack_simple demo.\n'); +umfpack_simple + +%------------------------------------------------------------------------------- +% rmfile: delete a file, but only if it exists +%------------------------------------------------------------------------------- + +function rmfile (file) +if (length (dir (file)) > 0) + delete (file) ; +end + +%------------------------------------------------------------------------------- +% cpfile: copy the src file to the filename dst, overwriting dst if it exists +%------------------------------------------------------------------------------- + +function cpfile (src, dst) +rmfile (dst) +if (length (dir (src)) == 0) + help umfpack_make + error (sprintf ('File does not exist: %s\n', src)) ; +end +copyfile (src, dst) ; + +%------------------------------------------------------------------------------- +% mvfile: move the src file to the filename dst, overwriting dst if it exists +%------------------------------------------------------------------------------- + +function mvfile (src, dst) +cpfile (src, dst) ; +rmfile (src) ; + +%------------------------------------------------------------------------------- +% cmd: display and execute a command +%------------------------------------------------------------------------------- + +function cmd (s) +fprintf ('.') ; +eval (s) ; + +%------------------------------------------------------------------------------- +% make: execute a "make" command for a source file +%------------------------------------------------------------------------------- + +function M = make (M, s, src, dst, rule, file1, file2, kind, obj, srcdir) +cmd (sprintf (s, rule, srcdir, file1)) ; +src = sprintf (src, file1, obj) ; +dst = sprintf (dst, kind, file2, obj) ; +mvfile (src, dst) ; +M {end + 1} = dst ; + diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_report.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_report.m new file mode 100644 index 0000000000..10d661a1dc --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_report.m @@ -0,0 +1,346 @@ +function umfpack_report (Control, Info) +% UMFPACK_REPORT +% +% umfpack_report (Control, Info) ; +% +% Prints the current Control settings for umfpack, and the statistical +% information returned by umfpack in the Info array. If Control is +% an empty matrix, then the default control settings are printed. +% +% Control is 20-by-1, and Info is 90-by-1. Not all entries are used. +% +% Alternative usages: +% +% umfpack_report ([ ], Info) ; print the default control parameters +% and the Info array. +% umfpack_report (Control) ; print the control parameters only. +% umfpack_report ; print the default control parameters +% and an empty Info array. +% +% See also umfpack, umfpack_make, umfpack_details, +% umfpack_demo, and umfpack_simple. + +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. See ../README for License. + +%------------------------------------------------------------------------------- +% get inputs, use defaults if input arguments not present +%------------------------------------------------------------------------------- + +% The contents of Control and Info are defined in umfpack.h +if (nargin < 1) + Control = [] ; +end +if (nargin < 2) + Info = [] ; +end +if (isempty (Control)) + Control = umfpack ; +end +if (isempty (Info)) + Info = [ 0 (-ones (1, 89)) ] ; +end + +%------------------------------------------------------------------------------- +% control settings +%------------------------------------------------------------------------------- + +fprintf ('\nUMFPACK Version 4.3: Control settings:\n\n') ; +fprintf (' Control (1): print level: %d\n', Control (1)) ; +fprintf (' Control (2): dense row parameter: %g\n', Control (2)) ; +fprintf (' "dense" rows have > max (16, (%g)*16*sqrt(n_col)) entries\n', Control (2)) ; +fprintf (' Control (3): dense column parameter: %g\n', Control (3)) ; +fprintf (' "dense" columns have > max (16, (%g)*16*sqrt(n_row)) entries\n', Control (3)) ; +fprintf (' Control (4): pivot tolerance: %g\n', Control (4)) ; +fprintf (' Control (5): max block size for dense matrix kernels: %d\n', Control (5)) ; +prstrat (' Control (6): strategy: %g ', Control (6)) ; +fprintf (' Control (7): initial allocation ratio: %g\n', Control (7)) ; +fprintf (' Control (8): max iterative refinement steps: %d\n', Control (8)) ; +fprintf (' Control (13): 2-by-2 pivot tolerance: %g\n', Control (13)) ; +fprintf (' Control (14): Q fixed during numeric factorization: %g ', Control (14)) ; +if (Control (14) > 0) + fprintf ('(yes)\n') ; +elseif (Control (14) < 0) + fprintf ('(no)\n') ; +else + fprintf ('(auto)\n') ; +end +fprintf (' Control (15): AMD dense row/column parameter: %g\n', Control (15)) ; +fprintf (' "dense" rows/columns in A+A'' have > max (16, (%g)*sqrt(n)) entries.\n', Control (15)) ; +fprintf (' Only used if the AMD ordering is used.\n') ; +fprintf (' Control (16): diagonal pivot tolerance: %g\n', Control (16)) ; +fprintf (' Only used if diagonal pivoting is attempted.\n') ; + +fprintf (' Control (17): scaling option: %g ', Control (17)) ; +if (Control (17) == 0) + fprintf ('(none)\n') ; +elseif (Control (17) == 2) + fprintf ('(scale the matrix by\n') ; + fprintf (' dividing each row by max. abs. value in each row)\n') ; +else + fprintf ('(scale the matrix by\n') ; + fprintf (' dividing each row by sum of abs. values in each row)\n') ; +end + +fprintf (' Control (18): frontal matrix allocation ratio: %g\n', Control (18)) ; +fprintf (' Control (19): drop tolerance: %g\n', Control (19)) ; +fprintf (' Control (20): AMD and COLAMD aggressive absorption: %g ', Control (20)) ; +yes_no (Control (20)) ; + +% compile-time options: + +fprintf ('\n The following options can only be changed at compile-time:\n') ; + +if (Control (9) == 1) + fprintf (' Control (9): compiled to use the BLAS\n') ; +else + fprintf (' Control (9): compiled without the BLAS\n') ; + fprintf (' (you will not get the best possible performance)\n') ; +end + +if (Control (10) == 1) + fprintf (' Control (10): compiled for MATLAB\n') ; +elseif (Control (10) == 2) + fprintf (' Control (10): compiled for MATLAB\n') ; + fprintf (' Uses internal utMalloc, utFree, utRealloc, utPrintf\n') ; + fprintf (' utDivideComplex, and utFdlibm_hypot routines.\n') ; +else + fprintf (' Control (10): not compiled for MATLAB\n') ; + fprintf (' Uses ANSI C malloc, free, realloc, and printf\n') ; + fprintf (' instead of mxMalloc, mxFree, mxRealloc, and mexPrintf.\n') ; + fprintf (' Printing will be in terms of 0-based matrix indexing,\n') ; + fprintf (' not 1-based as is expected in MATLAB. Diary output may\n') ; + fprintf (' not be properly recorded.\n') ; +end + +if (Control (11) == 2) + fprintf (' Control (11): uses POSIX times ( ) to get CPU time and wallclock time.\n') ; +elseif (Control (11) == 1) + fprintf (' Control (11): uses getrusage to get CPU time.\n') ; +else + fprintf (' Control (11): uses ANSI C clock to get CPU time.\n') ; + fprintf (' The CPU time may wrap around, type "help cputime".\n') ; +end + +if (Control (12) == 1) + fprintf (' Control (12): compiled with debugging enabled\n') ; + fprintf (' ###########################################\n') ; + fprintf (' ### This will be exceedingly slow! ########\n') ; + fprintf (' ###########################################\n') ; + if (Control (10) == 1) + fprintf (' Uses mxAssert.\n') ; + elseif (Control (10) == 2) + fprintf (' Uses utAssert.\n') ; + else + fprintf (' Uses ANSI C assert instead of mxAssert.\n') ; + end +else + fprintf (' Control (12): compiled for normal operation (no debugging)\n') ; +end + +%------------------------------------------------------------------------------- +% Info: +%------------------------------------------------------------------------------- + +if (nargin == 1) + return +end + +status = Info (1) ; +fprintf ('\nUMFPACK status: Info (1): %d, ', status) ; + +if (status == 0) + fprintf ('OK\n') ; +elseif (status == 1) + fprintf ('WARNING matrix is singular\n') ; +elseif (status == -1) + fprintf ('ERROR out of memory\n') ; +elseif (status == -3) + fprintf ('ERROR numeric LU factorization is invalid\n') ; +elseif (status == -4) + fprintf ('ERROR symbolic LU factorization is invalid\n') ; +elseif (status == -5) + fprintf ('ERROR required argument is missing\n') ; +elseif (status == -6) + fprintf ('ERROR n <= 0\n') ; +elseif (status <= -7 & status >= -12 | status == -14) + fprintf ('ERROR matrix A is corrupted\n') ; +elseif (status == -13) + fprintf ('ERROR invalid system\n') ; +elseif (status == -15) + fprintf ('ERROR invalid permutation\n') ; +elseif (status == -911) + fprintf ('ERROR internal error!\n') ; + fprintf ('Please report this error to Tim Davis (davis@cise.ufl.edu)\n') ; +else + fprintf ('ERROR unrecognized error. Info array corrupted\n') ; +end + +fprintf (' (a -1 means the entry has not been computed):\n') ; + +fprintf ('\n Basic statistics:\n') ; +fprintf (' Info (2): %d, # of rows of A\n', Info (2)) ; +fprintf (' Info (17): %d, # of columns of A\n', Info (17)) ; +fprintf (' Info (3): %d, nnz (A)\n', Info (3)) ; +fprintf (' Info (4): %d, Unit size, in bytes, for memory usage reported below\n', Info (4)) ; +fprintf (' Info (5): %d, size of int (in bytes)\n', Info (5)) ; +fprintf (' Info (6): %d, size of long (in bytes)\n', Info (6)) ; +fprintf (' Info (7): %d, size of pointer (in bytes)\n', Info (7)) ; +fprintf (' Info (8): %d, size of numerical entry (in bytes)\n', Info (8)) ; + +fprintf ('\n Pivots with zero Markowitz cost removed to obtain submatrix S:\n') ; +fprintf (' Info (57): %d, # of pivots with one entry in pivot column\n', Info (57)) ; +fprintf (' Info (58): %d, # of pivots with one entry in pivot row\n', Info (58)) ; +fprintf (' Info (59): %d, # of rows/columns in submatrix S (if square)\n', Info (59)) ; +fprintf (' Info (60): %d ') ; +if (Info (60) > 0) + fprintf ('submatrix S square and diagonal preserved\n') ; +elseif (Info (60) == 0) + fprintf ('submatrix S not square or diagonal not preserved\n') ; +else + fprintf ('\n') ; +end +fprintf (' Info (9): %d, # of "dense" rows in S\n', Info (9)) ; +fprintf (' Info (10): %d, # of empty rows in S\n', Info (10)) ; +fprintf (' Info (11): %d, # of "dense" columns in S\n', Info (11)) ; +fprintf (' Info (12): %d, # of empty columns in S\n', Info (12)) ; +fprintf (' Info (34): %g, symmetry of pattern of S\n', Info (34)) ; +fprintf (' Info (35): %d, # of off-diagonal nonzeros in S+S''\n', Info (35)) ; +fprintf (' Info (36): %d, nnz (diag (S))\n', Info (36)) ; + +fprintf ('\n 2-by-2 pivoting to place large entries on diagonal:\n') ; +fprintf (' Info (52): %d, # of small diagonal entries of S\n', Info (52)) ; +fprintf (' Info (53): %d, # of unmatched small diagonal entries\n', Info (53)) ; +fprintf (' Info (54): %g, symmetry of P2*S\n', Info (54)) ; +fprintf (' Info (55): %d, # of off-diagonal entries in (P2*S)+(P2*S)''\n', Info (55)) ; +fprintf (' Info (56): %d, nnz (diag (P2*S))\n', Info (56)) ; + +fprintf ('\n AMD results, for strict diagonal pivoting:\n') ; +fprintf (' Info (37): %d, est. nz in L and U\n', Info (37)) ; +fprintf (' Info (38): %g, est. flop count\n', Info (38)) ; +fprintf (' Info (39): %g, # of "dense" rows in S+S''\n', Info (39)) ; +fprintf (' Info (40): %g, est. max. nz in any column of L\n', Info (40)) ; + +fprintf ('\n Final strategy selection, based on the analysis above:\n') ; +prstrat (' Info (19): %d, strategy used ', Info (19)) ; +fprintf (' Info (20): %d, ordering used ', Info (20)) ; +if (Info (20) == 0) + fprintf ('(COLAMD on A)\n') ; +elseif (Info (20) == 1) + fprintf ('(AMD on A+A'')\n') ; +elseif (Info (20) == 2) + fprintf ('(provided by user)\n') ; +else + fprintf ('(undefined ordering option)\n') ; +end +fprintf (' Info (32): %d, Q fixed during numeric factorization: ', Info (32)) ; +yes_no (Info (32)) ; +fprintf (' Info (33): %d, prefer diagonal pivoting: ', Info (33)) ; +yes_no (Info (33)) ; + +fprintf ('\n symbolic analysis time and memory usage:\n') ; +fprintf (' Info (13): %d, defragmentations during symbolic analysis\n', Info (13)) ; +fprintf (' Info (14): %d, memory used during symbolic analysis (Units)\n', Info (14)) ; +fprintf (' Info (15): %d, final size of symbolic factors (Units)\n', Info (15)) ; +fprintf (' Info (16): %.2f, symbolic analysis CPU time (seconds)\n', Info (16)) ; +fprintf (' Info (18): %.2f, symbolic analysis wall clock time (seconds)\n', Info (18)) ; + +fprintf ('\n Estimates computed in the symbolic analysis:\n') ; +fprintf (' Info (21): %d, est. size of LU factors (Units)\n', Info (21)) ; +fprintf (' Info (22): %d, est. total peak memory usage (Units)\n', Info (22)) ; +fprintf (' Info (23): %d, est. factorization flop count\n', Info (23)) ; +fprintf (' Info (24): %d, est. nnz (L)\n', Info (24)) ; +fprintf (' Info (25): %d, est. nnz (U)\n', Info (25)) ; +fprintf (' Info (26): %d, est. initial size, variable-part of LU (Units)\n', Info (26)) ; +fprintf (' Info (27): %d, est. peak size, of variable-part of LU (Units)\n', Info (27)) ; +fprintf (' Info (28): %d, est. final size, of variable-part of LU (Units)\n', Info (28)) ; +fprintf (' Info (29): %d, est. max frontal matrix size (# of entries)\n', Info (29)) ; +fprintf (' Info (30): %d, est. max # of rows in frontal matrix\n', Info (30)) ; +fprintf (' Info (31): %d, est. max # of columns in frontal matrix\n', Info (31)) ; + +fprintf ('\n Computed in the numeric factorization (estimates shown above):\n') ; +fprintf (' Info (41): %d, size of LU factors (Units)\n', Info (41)) ; +fprintf (' Info (42): %d, total peak memory usage (Units)\n', Info (42)) ; +fprintf (' Info (43): %d, factorization flop count\n', Info (43)) ; +fprintf (' Info (44): %d, nnz (L)\n', Info (44)) ; +fprintf (' Info (45): %d, nnz (U)\n', Info (45)) ; +fprintf (' Info (46): %d, initial size of variable-part of LU (Units)\n', Info (46)) ; +fprintf (' Info (47): %d, peak size of variable-part of LU (Units)\n', Info (47)) ; +fprintf (' Info (48): %d, final size of variable-part of LU (Units)\n', Info (48)) ; +fprintf (' Info (49): %d, max frontal matrix size (# of numerical entries)\n', Info (49)) ; +fprintf (' Info (50): %d, max # of rows in frontal matrix\n', Info (50)) ; +fprintf (' Info (51): %d, max # of columns in frontal matrix\n', Info (51)) ; + +fprintf ('\n Computed in the numeric factorization (no estimates computed a priori):\n') ; +fprintf (' Info (61): %d, defragmentations during numeric factorization\n', Info (61)) ; +fprintf (' Info (62): %d, reallocations during numeric factorization\n', Info (62)) ; +fprintf (' Info (63): %d, costly reallocations during numeric factorization\n', Info (63)) ; +fprintf (' Info (64): %d, integer indices in compressed pattern of L and U\n', Info (64)) ; +fprintf (' Info (65): %d, numerical values stored in L and U\n', Info (65)) ; +fprintf (' Info (66): %.2f, numeric factorization CPU time (seconds)\n', Info (66)) ; +fprintf (' Info (76): %.2f, numeric factorization wall clock time (seconds)\n', Info (76)) ; +if (Info (66) > 0.05 & Info (43) > 0) +fprintf (' mflops in numeric factorization phase: %.2f\n', 1e-6 * Info (43) / Info (66)) ; +end +fprintf (' Info (67): %d, nnz (diag (U))\n', Info (67)) ; +fprintf (' Info (68): %g, reciprocal condition number estimate\n', Info (68)) ; +fprintf (' Info (69): %g, matrix was ', Info (69)) ; +if (Info (69) == 0) + fprintf ('not scaled\n') ; +elseif (Info (69) == 2) + fprintf ('scaled (row max)\n') ; +else + fprintf ('scaled (row sum)\n') ; +end +fprintf (' Info (70): %g, min. scale factor of rows of A\n', Info (70)) ; +fprintf (' Info (71): %g, max. scale factor of rows of A\n', Info (71)) ; +fprintf (' Info (72): %g, min. abs. on diagonal of U\n', Info (72)) ; +fprintf (' Info (73): %g, max. abs. on diagonal of U\n', Info (73)) ; +fprintf (' Info (74): %g, initial allocation parameter used\n', Info (74)) ; +fprintf (' Info (75): %g, # of forced updates due to frontal growth\n', Info (75)) ; +fprintf (' Info (77): %d, # of off-diaogonal pivots\n', Info (77)) ; +fprintf (' Info (78): %d, nnz (L), if no small entries dropped\n', Info (78)) ; +fprintf (' Info (79): %d, nnz (U), if no small entries dropped\n', Info (79)) ; +fprintf (' Info (80): %d, # of small entries dropped\n', Info (80)) ; + +fprintf ('\n Computed in the solve step:\n') ; +fprintf (' Info (81): %d, iterative refinement steps taken\n', Info (81)) ; +fprintf (' Info (82): %d, iterative refinement steps attempted\n', Info (82)) ; +fprintf (' Info (83): %g, omega(1), sparse-backward error estimate\n', Info (83)) ; +fprintf (' Info (84): %g, omega(2), sparse-backward error estimate\n', Info (84)) ; +fprintf (' Info (85): %d, solve flop count\n', Info (85)) ; +fprintf (' Info (86): %.2f, solve CPU time (seconds)\n', Info (86)) ; +fprintf (' Info (87): %.2f, solve wall clock time (seconds)\n', Info (87)) ; + +fprintf ('\n Info (88:90): unused\n\n') ; + +%------------------------------------------------------------------------------- + +function prstrat (fmt, strategy) +fprintf (fmt, strategy) ; +if (strategy == 1) + fprintf ('(unsymmetric)\n') ; + fprintf (' Q = COLAMD (A), Q refined during numerical\n') ; + fprintf (' factorization, and no attempt at diagonal pivoting.\n') ; +elseif (strategy == 2) + fprintf ('(symmetric, with 2-by-2 pivoting)\n') ; + fprintf (' P2 = row permutation to place large values on the diagonal\n') ; + fprintf (' Q = AMD (P2*A+(P2*A)''), Q not refined during numeric factorization,\n') ; + fprintf (' and diagonal pivoting attempted.\n') ; +elseif (strategy == 3) + fprintf ('(symmetric)\n') ; + fprintf (' Q = AMD (A+A''), Q not refined during numeric factorization,\n') ; + fprintf (' and diagonal pivoting (P=Q'') attempted.\n') ; +else + strategy = 0 ; + fprintf ('(auto)\n') ; +end + +%------------------------------------------------------------------------------- + +function yes_no (s) +if (s == 0) + fprintf ('(no)\n') ; +else + fprintf ('(yes)\n') ; +end diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_simple.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_simple.m new file mode 100644 index 0000000000..829b6718a4 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_simple.m @@ -0,0 +1,61 @@ +% umfpack_simple: a simple demo of UMFPACK +% +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. +% +% UMFPACK License: +% +% Your use or distribution of UMFPACK or any modified version of +% UMFPACK implies that you agree to this License. +% +% THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY +% EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. +% +% Permission is hereby granted to use or copy this program, provided +% that the Copyright, this License, and the Availability of the original +% version is retained on all copies. User documentation of any code that +% uses UMFPACK or any modified version of UMFPACK code must cite the +% Copyright, this License, the Availability note, and "Used by permission." +% Permission to modify the code and to distribute modified code is granted, +% provided the Copyright, this License, and the Availability note are +% retained, and a notice that the code was modified is included. This +% software was developed with support from the National Science Foundation, +% and is provided to you free of charge. +% +% Availability: http://www.cise.ufl.edu/research/sparse/umfpack +% +% See also: umfpack, umfpack_details + +help umfpack_simple +i = input ('Hit enter to agree to the above License: ', 's') ; +if (~isempty (i)) + error ('terminating') ; +end + +format short + +A = [ + 2 3 0 0 0 + 3 0 4 0 6 + 0 -1 -3 2 0 + 0 0 1 0 0 + 0 4 2 0 1 +] + +A = sparse (A) ; + +b = [8 45 -3 3 19]' + +fprintf ('Solution to Ax=b via UMFPACK:\n') ; +fprintf ('x1 = umfpack (A, ''\\'', b)\n') ; + +x1 = umfpack (A, '\', b) + +fprintf ('Solution to Ax=b via MATLAB:\n') ; +fprintf ('x2 = A\\b\n') ; + +x2 = A\b + +fprintf ('norm (x1-x2) should be small: %g\n', norm (x1-x2)) ; + +fprintf ('Type ''umfpack_demo'' for a full demo of UMFPACK\n') ; diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_simple.m.out b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_simple.m.out new file mode 100644 index 0000000000..6c019147ad --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_simple.m.out @@ -0,0 +1,74 @@ +>> umfpack_simple + + umfpack_simple: a simple demo of UMFPACK + + UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. + Davis. All Rights Reserved. + + UMFPACK License: + + Your use or distribution of UMFPACK or any modified version of + UMFPACK implies that you agree to this License. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY + EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to use or copy this program, provided + that the Copyright, this License, and the Availability of the original + version is retained on all copies. User documentation of any code that + uses UMFPACK or any modified version of UMFPACK code must cite the + Copyright, this License, the Availability note, and "Used by permission." + Permission to modify the code and to distribute modified code is granted, + provided the Copyright, this License, and the Availability note are + retained, and a notice that the code was modified is included. This + software was developed with support from the National Science Foundation, + and is provided to you free of charge. + + Availability: http://www.cise.ufl.edu/research/sparse/umfpack + + See also: umfpack, umfpack_details + +Hit enter to agree to the above License: + +A = + + 2 3 0 0 0 + 3 0 4 0 6 + 0 -1 -3 2 0 + 0 0 1 0 0 + 0 4 2 0 1 + + +b = + + 8 + 45 + -3 + 3 + 19 + +Solution to Ax=b via UMFPACK: +x1 = umfpack (A, '\', b) + +x1 = + + 1.0000 + 2.0000 + 3.0000 + 4.0000 + 5.0000 + +Solution to Ax=b via MATLAB: +x2 = A\b + +x2 = + + 1.0000 + 2.0000 + 3.0000 + 4.0000 + 5.0000 + +norm (x1-x2) should be small: 1.28037e-15 +Type 'umfpack_demo' for a full demo of UMFPACK +>> diary off diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_solve.m b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_solve.m new file mode 100644 index 0000000000..1a988c5833 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpack_solve.m @@ -0,0 +1,96 @@ +function x = umfpack_solve (arg1, op, arg2, Control) +% UMFPACK_SOLVE +% +% x = umfpack_solve (A, '\', b, Control) +% x = umfpack_solve (b, '/', A, Control) +% +% Computes x = A\b, or b/A, where A is square. Uses UMFPACK if A is sparse. +% The Control argument is optional. +% +% See also umfpack, umfpack_make, umfpack_details, umfpack_report, +% and umfpack_simple. + +% UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. +% Davis. All Rights Reserved. Type umfpack_details for License. + +%------------------------------------------------------------------------------- +% check inputs and get default control parameters +%------------------------------------------------------------------------------- + +if (op == '\') + A = arg1 ; + b = arg2 ; +elseif (op == '/') + A = arg2 ; + b = arg1 ; +else + help umfack_solve + error ('umfpack_solve: unrecognized operator') ; +end + +[m n] = size (A) ; +if (m ~= n) + help umfpack_solve + error ('umfpack_solve: A must be square') ; +end + +[m1 n1] = size (b) ; +if ((op == '\' & n ~= m1) | (op == '/' & n1 ~= m)) + help umfpack_solve + error ('umfpack_solve: b has the wrong dimensions') ; +end + +if (nargin < 4) + Control = umfpack ; +end + +%------------------------------------------------------------------------------- +% solve the system +%------------------------------------------------------------------------------- + +if (op == '\') + + if (~issparse (A)) + + % A is not sparse, so just use MATLAB + x = A\b ; + + elseif (n1 == 1 & ~issparse (b)) + + % the UMFPACK '\' requires b to be a dense column vector + x = umfpack (A, '\', b, Control) ; + + else + + % factorize with UMFPACK and do the forward/back solves in MATLAB + [L, U, P, Q, R] = umfpack (A, Control) ; + x = Q * (U \ (L \ (P * (R \ b)))) ; + + end + +else + + if (~issparse (A)) + + % A is not sparse, so just use MATLAB + x = b/A ; + + elseif (m1 == 1 & ~issparse (b)) + + % the UMFPACK '\' requires b to be a dense column vector + x = umfpack (b, '/', A, Control) ; + + else + + % factorize with UMFPACK and do the forward/back solves in MATLAB + % this mimics the behavior of x = b/A, except for the row scaling + [L, U, P, Q, R] = umfpack (A.', Control) ; + x = (Q * (U \ (L \ (P * (R \ (b.')))))).' ; + + % an alternative method: + % [L, U, P, Q, r] = umfpack (A, Control) ; + % x = (R \ (P' * (L.' \ (U.' \ (Q' * b.'))))).' ; + + end + +end diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpackmex.c b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpackmex.c new file mode 100644 index 0000000000..8e64a7e422 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/MATLAB/umfpackmex.c @@ -0,0 +1,1284 @@ +/* ========================================================================== */ +/* === UMFPACK mexFunction ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ +/* Minor revision, Aug 13, 2003 (version 4.1.1). Removed call to spparms. */ +/* -------------------------------------------------------------------------- */ + +/* + MATLAB interface for umfpack. + + Factor or solve a sparse linear system, returning either the solution + x to Ax=b or A'x'=b', or the factorization LU=P(R\A)Q or LU=PAQ. A must be + sparse, with nonzero dimensions, but it may be complex, singular, and/or + rectangular. b must be a dense n-by-1 vector (real or complex). + L is unit lower triangular, U is upper triangular, and R is diagonal. + P and Q are permutation matrices (permutations of an identity matrix). + + This has changed since v4.0. The outputs P and Q are returned as + permutation matrices instead of permutation vectors, and the row scale + factor r is also returned. v4.0 did not scale the matrix. + + The matrix A is scaled, by default. Each row i is divided by r (i), where + r (i) is the sum of the absolute values of the entries in that row. The + scaled matrix has an infinity norm of 1. The scale factors r (i) are + returned in a diagonal sparse matrix. If the factorization is: + + [L, U, P, Q, R] = umfpack (A) ; + + then the factorization is + + L*U = P * (R \ A) * Q + + This is safer than returning a matrix R such that L*U = P*R*A*Q, because + it avoids the division by small entries. If r(i) is subnormal, multiplying + by 1/r(i) would result in an IEEE Infinity, but dividing by r(i) is safe. + + The factorization + + [L, U, P, Q] = umfpack (A) ; + + returns LU factors such that L*U = P*A*Q, with no scaling. + + See umfpack.m and umfpack.h for details. + + Note that this mexFunction accesses only the user-callable UMFPACK routines. + Thus, is also provides another example of how user C code can access + UMFPACK. + + If NO_TRANSPOSE_FORWARD_SLASH is not defined at compile time, then the + forward slash (/) operator acts almost like x = b/A in MATLAB 6.1. It is + solved by factorizing the array transpose, and then x = (A.'\b.').' is + solved. This is the default behavior (for historical reasons), since + factorizing A can behave perform much differently than factorizing its + transpose. + + If NO_TRANSPOSE_FORWARD_SLASH is defined at compile time, then the forward + slash operator does not act like x=b/A in MATLAB 6.1. It is solved by + factorizing A, and then solving via the transposed L and U matrices. + The solution is still x = (A.'\b.').', except that A is factorized instead + of A.'. +*/ + +#include "umfpack.h" +#include "mex.h" +#include "matrix.h" +#include +#include +#include + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define STRING_MATCH(s1,s2) (strcmp ((s1), (s2)) == 0) +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef FALSE +#define FALSE (0) +#endif + +/* ========================================================================== */ +/* === error ================================================================ */ +/* ========================================================================== */ + +/* Return an error message */ + +static void error +( + char *s, + int A_is_complex, + int nargout, + mxArray *pargout [ ], + double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO], + int status, + int do_info +) +{ + int i ; + double *OutInfo ; + if (A_is_complex) + { + umfpack_zi_report_status (Control, status) ; + umfpack_zi_report_info (Control, Info) ; + } + else + { + umfpack_di_report_status (Control, status) ; + umfpack_di_report_info (Control, Info) ; + } + if (do_info > 0) + { + /* return Info */ + pargout [do_info] = mxCreateDoubleMatrix (1, UMFPACK_INFO, mxREAL) ; + OutInfo = mxGetPr (pargout [do_info]) ; + for (i = 0 ; i < UMFPACK_INFO ; i++) + { + OutInfo [i] = Info [i] ; + } + } + mexErrMsgTxt (s) ; +} + + +/* ========================================================================== */ +/* === UMFPACK ============================================================== */ +/* ========================================================================== */ + +void mexFunction +( + int nargout, /* number of outputs */ + mxArray *pargout [ ], /* output arguments */ + int nargin, /* number of inputs */ + const mxArray *pargin [ ] /* input arguments */ +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + void *Symbolic, *Numeric ; + int *Lp, *Li, *Up, *Ui, *Ap, *Ai, *P, *Q, do_solve, lnz, unz, nn, i, + transpose, size, do_info, do_numeric, *Front_npivcol, op, k, *Rp, *Ri, + *Front_parent, *Chain_start, *Chain_maxrows, *Chain_maxcols, nz, status, + nfronts, nchains, *Ltp, *Ltj, *Qinit, print_level, status2, no_scale, + *Front_1strow, *Front_leftmostdesc, n_row, n_col, n_inner, sys, + ignore1, ignore2, ignore3, A_is_complex, B_is_complex, X_is_complex, + *Pp, *Pi, *Qp, *Qi, do_recip ; + double *Lx, *Lz, *Ux, *Uz, *Ax, *Az, *Bx, *Bz, *Xx, *Xz, *User_Control, + *p, *q, Info [UMFPACK_INFO], Control [UMFPACK_CONTROL], *OutInfo, + *p1, *p2, *p3, *p4, *Ltx, *Ltz, *Rs, *Px, *Qx ; + mxArray *Amatrix, *Bmatrix, *User_Control_matrix, *User_Qinit ; + char *operator, *operation ; + mxComplexity Atype, Xtype ; + char warning [200] ; + +#ifndef NO_TRANSPOSE_FORWARD_SLASH + int *Cp, *Ci ; + double *Cx, *Cz ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get inputs A, b, and the operation to perform */ + /* ---------------------------------------------------------------------- */ + + User_Control_matrix = (mxArray *) NULL ; + User_Qinit = (mxArray *) NULL ; + + do_info = 0 ; + do_solve = FALSE ; + do_numeric = TRUE ; + transpose = FALSE ; + no_scale = FALSE ; + + /* find the operator */ + op = 0 ; + for (i = 0 ; i < nargin ; i++) + { + if (mxIsChar (pargin [i])) + { + op = i ; + break ; + } + } + + if (op > 0) + { + operator = mxArrayToString (pargin [op]) ; + + if (STRING_MATCH (operator, "\\")) + { + + /* -------------------------------------------------------------- */ + /* matrix left divide, x = A\b */ + /* -------------------------------------------------------------- */ + + /* + [x, Info] = umfpack (A, '\', b) ; + [x, Info] = umfpack (A, '\', b, Control) ; + [x, Info] = umfpack (A, Qinit, '\', b, Control) ; + [x, Info] = umfpack (A, Qinit, '\', b) ; + */ + + operation = "x = A\\b" ; + do_solve = TRUE ; + Amatrix = (mxArray *) pargin [0] ; + Bmatrix = (mxArray *) pargin [op+1] ; + + if (nargout == 2) + { + do_info = 1 ; + } + if (op == 2) + { + User_Qinit = (mxArray *) pargin [1] ; + } + if ((op == 1 && nargin == 4) || (op == 2 && nargin == 5)) + { + User_Control_matrix = (mxArray *) pargin [nargin-1] ; + } + if (nargin < 3 || nargin > 5 || nargout > 2) + { + mexErrMsgTxt ("wrong number of arguments") ; + } + + } + else if (STRING_MATCH (operator, "/")) + { + + /* -------------------------------------------------------------- */ + /* matrix right divide, x = b/A */ + /* -------------------------------------------------------------- */ + + /* + [x, Info] = umfpack (b, '/', A) ; + [x, Info] = umfpack (b, '/', A, Control) ; + [x, Info] = umfpack (b, '/', A, Qinit) ; + [x, Info] = umfpack (b, '/', A, Qinit, Control) ; + */ + + operation = "x = b/A" ; + do_solve = TRUE ; + transpose = TRUE ; + Amatrix = (mxArray *) pargin [2] ; + Bmatrix = (mxArray *) pargin [0] ; + + if (nargout == 2) + { + do_info = 1 ; + } + if (nargin == 5) + { + User_Qinit = (mxArray *) pargin [3] ; + User_Control_matrix = (mxArray *) pargin [4] ; + } + else if (nargin == 4) + { + /* Control is k-by-1 where k > 1, Qinit is 1-by-n */ + if (mxGetM (pargin [3]) == 1) + { + User_Qinit = (mxArray *) pargin [3] ; + } + else + { + User_Control_matrix = (mxArray *) pargin [3] ; + } + } + else if (nargin < 3 || nargin > 5 || nargout > 2) + { + mexErrMsgTxt ("wrong number of arguments") ; + } + + } + else if (STRING_MATCH (operator, "symbolic")) + { + + /* -------------------------------------------------------------- */ + /* symbolic factorization only */ + /* -------------------------------------------------------------- */ + + /* + [P Q Fr Ch Info] = umfpack (A, 'symbolic') ; + [P Q Fr Ch Info] = umfpack (A, 'symbolic', Control) ; + [P Q Fr Ch Info] = umfpack (A, Qinit, 'symbolic') ; + [P Q Fr Ch Info] = umfpack (A, Qinit, 'symbolic', Control) ; + */ + + operation = "symbolic factorization" ; + do_numeric = FALSE ; + Amatrix = (mxArray *) pargin [0] ; + + if (nargout == 5) + { + do_info = 4 ; + } + if (op == 2) + { + User_Qinit = (mxArray *) pargin [1] ; + } + if ((op == 1 && nargin == 3) || (op == 2 && nargin == 4)) + { + User_Control_matrix = (mxArray *) pargin [nargin-1] ; + } + if (nargin < 2 || nargin > 4 || nargout > 5 || nargout < 4) + { + mexErrMsgTxt ("wrong number of arguments") ; + } + + } + else + { + mexErrMsgTxt ("operator must be '/', '\\', or 'symbolic'") ; + } + mxFree (operator) ; + + } + else if (nargin > 0) + { + + /* ------------------------------------------------------------------ */ + /* LU factorization */ + /* ------------------------------------------------------------------ */ + + /* + with scaling: + [L, U, P, Q, R, Info] = umfpack (A) ; + [L, U, P, Q, R, Info] = umfpack (A, Qinit) ; + + scaling determined by Control settings: + [L, U, P, Q, R, Info] = umfpack (A, Control) ; + [L, U, P, Q, R, Info] = umfpack (A, Qinit, Control) ; + + with no scaling: + [L, U, P, Q] = umfpack (A) ; + [L, U, P, Q] = umfpack (A, Control) ; + [L, U, P, Q] = umfpack (A, Qinit) ; + [L, U, P, Q] = umfpack (A, Qinit, Control) ; + */ + + operation = "numeric factorization" ; + Amatrix = (mxArray *) pargin [0] ; + + no_scale = nargout <= 4 ; + + if (nargout == 6) + { + do_info = 5 ; + } + if (nargin == 3) + { + User_Qinit = (mxArray *) pargin [1] ; + User_Control_matrix = (mxArray *) pargin [2] ; + } + else if (nargin == 2) + { + /* Control is k-by-1 where k > 1, Qinit is 1-by-n */ + if (mxGetM (pargin [1]) == 1) + { + User_Qinit = (mxArray *) pargin [1] ; + } + else + { + User_Control_matrix = (mxArray *) pargin [1] ; + } + } + else if (nargin > 3 || nargout > 6 || nargout < 4) + { + mexErrMsgTxt ("wrong number of arguments") ; + } + + } + else + { + + /* ------------------------------------------------------------------ */ + /* return default control settings */ + /* ------------------------------------------------------------------ */ + + /* + Control = umfpack ; + umfpack ; + */ + + if (nargout > 1) + { + mexErrMsgTxt ("wrong number of arguments") ; + } + + pargout [0] = mxCreateDoubleMatrix (UMFPACK_CONTROL, 1, mxREAL) ; + User_Control = mxGetPr (pargout [0]) ; + umfpack_di_defaults (User_Control) ; + + return ; + } + + /* ---------------------------------------------------------------------- */ + /* check inputs */ + /* ---------------------------------------------------------------------- */ + + if (mxGetNumberOfDimensions (Amatrix) != 2) + { + mexErrMsgTxt ("input matrix A must be 2-dimensional") ; + } + n_row = mxGetM (Amatrix) ; + n_col = mxGetN (Amatrix) ; + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + if (do_solve && n_row != n_col) + { + mexErrMsgTxt ("input matrix A must square for '\\' or '/'") ; + } + if (!mxIsSparse (Amatrix)) + { + mexErrMsgTxt ("input matrix A must be sparse") ; + } + if (n_row == 0 || n_col == 0) + { + mexErrMsgTxt ("input matrix A cannot have zero rows or zero columns") ; + } + + /* The real/complex status of A determines which version to use, */ + /* (umfpack_di_* or umfpack_zi_*). */ + A_is_complex = mxIsComplex (Amatrix) ; + Atype = A_is_complex ? mxCOMPLEX : mxREAL ; + Ap = mxGetJc (Amatrix) ; + Ai = mxGetIr (Amatrix) ; + Ax = mxGetPr (Amatrix) ; + Az = mxGetPi (Amatrix) ; + + if (do_solve) + { + + if (n_row != n_col) + { + mexErrMsgTxt ("A must be square for \\ or /") ; + } + if (transpose) + { + if (mxGetM (Bmatrix) != 1 || mxGetN (Bmatrix) != nn) + { + mexErrMsgTxt ("b has the wrong dimensions") ; + } + } + else + { + if (mxGetM (Bmatrix) != nn || mxGetN (Bmatrix) != 1) + { + mexErrMsgTxt ("b has the wrong dimensions") ; + } + } + if (mxGetNumberOfDimensions (Bmatrix) != 2) + { + mexErrMsgTxt ("input matrix b must be 2-dimensional") ; + } + if (mxIsSparse (Bmatrix)) + { + mexErrMsgTxt ("input matrix b cannot be sparse") ; + } + if (mxGetClassID (Bmatrix) != mxDOUBLE_CLASS) + { + mexErrMsgTxt ("input matrix b must double precision matrix") ; + } + + B_is_complex = mxIsComplex (Bmatrix) ; + Bx = mxGetPr (Bmatrix) ; + Bz = mxGetPi (Bmatrix) ; + + X_is_complex = A_is_complex || B_is_complex ; + Xtype = X_is_complex ? mxCOMPLEX : mxREAL ; + } + + /* ---------------------------------------------------------------------- */ + /* set the Control parameters */ + /* ---------------------------------------------------------------------- */ + + if (A_is_complex) + { + umfpack_zi_defaults (Control) ; + } + else + { + umfpack_di_defaults (Control) ; + } + if (User_Control_matrix) + { + if (mxGetClassID (User_Control_matrix) != mxDOUBLE_CLASS || + mxIsSparse (User_Control_matrix)) + { + mexErrMsgTxt ("Control must be a dense real matrix") ; + } + size = UMFPACK_CONTROL ; + size = MIN (size, mxGetNumberOfElements (User_Control_matrix)) ; + User_Control = mxGetPr (User_Control_matrix) ; + for (i = 0 ; i < size ; i++) + { + Control [i] = User_Control [i] ; + } + } + + if (no_scale) + { + /* turn off scaling for [L, U, P, Q] = umfpack (A) ; + * ignoring the input value of Control (24) for the usage + * [L, U, P, Q] = umfpack (A, Control) ; */ + Control [UMFPACK_SCALE] = UMFPACK_SCALE_NONE ; + } + + if (mxIsNaN (Control [UMFPACK_PRL])) + { + print_level = UMFPACK_DEFAULT_PRL ; + } + else + { + print_level = (int) Control [UMFPACK_PRL] ; + } + + Control [UMFPACK_PRL] = print_level ; + + /* ---------------------------------------------------------------------- */ + /* get Qinit, if present */ + /* ---------------------------------------------------------------------- */ + + if (User_Qinit) + { + if (mxGetM (User_Qinit) != 1 || mxGetN (User_Qinit) != n_col) + { + mexErrMsgTxt ("Qinit must be 1-by-n_col") ; + } + if (mxGetNumberOfDimensions (User_Qinit) != 2) + { + mexErrMsgTxt ("input Qinit must be 2-dimensional") ; + } + if (mxIsComplex (User_Qinit)) + { + mexErrMsgTxt ("input Qinit must not be complex") ; + } + if (mxGetClassID (User_Qinit) != mxDOUBLE_CLASS) + { + mexErrMsgTxt ("input Qinit must be a double matrix") ; + } + if (mxIsSparse (User_Qinit)) + { + mexErrMsgTxt ("input Qinit must be dense") ; + } + Qinit = (int *) mxMalloc (n_col * sizeof (int)) ; + p = mxGetPr (User_Qinit) ; + for (k = 0 ; k < n_col ; k++) + { + /* convert from 1-based to 0-based indexing */ + Qinit [k] = ((int) (p [k])) - 1 ; + } + + } + else + { + /* umfpack_*_qsymbolic will call colamd to get Qinit. This is the */ + /* same as calling umfpack_*_symbolic with Qinit set to NULL*/ + Qinit = (int *) NULL ; + } + + /* ---------------------------------------------------------------------- */ + /* report the inputs A and Qinit */ + /* ---------------------------------------------------------------------- */ + + if (print_level >= 2) + { + /* print the operation */ + mexPrintf ("\numfpack: %s\n", operation) ; + } + + if (A_is_complex) + { + umfpack_zi_report_control (Control) ; + if (print_level >= 3) mexPrintf ("\nA: ") ; + (void) umfpack_zi_report_matrix (n_row, n_col, Ap, Ai, Ax, Az, + 1, Control) ; + if (Qinit) + { + if (print_level >= 3) mexPrintf ("\nQinit: ") ; + (void) umfpack_zi_report_perm (n_col, Qinit, Control) ; + } + } + else + { + umfpack_di_report_control (Control) ; + if (print_level >= 3) mexPrintf ("\nA: ") ; + (void) umfpack_di_report_matrix (n_row, n_col, Ap, Ai, Ax, + 1, Control) ; + if (Qinit) + { + if (print_level >= 3) mexPrintf ("\nQinit: ") ; + (void) umfpack_di_report_perm (n_col, Qinit, Control) ; + } + } + +#ifndef NO_TRANSPOSE_FORWARD_SLASH + /* ---------------------------------------------------------------------- */ + /* create the array transpose for x = b/A */ + /* ---------------------------------------------------------------------- */ + + if (transpose) + { + /* note that in this case A will be square (nn = n_row = n_col) */ + /* x = (A.'\b.').' will be computed */ + + /* make sure Ci and Cx exist, avoid malloc of zero-sized arrays. */ + nz = MAX (Ap [nn], 1) ; + + Cp = (int *) mxMalloc ((nn+1) * sizeof (int)) ; + Ci = (int *) mxMalloc (nz * sizeof (int)) ; + Cx = (double *) mxMalloc (nz * sizeof (double)) ; + if (A_is_complex) + { + Cz = (double *) mxMalloc (nz * sizeof (double)) ; + status = umfpack_zi_transpose (nn, nn, Ap, Ai, Ax, Az, + (int *) NULL, (int *) NULL, Cp, Ci, Cx, Cz, FALSE) ; + } + else + { + status = umfpack_di_transpose (nn, nn, Ap, Ai, Ax, + (int *) NULL, (int *) NULL, Cp, Ci, Cx) ; + } + + if (status != UMFPACK_OK) + { + error ("transpose of A failed", A_is_complex, nargout, pargout, + Control, Info, status, do_info); + return ; + } + + /* modify pointers so that C will be factorized and solved, not A */ + Ap = Cp ; + Ai = Ci ; + Ax = Cx ; + if (A_is_complex) + { + Az = Cz ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* perform the symbolic factorization */ + /* ---------------------------------------------------------------------- */ + + if (A_is_complex) + { + status = umfpack_zi_qsymbolic (n_row, n_col, Ap, Ai, Ax, Az, + Qinit, &Symbolic, Control, Info) ; + } + else + { + status = umfpack_di_qsymbolic (n_row, n_col, Ap, Ai, Ax, + Qinit, &Symbolic, Control, Info) ; + } + + if (Qinit) + { + mxFree (Qinit) ; + } + + if (status < 0) + { + error ("symbolic factorization failed", A_is_complex, nargout, pargout, + Control, Info, status, do_info) ; + return ; + } + + /* ---------------------------------------------------------------------- */ + /* report the Symbolic object */ + /* ---------------------------------------------------------------------- */ + + if (A_is_complex) + { + (void) umfpack_zi_report_symbolic (Symbolic, Control) ; + } + else + { + (void) umfpack_di_report_symbolic (Symbolic, Control) ; + } + + /* ---------------------------------------------------------------------- */ + /* perform numeric factorization, or just return symbolic factorization */ + /* ---------------------------------------------------------------------- */ + + if (do_numeric) + { + + /* ------------------------------------------------------------------ */ + /* perform the numeric factorization */ + /* ------------------------------------------------------------------ */ + + if (A_is_complex) + { + status = umfpack_zi_numeric (Ap, Ai, Ax, Az, Symbolic, &Numeric, + Control, Info) ; + } + else + { + status = umfpack_di_numeric (Ap, Ai, Ax, Symbolic, &Numeric, + Control, Info) ; + } + + /* ------------------------------------------------------------------ */ + /* free the symbolic factorization */ + /* ------------------------------------------------------------------ */ + + if (A_is_complex) + { + umfpack_zi_free_symbolic (&Symbolic) ; + } + else + { + umfpack_di_free_symbolic (&Symbolic) ; + } + + /* ------------------------------------------------------------------ */ + /* report the Numeric object */ + /* ------------------------------------------------------------------ */ + + if (status < 0) + { + error ("numeric factorization failed", A_is_complex, nargout, + pargout, Control, Info, status, do_info); + return ; + } + + if (A_is_complex) + { + (void) umfpack_zi_report_numeric (Numeric, Control) ; + } + else + { + (void) umfpack_di_report_numeric (Numeric, Control) ; + } + + /* ------------------------------------------------------------------ */ + /* return the solution or the factorization */ + /* ------------------------------------------------------------------ */ + + if (do_solve) + { + /* -------------------------------------------------------------- */ + /* solve Ax=b or A'x'=b', and return just the solution x */ + /* -------------------------------------------------------------- */ + +#ifndef NO_TRANSPOSE_FORWARD_SLASH + if (transpose) + { + /* A.'x.'=b.' gives the same x=b/A as solving A'x'=b' */ + /* since C=A.' was factorized, solve with sys = UMFPACK_A */ + /* since x and b are vectors, x.' and b.' are implicit */ + pargout [0] = mxCreateDoubleMatrix (1, nn, Xtype) ; + } + else + { + pargout [0] = mxCreateDoubleMatrix (nn, 1, Xtype) ; + } + sys = UMFPACK_A ; +#else + if (transpose) + { + /* If A is real, A'x=b is the same as A.'x=b. */ + /* x and b are vectors, so x and b are the same as x' and b'. */ + /* If A is complex, then A.'x.'=b.' gives the same solution x */ + /* as the complex conjugate transpose. If we used the A'x=b */ + /* option in umfpack_*_solve, we would have to form b' on */ + /* input and x' on output (negating the imaginary part). */ + /* We can save this work by just using the A.'x=b option in */ + /* umfpack_*_solve. Then, forming x.' and b.' is implicit, */ + /* since x and b are just vectors anyway. */ + /* In both cases, the system to solve is A.'x=b */ + pargout [0] = mxCreateDoubleMatrix (1, nn, Xtype) ; + sys = UMFPACK_Aat ; + } + else + { + pargout [0] = mxCreateDoubleMatrix (nn, 1, Xtype) ; + sys = UMFPACK_A ; + } +#endif + + /* -------------------------------------------------------------- */ + /* print the right-hand-side, B */ + /* -------------------------------------------------------------- */ + + if (print_level >= 3) mexPrintf ("\nright-hand side, b: ") ; + if (B_is_complex) + { + (void) umfpack_zi_report_vector (nn, Bx, Bz, Control) ; + } + else + { + (void) umfpack_di_report_vector (nn, Bx, Control) ; + } + + /* -------------------------------------------------------------- */ + /* solve the system */ + /* -------------------------------------------------------------- */ + + Xx = mxGetPr (pargout [0]) ; + Xz = mxGetPi (pargout [0]) ; + status2 = UMFPACK_OK ; + + if (A_is_complex) + { + if (!B_is_complex) + { + /* umfpack_zi_solve expects a complex B */ + Bz = (double *) mxCalloc (nn, sizeof (double)) ; + } + status = umfpack_zi_solve (sys, Ap, Ai, Ax, Az, Xx, Xz, Bx, Bz, + Numeric, Control, Info) ; + if (!B_is_complex) + { + mxFree (Bz) ; + } + } + else + { + if (B_is_complex) + { + /* Ax=b when b is complex and A is sparse can be split */ + /* into two systems, A*xr=br and A*xi=bi, where r denotes */ + /* the real part and i the imaginary part of x and b. */ + status2 = umfpack_di_solve (sys, Ap, Ai, Ax, Xz, Bz, + Numeric, Control, Info) ; + } + status = umfpack_di_solve (sys, Ap, Ai, Ax, Xx, Bx, + Numeric, Control, Info) ; + } + +#ifndef NO_TRANSPOSE_FORWARD_SLASH + /* -------------------------------------------------------------- */ + /* free the transposed matrix C */ + /* -------------------------------------------------------------- */ + + if (transpose) + { + mxFree (Cp) ; + mxFree (Ci) ; + mxFree (Cx) ; + if (A_is_complex) + { + mxFree (Cz) ; + } + } +#endif + + /* -------------------------------------------------------------- */ + /* free the Numeric object */ + /* -------------------------------------------------------------- */ + + if (A_is_complex) + { + umfpack_zi_free_numeric (&Numeric) ; + } + else + { + umfpack_di_free_numeric (&Numeric) ; + } + + /* -------------------------------------------------------------- */ + /* check error status */ + /* -------------------------------------------------------------- */ + + if (status < 0 || status2 < 0) + { + mxDestroyArray (pargout [0]) ; + error ("solve failed", A_is_complex, nargout, pargout, Control, + Info, status, do_info) ; + return ; + } + + /* -------------------------------------------------------------- */ + /* print the solution, X */ + /* -------------------------------------------------------------- */ + + if (print_level >= 3) mexPrintf ("\nsolution, x: ") ; + if (X_is_complex) + { + (void) umfpack_zi_report_vector (nn, Xx, Xz, Control) ; + } + else + { + (void) umfpack_di_report_vector (nn, Xx, Control) ; + } + + /* -------------------------------------------------------------- */ + /* warn about singular or near-singular matrices */ + /* -------------------------------------------------------------- */ + + /* no warning is given if Control (1) is zero */ + + if (Control [UMFPACK_PRL] >= 1) + { + if (status == UMFPACK_WARNING_singular_matrix) + { + sprintf (warning, "matrix is singular\n" + "Try increasing Control (%d) and Control (%d).\n" + "(Suppress this warning with Control (%d) = 0.)\n", + 1+UMFPACK_PIVOT_TOLERANCE, + 1+UMFPACK_SYM_PIVOT_TOLERANCE, + 1+UMFPACK_PRL) ; + mexWarnMsgTxt (warning) ; + } + else if (Info [UMFPACK_RCOND] < DBL_EPSILON) + { + sprintf (warning, "matrix is nearly singular, rcond = %g\n" + "Try increasing Control (%d) and Control (%d).\n" + "(Suppress this warning with Control (%d) = 0.)\n", + Info [UMFPACK_RCOND], + 1+UMFPACK_PIVOT_TOLERANCE, + 1+UMFPACK_SYM_PIVOT_TOLERANCE, + 1+UMFPACK_PRL) ; + mexWarnMsgTxt (warning) ; + } + } + + } + else + { + + /* -------------------------------------------------------------- */ + /* get L, U, P, Q, and r */ + /* -------------------------------------------------------------- */ + + if (A_is_complex) + { + status = umfpack_zi_get_lunz (&lnz, &unz, &ignore1, &ignore2, + &ignore3, Numeric) ; + } + else + { + status = umfpack_di_get_lunz (&lnz, &unz, &ignore1, &ignore2, + &ignore3, Numeric) ; + } + + if (status < 0) + { + if (A_is_complex) + { + umfpack_zi_free_numeric (&Numeric) ; + } + else + { + umfpack_di_free_numeric (&Numeric) ; + } + error ("extracting LU factors failed", A_is_complex, nargout, + pargout, Control, Info, status, do_info) ; + return ; + } + + /* avoid malloc of zero-sized arrays */ + lnz = MAX (lnz, 1) ; + unz = MAX (unz, 1) ; + + /* get temporary space, for the *** ROW *** form of L */ + Ltp = (int *) mxMalloc ((n_row+1) * sizeof (int)) ; + Ltj = (int *) mxMalloc (lnz * sizeof (int)) ; + Ltx = (double *) mxMalloc (lnz * sizeof (double)) ; + if (A_is_complex) + { + Ltz = (double *) mxMalloc (lnz * sizeof (double)) ; + } + else + { + Ltz = (double *) NULL ; + } + + /* create permanent copy of the output matrix U */ + pargout [1] = mxCreateSparse (n_inner, n_col, unz, Atype) ; + Up = mxGetJc (pargout [1]) ; + Ui = mxGetIr (pargout [1]) ; + Ux = mxGetPr (pargout [1]) ; + Uz = mxGetPi (pargout [1]) ; + + /* temporary space for the integer permutation vectors */ + P = (int *) mxMalloc (n_row * sizeof (int)) ; + Q = (int *) mxMalloc (n_col * sizeof (int)) ; + + /* get scale factors, if requested */ + status2 = UMFPACK_OK ; + if (!no_scale) + { + /* create a diagonal sparse matrix for the scale factors */ + pargout [4] = mxCreateSparse (n_row, n_row, n_row, mxREAL) ; + Rp = mxGetJc (pargout [4]) ; + Ri = mxGetIr (pargout [4]) ; + for (i = 0 ; i < n_row ; i++) + { + Rp [i] = i ; + Ri [i] = i ; + } + Rp [n_row] = n_row ; + Rs = mxGetPr (pargout [4]) ; + } + else + { + Rs = (double *) NULL ; + } + + /* get Lt, U, P, Q, and Rs from the numeric object */ + if (A_is_complex) + { + status = umfpack_zi_get_numeric (Ltp, Ltj, Ltx, Ltz, Up, Ui, Ux, + Uz, P, Q, (double *) NULL, (double *) NULL, + &do_recip, Rs, Numeric) ; + umfpack_zi_free_numeric (&Numeric) ; + } + else + { + status = umfpack_di_get_numeric (Ltp, Ltj, Ltx, Up, Ui, + Ux, P, Q, (double *) NULL, + &do_recip, Rs, Numeric) ; + umfpack_di_free_numeric (&Numeric) ; + } + + /* for the mexFunction, -DNRECIPROCAL must be set, + * so do_recip must be FALSE */ + + if (status < 0 || status2 < 0 || do_recip) + { + mxFree (Ltp) ; + mxFree (Ltj) ; + mxFree (Ltx) ; + if (Ltz) mxFree (Ltz) ; + mxFree (P) ; + mxFree (Q) ; + mxDestroyArray (pargout [1]) ; + error ("extracting LU factors failed", A_is_complex, nargout, + pargout, Control, Info, status, do_info) ; + return ; + } + + /* create sparse permutation matrix for P */ + pargout [2] = mxCreateSparse (n_row, n_row, n_row, mxREAL) ; + Pp = mxGetJc (pargout [2]) ; + Pi = mxGetIr (pargout [2]) ; + Px = mxGetPr (pargout [2]) ; + for (k = 0 ; k < n_row ; k++) + { + Pp [k] = k ; + Px [k] = 1 ; + Pi [P [k]] = k ; + } + Pp [n_row] = n_row ; + + /* create sparse permutation matrix for Q */ + pargout [3] = mxCreateSparse (n_col, n_col, n_col, mxREAL) ; + Qp = mxGetJc (pargout [3]) ; + Qi = mxGetIr (pargout [3]) ; + Qx = mxGetPr (pargout [3]) ; + for (k = 0 ; k < n_col ; k++) + { + Qp [k] = k ; + Qx [k] = 1 ; + Qi [k] = Q [k] ; + } + Qp [n_col] = n_col ; + + /* permanent copy of L */ + pargout [0] = mxCreateSparse (n_row, n_inner, lnz, Atype) ; + Lp = mxGetJc (pargout [0]) ; + Li = mxGetIr (pargout [0]) ; + Lx = mxGetPr (pargout [0]) ; + Lz = mxGetPi (pargout [0]) ; + + /* convert L from row form to column form */ + if (A_is_complex) + { + /* non-conjugate array transpose */ + status = umfpack_zi_transpose (n_inner, n_row, Ltp, Ltj, Ltx, + Ltz, (int *) NULL, (int *) NULL, Lp, Li, Lx, Lz, FALSE) ; + } + else + { + status = umfpack_di_transpose (n_inner, n_row, Ltp, Ltj, Ltx, + (int *) NULL, (int *) NULL, Lp, Li, Lx) ; + } + + mxFree (Ltp) ; + mxFree (Ltj) ; + mxFree (Ltx) ; + if (Ltz) mxFree (Ltz) ; + + if (status < 0) + { + mxFree (P) ; + mxFree (Q) ; + mxDestroyArray (pargout [0]) ; + mxDestroyArray (pargout [1]) ; + mxDestroyArray (pargout [2]) ; + mxDestroyArray (pargout [3]) ; + error ("constructing L failed", A_is_complex, nargout, pargout, + Control, Info, status, do_info) ; + return ; + } + + /* -------------------------------------------------------------- */ + /* print L, U, P, and Q */ + /* -------------------------------------------------------------- */ + + if (A_is_complex) + { + if (print_level >= 3) mexPrintf ("\nL: ") ; + (void) umfpack_zi_report_matrix (n_row, n_inner, Lp, Li, + Lx, Lz, 1, Control) ; + if (print_level >= 3) mexPrintf ("\nU: ") ; + (void) umfpack_zi_report_matrix (n_inner, n_col, Up, Ui, + Ux, Uz, 1, Control) ; + if (print_level >= 3) mexPrintf ("\nP: ") ; + (void) umfpack_zi_report_perm (n_row, P, Control) ; + if (print_level >= 3) mexPrintf ("\nQ: ") ; + (void) umfpack_zi_report_perm (n_col, Q, Control) ; + } + else + { + if (print_level >= 3) mexPrintf ("\nL: ") ; + (void) umfpack_di_report_matrix (n_row, n_inner, Lp, Li, + Lx, 1, Control) ; + if (print_level >= 3) mexPrintf ("\nU: ") ; + (void) umfpack_di_report_matrix (n_inner, n_col, Up, Ui, + Ux, 1, Control) ; + if (print_level >= 3) mexPrintf ("\nP: ") ; + (void) umfpack_di_report_perm (n_row, P, Control) ; + if (print_level >= 3) mexPrintf ("\nQ: ") ; + (void) umfpack_di_report_perm (n_col, Q, Control) ; + } + + mxFree (P) ; + mxFree (Q) ; + + } + + } + else + { + + /* ------------------------------------------------------------------ */ + /* return the symbolic factorization */ + /* ------------------------------------------------------------------ */ + + Q = (int *) mxMalloc (n_col * sizeof (int)) ; + P = (int *) mxMalloc (n_row * sizeof (int)) ; + Front_npivcol = (int *) mxMalloc ((nn+1) * sizeof (int)) ; + Front_parent = (int *) mxMalloc ((nn+1) * sizeof (int)) ; + Front_1strow = (int *) mxMalloc ((nn+1) * sizeof (int)) ; + Front_leftmostdesc = (int *) mxMalloc ((nn+1) * sizeof (int)) ; + Chain_start = (int *) mxMalloc ((nn+1) * sizeof (int)) ; + Chain_maxrows = (int *) mxMalloc ((nn+1) * sizeof (int)) ; + Chain_maxcols = (int *) mxMalloc ((nn+1) * sizeof (int)) ; + + if (A_is_complex) + { + status = umfpack_zi_get_symbolic (&ignore1, &ignore2, &ignore3, + &nz, &nfronts, &nchains, P, Q, Front_npivcol, + Front_parent, Front_1strow, Front_leftmostdesc, + Chain_start, Chain_maxrows, Chain_maxcols, Symbolic) ; + umfpack_zi_free_symbolic (&Symbolic) ; + } + else + { + status = umfpack_di_get_symbolic (&ignore1, &ignore2, &ignore3, + &nz, &nfronts, &nchains, P, Q, Front_npivcol, + Front_parent, Front_1strow, Front_leftmostdesc, + Chain_start, Chain_maxrows, Chain_maxcols, Symbolic) ; + umfpack_di_free_symbolic (&Symbolic) ; + } + + if (status < 0) + { + mxFree (P) ; + mxFree (Q) ; + mxFree (Front_npivcol) ; + mxFree (Front_parent) ; + mxFree (Front_1strow) ; + mxFree (Front_leftmostdesc) ; + mxFree (Chain_start) ; + mxFree (Chain_maxrows) ; + mxFree (Chain_maxcols) ; + error ("extracting symbolic factors failed", A_is_complex, nargout, + pargout, Control, Info, status, do_info) ; + return ; + } + + /* create sparse permutation matrix for P */ + pargout [0] = mxCreateSparse (n_row, n_row, n_row, mxREAL) ; + Pp = mxGetJc (pargout [0]) ; + Pi = mxGetIr (pargout [0]) ; + Px = mxGetPr (pargout [0]) ; + for (k = 0 ; k < n_row ; k++) + { + Pp [k] = k ; + Px [k] = 1 ; + Pi [P [k]] = k ; + } + Pp [n_row] = n_row ; + + /* create sparse permutation matrix for Q */ + pargout [1] = mxCreateSparse (n_col, n_col, n_col, mxREAL) ; + Qp = mxGetJc (pargout [1]) ; + Qi = mxGetIr (pargout [1]) ; + Qx = mxGetPr (pargout [1]) ; + for (k = 0 ; k < n_col ; k++) + { + Qp [k] = k ; + Qx [k] = 1 ; + Qi [k] = Q [k] ; + } + Qp [n_col] = n_col ; + + /* create Fr */ + pargout [2] = mxCreateDoubleMatrix (nfronts+1, 4, mxREAL) ; + + p1 = mxGetPr (pargout [2]) ; + p2 = p1 + nfronts + 1 ; + p3 = p2 + nfronts + 1 ; + p4 = p3 + nfronts + 1 ; + for (i = 0 ; i <= nfronts ; i++) + { + /* convert parent, 1strow, and leftmostdesc to 1-based */ + p1 [i] = (double) (Front_npivcol [i]) ; + p2 [i] = (double) (Front_parent [i] + 1) ; + p3 [i] = (double) (Front_1strow [i] + 1) ; + p4 [i] = (double) (Front_leftmostdesc [i] + 1) ; + } + + /* create Ch */ + pargout [3] = mxCreateDoubleMatrix (nchains+1, 3, mxREAL) ; + p1 = mxGetPr (pargout [3]) ; + p2 = p1 + nchains + 1 ; + p3 = p2 + nchains + 1 ; + for (i = 0 ; i < nchains ; i++) + { + p1 [i] = (double) (Chain_start [i] + 1) ; /* convert to 1-based */ + p2 [i] = (double) (Chain_maxrows [i]) ; + p3 [i] = (double) (Chain_maxcols [i]) ; + } + p1 [nchains] = Chain_start [nchains] + 1 ; + p2 [nchains] = 0 ; + p3 [nchains] = 0 ; + + mxFree (P) ; + mxFree (Q) ; + mxFree (Front_npivcol) ; + mxFree (Front_parent) ; + mxFree (Front_1strow) ; + mxFree (Front_leftmostdesc) ; + mxFree (Chain_start) ; + mxFree (Chain_maxrows) ; + mxFree (Chain_maxcols) ; + } + + /* ---------------------------------------------------------------------- */ + /* report Info */ + /* ---------------------------------------------------------------------- */ + + if (A_is_complex) + { + umfpack_zi_report_info (Control, Info) ; + } + else + { + umfpack_di_report_info (Control, Info) ; + } + + if (do_info > 0) + { + /* return Info */ + pargout [do_info] = mxCreateDoubleMatrix (1, UMFPACK_INFO, mxREAL) ; + OutInfo = mxGetPr (pargout [do_info]) ; + for (i = 0 ; i < UMFPACK_INFO ; i++) + { + OutInfo [i] = Info [i] ; + } + } +} diff --git a/deal.II/contrib/umfpack/UMFPACK/MATLAB/west0067.mat b/deal.II/contrib/umfpack/UMFPACK/MATLAB/west0067.mat new file mode 100644 index 0000000000000000000000000000000000000000..6cd8c88e60af494ac87bc8fe50c3d1e0ba24409f GIT binary patch literal 4456 zcmcha2~bs47{~83L`@Lf$%U><8U^$aBq8OnxZr}2LXJy?3N8gIOPLFfxl|)TE;!kS z71fY?=Aih11MYwekQ!t#`n1`p$-(;A=u!Q=^@!BO$?1s+`s`W3dhCqn^ysPS zx*nkSGY9+QUmy&#pZ?#6NsAh;X<8dibFatM8MFk=07r8`-}EP1#H7xil9ZUNX-?L> zZsg+AXQn45!uhKa_E!lbK5KIF-<4B^b!w$RtoWw}Smef_JO3YsychW6W8H_Fjptmz1-qtE(IfHm~lt21CdefI5U*iFrely!~)xN?o`Pn+k4 zy=mVA*q1i@@yfw|t-!q?2&j6-S{pIdhcOo;rus4FYQ)%=KF#1js^Y{Qi5C%AhFX(6ZI=kP3?)#h|h)3g7=^IojS%t0Ow<#whpMH&3UM&%{f>{yQzL$JJ-uK za$Q`HIv?J!nGXf(j4(z$W9pnR#(KuoSz(O*7~}l(**g@lKl`zsdgj!941^sH)O}RG zx{u0N_fh%mt!(yUJ!_~_ahUuaF!2m>@O#AX2G3q6z_X~HIoHN}%eAu?`*03^hXfc5 zB0&_02IIjb@HChL62T0R1m=Kr@B&x>mV;M7CddY>Kn}+7)xm z4a;P8-$xhDa?6)TN+xz`)@r}p=M|iJvE?SY&9CBSXjYIYv*|>cO(%*=CSESfeM{`{ z3NEPclOo==>E!osaJ(rmMrVv%9<)-_*?e&sGh*DWyyLv0EU?Yi0}q#r5|2jL*8++} z!4}iFR@>U+g1mj^*^7Ssf=2kAHE&+V^v0$#bV`wq$p=um794xaq;~6)9g< zEGwS5Wd0knGV%Q%TU;uXm8m@^jhv7o%V!O$9Qg5ZxxatNM?N>!2wAf*C~!%dD4pXw zZE)>eQ95@^*3b_%QL=2r!t2L-h{80Fn5EUtrE$K*S%2%W&+|XW#g~UUj>yIhnZKrg zxKGqukGZDizgv07d1b|tfNRd~7I}DW$B!nY4wOeC-V=)>SIYA5ulZ!0e^r)Qx>m%0 zwdY^!E*twau4;ese~ybCrp=^kE+17SrFURvC?o!@yUb!!1lWWii)8wJX@EH>(CCeij z^YW*x2$DxbR=YUoE|v#7JKc&1%l+57OSb1GjjP(f^40ZD$Hm6bLx-AAFBhLCxDMHM z;FPdntXlQ~va+H|tI-4|1K zE*~VTY&!8<)u_nLjTUicz}e>~Wo(s(FB@&XTEF` zI>WzBcfRiAyO|G6wd)N3Hl146=Qh1#UREsnY;Zxl=jBna%`c>F?JbXO8&TlAbAznN zzOugILYjU3YM)=G9JsLS`qOHk_PpAk$}3;}j;npCyz*6E?MLO6uktEx>_g2f_sm<{ zd2HR=@=*5f@Rzcp_jZtcu83x4pfq(&1Z$cPi_9pfj#5ffL}sJD1fr;o2JT b%E5dmRD|R0bS~Z!)9_|!#wSq2Ux2>@cgC5+ literal 0 HcmV?d00001 diff --git a/deal.II/contrib/umfpack/UMFPACK/README b/deal.II/contrib/umfpack/UMFPACK/README new file mode 100644 index 0000000000..ff39cb6f17 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/README @@ -0,0 +1,376 @@ +UMFPACK Version 4.3: a set of routines solving sparse linear systems via LU + factorization. Requires two other packages: the BLAS (dense matrix + operations) and AMD (sparse matrix minimum degree ordering). Includes + a C-callable and MATLAB interface, and a basic FORTRAN 77 interface to + a subset of the C-callable routines. + +Quick start (Unix, or Windows with Cygwin): + + To compile, test, and install both UMFPACK and AMD, the UMFPACK and AMD + directories must be in the same parent directory. To configure, edit + UMFPACK/Make/Make.include and AMD/Make/Make.include (otherwise the BLAS + will not be used, and UMFPACK will be slow). cd to this directory (UMFPACK) + and type "make" (or "make lib" if you do not have MATLAB). To compile and + run a demo program for Harwell/Boeing matrices, type "make hb". To compile + a FORTRAN main program that calls the 32-bit C-callable UMFPACK library, + type "make fortran". When done, type "make clean" to remove unused *.o + files (keeps the compiled libraries and demo programs). See the User Guide + (Doc/UserGuide.pdf), or UMFPACK/Make/Make.include, for more details + (including options for compiling 64-bit mode). + +Quick start (for MATLAB users): + + To compile, test, and install the UMFPACK mexFunction, cd to the + UMFPACK/MATLAB directory and type umfpack_make at the MATLAB prompt. + This works on any system supported by MATLAB. + +-------------------------------------------------------------------------------- + +UMFPACK Version 4.3 (Jan. 16, 2003), Copyright (c) 2003 by Timothy A. +Davis. All Rights Reserved. + +UMFPACK License: + + Your use or distribution of UMFPACK or any modified version of + UMFPACK implies that you agree to this License. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY + EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to use or copy this program, provided + that the Copyright, this License, and the Availability of the original + version is retained on all copies. User documentation of any code that + uses UMFPACK or any modified version of UMFPACK code must cite the + Copyright, this License, the Availability note, and "Used by permission." + Permission to modify the code and to distribute modified code is granted, + provided the Copyright, this License, and the Availability note are + retained, and a notice that the code was modified is included. This + software was developed with support from the National Science Foundation, + and is provided to you free of charge. + +Availability: + + http://www.cise.ufl.edu/research/sparse/umfpack + + UMFPACK (including versions 2.2.1 and earlier, in Fortran) is available at + http://www.cise.ufl.edu/research/sparse. MA38 is available in the Harwell + Subroutine Library. This version of UMFPACK includes a modified form of + COLAMD Version 2.0, originally released on Jan. 31, 2000, also available at + http://www.cise.ufl.edu/research/sparse. COLAMD V2.0 is also incorporated + as a built-in function in MATLAB version 6.1, by The MathWorks, Inc. + (http://www.mathworks.com). COLAMD V1.0 appears as a column-preordering + in SuperLU (SuperLU is available at http://www.netlib.org). + UMFPACK v4.0 is a built-in routine in MATLAB 6.5. + +-------------------------------------------------------------------------------- + +Refer to ../AMD/README for the License for AMD, which is a separate +package for ordering sparse matrices that is required by UMFPACK. +UMFPACK v4.3 can use either AMD v1.0 or v1.1. + +-------------------------------------------------------------------------------- + +This is the UMFPACK README file. It is a terse overview of UMFPACK. +Refer to the User Guide (Doc/UserGuide.pdf) for how to install and use UMFPACK, +or to the Quick Start Guide, QuickStart.pdf. + +Description: + + UMFPACK is a set of routines for solving unsymmetric sparse linear systems, + Ax=b, using the Unsymmetric MultiFrontal method. Written in ANSI/ISO C, + with a MATLAB (Version 6.0 or later) interface. + + For best performance, UMFPACK requires an optimized BLAS library. It can + also be compiled without any BLAS at all. UMFPACK requires AMD Version 1.0 + or Version 1.1 + +Authors: + + Timothy A. Davis (davis@cise.ufl.edu), University of Florida. + + Includes a modified version of COLAMD V2.0, by Stefan I. Larimore and + Timothy A. Davis, University of Florida. The COLAMD algorithm was developed + in collaboration with John Gilbert, Xerox Palo Alto Research Center, and + Esmond Ng, Lawrence Berkeley National Laboratory. + + Includes AMD, by Timothy A. Davis, Patrick R. Amestoy, and Iain S. Duff. + + UMFPACK Version 2.2.1 (MA38 in the Harwell Subroutine Library) is + co-authored with Iain S. Duff, Rutherford Appleton Laboratory. + +Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974, DMS-9803599, and CCR-0203270. + + Portions of this work were done while on sabbatical at Stanford University + and Lawrence Berkeley National Laboratory (with funding from the SciDAC + program). I would like to thank Gene Golub, Esmond Ng, and Horst Simon + for making this sabbatical possible. + + I would also like to thank the many researchers who provided sparse + matrices from a wide range of domains and used earlier versions of UMFPACK/ + MA38 in their applications, and thus assisted in the practical development + of the algorithm (see http://www.cise.ufl.edu/research/sparse, future + contributions of matrices are always welcome). + + The MathWorks, Inc., provided a pre-release of MATLAB V6 which allowed me + to release the first umfpack mexFunction (v3.0) about 6 months earlier than + I had originally planned. They also supported the extension of UMFPACK to + complex, singular, and rectangular matrices (UMFPACK v4.0). + + Penny Anderson (The MathWorks, Inc.), Anshul Gupta (IBM), and Friedrich + Grund (WAIS) assisted in porting UMFPACK to different platforms. Penny + Anderson also incorporated UMFPACK v4.0 into MATLAB, for lu, backslash (\), + and forward slash (/). + +-------------------------------------------------------------------------------- +Files and directories in the UMFPACK distribution: +-------------------------------------------------------------------------------- + + ---------------------------------------------------------------------------- + Subdirectories of the UMFPACK directory: + ---------------------------------------------------------------------------- + + Doc documentation + Make for compiling UMFPACK (a symbolic link to ../AMD/Make) + Source primary source code + Include include files for use in your code that calls UMFPACK + Demo demo programs. also serves as test of the UMFPACK installation. + MATLAB UMFPACK mexFunction for MATLAB, and supporting m-files + Lib where the compiled C-callable UMFPACK library is placed. + + ---------------------------------------------------------------------------- + Files in the UMFPACK directory: + ---------------------------------------------------------------------------- + + Makefile top-level Makefile for GNU make or original make. + Windows users would require Cygwin to use "make" + + README this file + + ---------------------------------------------------------------------------- + Doc directory: documentation + ---------------------------------------------------------------------------- + + ChangeLog change log + License the UMFPACK License + Makefile for creating the documentation + QuickStart.tex Quick Start guide (source) + QuickStart.pdf Quick Start guide (PDF) + UserGuide.bib User Guide (references) + UserGuide.sed1 sed script for processing UserGuide.stex + UserGuide.sed2 sed script for processing UserGuide.stex + UserGuide.stex User Guide (LaTeX) + UserGuide.pdf User Guide (PDF) + + ---------------------------------------------------------------------------- + Make directory: for compiling the UMFPACK library (Lib/libumfpack.a) + ---------------------------------------------------------------------------- + + This is a symbolic link to the ../AMD/Make directory. Refer to the + ../AMD/README file for a list of its contents. + + ---------------------------------------------------------------------------- + Source directory: + ---------------------------------------------------------------------------- + + GNUmakefile a nice Makefile, for GNU make + Makefile an ugly Unix Makefile (for older make's) + + umfpack_col_to_triplet.c convert col form to triplet + umfpack_defaults.c set Control defaults + umfpack_free_numeric.c free Numeric object + umfpack_free_symbolic.c free Symbolic object + umfpack_get_lunz.c get nz's in L and U + umfpack_get_numeric.c get Numeric object + umfpack_get_symbolic.c get Symbolic object + umfpack_numeric.c numeric factorization + umfpack_qsymbolic.c symbolic factorization, user Q + umfpack_report_control.c print Control settings + umfpack_report_info.c print Info statistics + umfpack_report_matrix.c print col or row-form sparse matrix + umfpack_report_numeric.c print Numeric object + umfpack_report_perm.c print permutation + umfpack_report_status.c print return status + umfpack_report_symbolic.c print Symbolic object + umfpack_report_triplet.c print triplet matrix + umfpack_report_vector.c print dense vector + umfpack_scale.c scale a vector + umfpack_solve.c solve a linear system + umfpack_symbolic.c symbolic factorization + umfpack_tictoc.c timer + umfpack_timer.c timer + umfpack_transpose.c transpose a matrix + umfpack_triplet_to_col.c convert triplet to col form + + umf_config.h configuration file (BLAS, memory, timer) + umf_internal.h definitions internal to UMFPACK + umf_version.h version definitions (int/long, real/complex) + + umf_2by2.[ch] + umf_analyze.[ch] symbolic factorization of A'*A + umf_apply_order.[ch] apply column etree postorder + umf_assemble.[ch] assemble elements into current front + umf_blas3_update.[ch] rank-k update. Uses level-3 BLAS + umf_build_tuples.[ch] construct tuples for elements + umf_colamd.[ch] COLAMD pre-ordering, modified for UMFPACK + umf_create_element.[ch] create a new element + umf_dump.[ch] debugging routines, not normally active + umf_extend_front.[ch] extend the current frontal matrix + umf_free.[ch] free memory + umf_fsize.[ch] determine largest front in each subtree + umf_garbage_collection.[ch] compact Numeric->Memory + umf_get_memory.[ch] make Numeric->Memory bigger + umf_grow_front.[ch] make current frontal matrix bigger + umf_init_front.[ch] initialize a new frontal matrix + umf_is_permutation.[ch] checks the validity of a permutation vector + umf_kernel.[ch] the main numeric factorization kernel + umf_kernel_init.[ch] initializations for umf_kernel + umf_kernel_wrapup.[ch] wrapup for umf_kernel + umf_local_search.[ch] local row and column pivot search + umf_lsolve.[ch] solve Lx=b + umf_ltsolve.[ch] solve L'x=b and L.'x=b + umf_malloc.[ch] malloc some memory + umf_mem_alloc_element.[ch] allocate element in Numeric->Memory + umf_mem_alloc_head_block.[ch] alloc. block at head of Numeric->Memory + umf_mem_alloc_tail_block.[ch] alloc. block at tail of Numeric->Memory + umf_mem_free_tail_block.[ch] free block at tail of Numeric->Memory + umf_mem_init_memoryspace.[ch] initialize Numeric->Memory + umf_realloc.[ch] realloc memory + umf_report_perm.[ch] print a permutation vector + umf_report_vector.[ch] print a double vector + umf_row_search.[ch] look for a pivot row + umf_scale.[ch] scale the pivot column + umf_scale_column.[ch] move pivot row & column into place, log P and Q + umf_set_stats.[ch] set statistics (final or estimates) + umf_singletons.[ch] find all zero-cost pivots + umf_solve.[ch] solve a linear system + umf_start_front.[ch] start a new frontal matrix for one frontal chain + umf_store_lu.[ch] store LU factors of current front + umf_symbolic_usage.[ch] determine memory usage for Symbolic object + umf_transpose.[ch] transpose a matrix in row or col form + umf_triplet.[ch] convert triplet to column form + umf_tuple_lengths.[ch] determine the tuple list lengths + umf_usolve.[ch] solve Ux=b + umf_utsolve.[ch] solve U'x=b and U.'x=b + umf_valid_numeric.[ch] checks the validity of a Numeric object + umf_valid_symbolic.[ch] check the validity of a Symbolic object + + ---------------------------------------------------------------------------- + Include directory: + ---------------------------------------------------------------------------- + + umfpack.h include file for user programs. Includes all of + the following files. This serves are source- + code level documenation. These files are also + used to construct the User Guide. + + umfpack_col_to_triplet.h + umfpack_defaults.h + umfpack_free_numeric.h + umfpack_free_symbolic.h + umfpack_get_lunz.h + umfpack_get_numeric.h + umfpack_get_symbolic.h + umfpack_numeric.h + umfpack_qsymbolic.h + umfpack_report_control.h + umfpack_report_info.h + umfpack_report_matrix.h + umfpack_report_numeric.h + umfpack_report_perm.h + umfpack_report_status.h + umfpack_report_symbolic.h + umfpack_report_triplet.h + umfpack_report_vector.h + umfpack_scale.h + umfpack_solve.h + umfpack_symbolic.h + umfpack_tictoc.h + umfpack_timer.h + umfpack_transpose.h + umfpack_triplet_to_col.h + umfpack_wsolve.h note that there is no umfpack_wsolve.c. The + umfpack_*_wsolve routines are created from the + umfpack_solve.c file. + + ---------------------------------------------------------------------------- + Demo directory: + ---------------------------------------------------------------------------- + + Makefile for GNU make or original make + + umfpack_simple.c a simple demo + umpack_xx_demo.c template to create the demo codes below + + umfpack_di_demo.sed for creating umfpack_di_demo.c + umfpack_dl_demo.sed for creating umfpack_dl_demo.c + umfpack_zi_demo.sed for creating umfpack_zi_demo.c + umfpack_zl_demo.sed for creating umfpack_zl_demo.c + + umfpack_di_demo.c a full demo (real/int version) + umfpack_dl_demo.c a full demo (real/long version) + umfpack_zi_demo.c a full demo (complex/int version) + umfpack_zl_demo.c a full demo (complex/long version) + + umfpack_di_demo.out umfpack_di_demo output, unmodified Make.include + umfpack_dl_demo.out umfpack_dl_demo output, unmodified Make.include + umfpack_zi_demo.out umfpack_zi_demo output, unmodified Make.include + umfpack_zl_demo.out umfpack_zl_demo output, unmodified Make.include + + umf4.c a demo (real/int) for Harwell/Boeing matrices + umf4.out output of "make hb", unmodified Make.include + HB directory of sample Harwell/Boeing matrices + readhb.f reads HB matrices, keeps zero entries + readhb_nozeros.f reads HB matrices, removes zero entries + readhb_size.f reads HB matrix dimension, nnz + tmp empty directory for umf4.c demo + + umf4_f77wrapper.c a simple FORTRAN interface for UMFPACK. + compile with "make fortran" + umf4hb.f a demo of the FORTRAN interface + umf4hb.out output of "make fortran" + + umf4_f77zwrapper.c a simple FORTRAN interface for the complex + UMFPACK routines. compile with "make fortran" + umf4zhb.f a demo of the FORTRAN interface (complex) + umf4zhb.out output of umf4zhb with Bai/qc324.cua, which is + not included in this distribution, see + http://www.cise.ufl.edu/research/sparse/matrices + + umf4hb64.f 64-bit version of umf4hb.f + + ---------------------------------------------------------------------------- + MATLAB directory: + ---------------------------------------------------------------------------- + + Contents.m for "help umfpack" listing of toolbox contents + GNUmakefile a nice Makefile, for GNU make + Makefile an ugly Unix Makefile (for older make's) + + luflop.m for "help luflop" + luflopmex.c luflop mexFunction, for computing LU flop count + lu_normest.m 1-norm estimate of A-L*U (by Hager & Davis). + umfpack_btf.m solve Ax=b using umfpack and dmperm + umfpack_demo.m a full umfpack demo + umfpack_details.m the details of how to use umfpack + umfpack.m for "help umfpack" + umfpack_make.m compile the umfpack mexFunction within MATLAB + umfpackmex.c the umfpack mexFunction + umfpack_report.m report statistics + umfpack_simple.m a simple umfpack demo + umfpack_solve.m x=A\b or b/A for arbitrary b + west0067.mat sparse matrix for umfpack_demo.m + + umfpack_demo.m.out output of umfpack_demo.m + umfpack_simple.m.out output of umfpack_simple + + lcc_lib/lapacksyms.def LAPACK definitions for lcc compiler (Windows) + lcc_lib/libmwlapack.lib LAPACK definitions for lcc compiler (Windows) + + ---------------------------------------------------------------------------- + Lib directory: libumfpack.a library placed here + ---------------------------------------------------------------------------- + + libumfpack.def UMPFACK definitions for Windows diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/Makefile b/deal.II/contrib/umfpack/UMFPACK/Source/Makefile new file mode 100644 index 0000000000..a2801eaf20 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/Makefile @@ -0,0 +1,193 @@ +#------------------------------------------------------------------------------- +# UMFPACK Makefile for compiling on Unix systems (for GNU Make) +#------------------------------------------------------------------------------- + +include ../../../../common/Make.global_options + +#------------------------------------------------------------------------------- +# source files +#------------------------------------------------------------------------------- + +# non-user-callable umf_*.[ch] files: +UMFCH = umf_assemble umf_blas3_update umf_build_tuples umf_create_element \ + umf_dump umf_extend_front umf_garbage_collection umf_get_memory \ + umf_init_front umf_kernel umf_kernel_init umf_kernel_wrapup \ + umf_local_search umf_lsolve umf_ltsolve umf_mem_alloc_element \ + umf_mem_alloc_head_block umf_mem_alloc_tail_block \ + umf_mem_free_tail_block umf_mem_init_memoryspace \ + umf_report_vector umf_row_search umf_scale_column \ + umf_set_stats umf_solve umf_symbolic_usage umf_transpose \ + umf_tuple_lengths umf_usolve umf_utsolve umf_valid_numeric \ + umf_valid_symbolic umf_grow_front umf_start_front umf_2by2 \ + umf_store_lu umf_scale + +# non-user-callable umf_*.[ch] files, int/long versions only (no real/complex): +UMFINT = umf_analyze umf_apply_order umf_colamd umf_free umf_fsize \ + umf_is_permutation umf_malloc umf_realloc umf_report_perm \ + umf_singletons + +# non-user-callable, created from umf_ltsolve.c, umf_utsolve.c, +# umf_triplet.c, and umf_assemble.c , with int/long and real/complex versions: +UMF_CREATED = umf_lhsolve umf_uhsolve umf_triplet_map_nox \ + umf_triplet_nomap_x umf_triplet_nomap_nox umf_triplet_map_x \ + umf_assemble_fixq umf_store_lu_drop + +# non-user-callable, int/long and real/complex versions: +UMF = $(UMF_CREATED) $(UMFCH) + +# user-callable umfpack_*.[ch] files (int/long and real/complex): +UMFPACK = umfpack_col_to_triplet umfpack_defaults umfpack_free_numeric \ + umfpack_free_symbolic umfpack_get_numeric umfpack_get_lunz \ + umfpack_get_symbolic umfpack_numeric umfpack_qsymbolic \ + umfpack_report_control umfpack_report_info umfpack_report_matrix \ + umfpack_report_numeric umfpack_report_perm umfpack_report_status \ + umfpack_report_symbolic umfpack_report_triplet \ + umfpack_report_vector umfpack_solve umfpack_symbolic \ + umfpack_transpose umfpack_triplet_to_col umfpack_scale \ + umfpack_load_numeric umfpack_save_numeric \ + umfpack_load_symbolic umfpack_save_symbolic + +# user-callable, created from umfpack_solve.c (umfpack_wsolve.h exists, though): +# with int/long and real/complex versions: +UMFPACKW = umfpack_wsolve + +USER = $(UMFPACKW) $(UMFPACK) + +# user-callable, only one version for int/long, real/complex, *.[ch] files: +GENERIC = umfpack_timer umfpack_tictoc + +#------------------------------------------------------------------------------- +# include files: +#------------------------------------------------------------------------------- + +INC = ../Include/umfpack.h \ + umf_config.h umf_version.h umf_internal.h umf_triplet.h \ + $(addsuffix .h,$(UMFCH)) \ + $(addsuffix .h,$(UMFINT)) \ + $(addprefix ../Include/, $(addsuffix .h,$(USER))) \ + $(addprefix ../Include/, $(addsuffix .h,$(GENERIC))) \ + ../../AMD/Source/amd_internal.h ../../AMD/Include/amd.h + +#------------------------------------------------------------------------------- +# object files for each version +#------------------------------------------------------------------------------- + +DI = $(addprefix $D/lib/umfpack/, $(addsuffix .$(OBJEXT), $(subst umf_,umf_di_,$(UMF)) $(subst umfpack_,umfpack_di_,$(USER)))) +DL = $(addprefix $D/lib/umfpack/, $(addsuffix .$(OBJEXT), $(subst umf_,umf_dl_,$(UMF)) $(subst umfpack_,umfpack_dl_,$(USER)))) +I = $(addprefix $D/lib/umfpack/, $(addsuffix .$(OBJEXT), $(subst umf_,umf_i_,$(UMFINT)))) +L = $(addprefix $D/lib/umfpack/, $(addsuffix .$(OBJEXT), $(subst umf_,umf_l_,$(UMFINT)))) +GN = $(addprefix $D/lib/umfpack/, $(addsuffix .$(OBJEXT), $(subst umfpack_,umfpack_gn_,$(GENERIC)))) + +#------------------------------------------------------------------------------- +# compile each int and long routine (with no real/complex version) +#------------------------------------------------------------------------------- + +$D/lib/umfpack/umf_i_%.$(OBJEXT): umf_%.c $(INC) + @echo =====umfpack==========optimized==$(MT)== $(= 0 ; newcol--) + { + if (Next [newcol] == IS_WEAK) + { + /* add this column to the list of weak nodes */ + DEBUGm1 ((" newcol "ID" has a weak diagonal deg "ID"\n", + newcol, deg)) ; + deg = Degree [newcol] ; + ASSERT (deg >= 0 && deg < n2) ; + Next [newcol] = Head [deg] ; + Head [deg] = newcol ; + maxdeg = MAX (maxdeg, deg) ; + mindeg = MIN (mindeg, deg) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* construct R = C' (C = strong entries in pruned submatrix) */ + /* ---------------------------------------------------------------------- */ + + /* Ri [0..n2-1] is the length of each row of R */ + /* use P as temporary pointer into the row form of R [ */ + Rp [0] = 0 ; + for (row = 0 ; row < n2 ; row++) + { + Rp [row+1] = Rp [row] + Ri [row] ; + P [row] = Rp [row] ; + } + /* Ri no longer needed for row counts */ + + /* all entries in C are strong */ + for (col = 0 ; col < n2 ; col++) + { + p2 = Cp [col+1] ; + for (p = Cp [col] ; p < p2 ; p++) + { + /* place the column index in row = Ci [p] */ + Ri [P [Ci [p]]++] = col ; + } + } + + /* contents of P no longer needed ] */ + +#ifndef NDEBUG + DEBUG0 (("==================R: row form of strong entries in A:\n")) ; + UMF_dump_col_matrix ((double *) NULL, +#ifdef COMPLEX + (double *) NULL, +#endif + Ri, Rp, n2, n2, Rp [n2]) ; +#endif + ASSERT (AMD_valid (n2, n2, Rp, Ri)) ; + + /* ---------------------------------------------------------------------- */ + /* for each weak diagonal, find a pair of strong off-diagonal entries */ + /* ---------------------------------------------------------------------- */ + + for (row = 0 ; row < n2 ; row++) + { + P [row] = EMPTY ; + } + + unmatched = 0 ; + best = EMPTY ; + jdiff = EMPTY ; + jdeg = EMPTY ; + + for (deg = mindeg ; deg <= maxdeg ; deg++) + { + /* find the next weak diagonal of lowest degree */ + DEBUGm2 (("---------------------------------- Deg: "ID"\n", deg)) ; + for (k = Head [deg] ; k != EMPTY ; k = Next [k]) + { + DEBUGm2 (("k: "ID"\n", k)) ; + if (P [k] == EMPTY) + { + /* C (k,k) is a weak diagonal entry. Find an index j != k such + * that C (j,k) and C (k,j) are both strong, and also such + * that Degree [j] is minimized. In case of a tie, pick + * the smallest index j. C and R contain the pattern of + * strong entries only. + * + * Note that row k of R and column k of C are both sorted. */ + + DEBUGm4 (("===== Weak diagonal k = "ID"\n", k)) ; + DEBUG1 (("Column k of C:\n")) ; + for (p = Cp [k] ; p < Cp [k+1] ; p++) + { + DEBUG1 ((" "ID": deg "ID"\n", Ci [p], Degree [Ci [p]])); + } + DEBUG1 (("Row k of R (strong entries only):\n")) ; + for (p = Rp [k] ; p < Rp [k+1] ; p++) + { + DEBUG1 ((" "ID": deg "ID"\n", Ri [p], Degree [Ri [p]])); + } + + /* no (C (k,j), C (j,k)) pair exists yet */ + j_best = EMPTY ; + jdiff_best = Int_MAX ; + jdeg_best = Int_MAX ; + + /* pointers into column k (including values) */ + cp1 = Cp [k] ; + cp2 = Cp [k+1] ; + cp = cp1 ; + + /* pointers into row k (strong entries only, no values) */ + rp1 = Rp [k] ; + rp2 = Rp [k+1] ; + rp = rp1 ; + + /* while entries searched in column k and row k */ + while (TRUE) + { + + if (cp >= cp2) + { + /* no more entries in this column */ + break ; + } + + /* get C (j,k), which is strong */ + j = Ci [cp] ; + + if (rp >= rp2) + { + /* no more entries in this column */ + break ; + } + + /* get R (k,j2), which is strong */ + j2 = Ri [rp] ; + + if (j < j2) + { + /* C (j,k) is strong, but R (k,j) is not strong */ + cp++ ; + continue ; + } + + if (j2 < j) + { + /* C (k,j2) is strong, but R (j2,k) is not strong */ + rp++ ; + continue ; + } + + /* j == j2: C (j,k) is strong and R (k,j) is strong */ + + best = FALSE ; + + if (P [j] == EMPTY) + { + /* j has not yet been matched */ + jdeg = Degree [j] ; + jdiff = SCALAR_ABS (k-j) ; + + DEBUG1 (("Try candidate j "ID" deg "ID" diff "ID + "\n", j, jdeg, jdiff)) ; + + if (j_best == EMPTY) + { + /* this is the first candidate seen */ + DEBUG1 ((" first\n")) ; + best = TRUE ; + } + else + { + if (jdeg < jdeg_best) + { + /* the degree of j is best seen so far. */ + DEBUG1 ((" least degree\n")) ; + best = TRUE ; + } + else if (jdeg == jdeg_best) + { + /* degree of j and j_best are the same */ + /* tie break by nearest node number */ + if (jdiff < jdiff_best) + { + DEBUG1 ((" tie degree, closer\n")) ; + best = TRUE ; + } + else if (jdiff == jdiff_best) + { + /* |j-k| = |j_best-k|. For any given k + * and j_best there is only one other j + * than can be just as close as j_best. + * Tie break by picking the smaller of + * j and j_best */ + DEBUG1 ((" tie degree, as close\n")); + best = j < j_best ; + } + } + else + { + /* j has higher degree than best so far */ + best = FALSE ; + } + } + } + + if (best) + { + /* j is best match for k */ + /* found a strong pair, A (j,k) and A (k,j) */ + DEBUG1 ((" --- Found pair k: "ID" j: " ID + " jdeg: "ID" jdiff: "ID"\n", + k, j, jdeg, jdiff)) ; + ASSERT (jdiff != EMPTY) ; + ASSERT (jdeg != EMPTY) ; + j_best = j ; + jdeg_best = jdeg ; + jdiff_best = jdiff ; + } + + /* get the next entries in column k and row k */ + cp++ ; + rp++ ; + } + + /* save the pair (j,k), if we found one */ + if (j_best != EMPTY) + { + j = j_best ; + DEBUGm4 ((" --- best pair j: "ID" for k: "ID"\n", j, k)) ; + P [k] = j ; + P [j] = k ; + } + else + { + /* no match was found for k */ + unmatched++ ; + } + } + } + } + + /* ---------------------------------------------------------------------- */ + /* finalize the row permutation, P */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < n2 ; k++) + { + if (P [k] == EMPTY) + { + P [k] = k ; + } + } + ASSERT (UMF_is_permutation (P, Rp, n2, n2)) ; + + return (unmatched) ; +} + + +/* ========================================================================== */ +/* === UMF_2by2 ============================================================= */ +/* ========================================================================== */ + +GLOBAL void UMF_2by2 +( + /* input, not modified: */ + Int n, /* A is n-by-n */ + const Int Ap [ ], /* size n+1 */ + const Int Ai [ ], /* size nz = Ap [n] */ + const double Ax [ ], /* size nz if present */ +#ifdef COMPLEX + const double Az [ ], /* size nz if present */ +#endif + double tol, /* tolerance for determining whether or not an + * entry is numerically acceptable. If tol <= 0 + * then all numerical values ignored. */ + Int scale, /* scaling to perform (none, sum, or max) */ + Int Cperm1 [ ], /* singleton permutations */ +#ifndef NDEBUG + Int Rperm1 [ ], /* not needed, since Rperm1 = Cperm1 for submatrix S */ +#endif + Int InvRperm1 [ ], /* inverse of Rperm1 */ + Int n1, /* number of singletons */ + Int nempty, /* number of empty rows/cols */ + + /* input, contents undefined on output: */ + Int Degree [ ], /* Degree [j] is the number of off-diagonal + * entries in row/column j of S+S', where + * where S = A (Cperm1 [n1..], Rperm1 [n1..]). + * Note that S is not used, nor formed. */ + + /* output: */ + Int P [ ], /* P [k] = i means original row i is kth row in S(P,:) + * where S = A (Cperm1 [n1..], Rperm1 [n1..]) */ + Int *p_nweak, + Int *p_unmatched, + + /* workspace (not defined on input or output): */ + Int Ri [ ], /* of size >= max (nz, n) */ + Int Rp [ ], /* of size n+1 */ + double Rs [ ], /* of size n if present. Rs = sum (abs (A),2) or + * max (abs (A),2), the sum or max of each row. Unused + * if scale is equal to UMFPACK_SCALE_NONE. */ + Int Head [ ], /* of size n. Head pointers for bucket sort */ + Int Next [ ], /* of size n. Next pointers for bucket sort */ + Int Ci [ ], /* size nz */ + Int Cp [ ] /* size n+1 */ +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int k, p, row, col, do_values, do_sum, do_max, do_scale, nweak, weak, + p1, p2, dfound, unmatched, n2, oldrow, newrow, oldcol, newcol, pp ; + + double cmax, value, rs, ctol, dvalue ; + Entry aij ; + +#ifndef NRECIPROCAL + Int do_recip = FALSE ; +#endif + +#ifndef NDEBUG + /* UMF_debug += 99 ; */ + DEBUGm3 (("\n ==================================UMF_2by2: tol %g\n", tol)) ; + ASSERT (AMD_valid (n, n, Ap, Ai)) ; + for (k = n1 ; k < n - nempty ; k++) + { + ASSERT (Cperm1 [k] == Rperm1 [k]) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* determine scaling options */ + /* ---------------------------------------------------------------------- */ + + /* use the values, but only if they are present */ + /* ignore the values if tol <= 0 */ + do_values = (tol > 0) + && (Ax != (double *) NULL) +#ifdef COMPLEX + && (Az != (double *) NULL) +#endif + ; + if (do_values && (Rs != (double *) NULL)) + { + do_sum = (scale == UMFPACK_SCALE_SUM) ; + do_max = (scale == UMFPACK_SCALE_MAX) ; + } + else + { + /* no scaling */ + do_sum = FALSE ; + do_max = FALSE ; + } + do_scale = do_max || do_sum ; + DEBUGm3 (("do_values "ID" do_sum "ID" do_max "ID" do_scale "ID"\n", + do_values, do_sum, do_max, do_scale)) ; + + /* ---------------------------------------------------------------------- */ + /* compute the row scaling, if requested */ + /* ---------------------------------------------------------------------- */ + + /* see also umf_kernel_init */ + + if (do_scale) + { +#ifndef NRECIPROCAL + double rsmin ; +#endif + for (row = 0 ; row < n ; row++) + { + Rs [row] = 0.0 ; + } + for (col = 0 ; col < n ; col++) + { + p2 = Ap [col+1] ; + for (p = Ap [col] ; p < p2 ; p++) + { + row = Ai [p] ; + ASSIGN (aij, Ax [p], Az [p]) ; + APPROX_ABS (value, aij) ; + rs = Rs [row] ; + if (!SCALAR_IS_NAN (rs)) + { + if (SCALAR_IS_NAN (value)) + { + /* if any entry in a row is NaN, then the scale factor + * for the row is NaN. It will be set to 1 later. */ + Rs [row] = value ; + } + else if (do_max) + { + Rs [row] = MAX (rs, value) ; + } + else + { + Rs [row] += value ; + } + } + } + } +#ifndef NRECIPROCAL + rsmin = Rs [0] ; + if (SCALAR_IS_ZERO (rsmin) || SCALAR_IS_NAN (rsmin)) + { + rsmin = 1.0 ; + } +#endif + for (row = 0 ; row < n ; row++) + { + /* do not scale an empty row, or a row with a NaN */ + rs = Rs [row] ; + if (SCALAR_IS_ZERO (rs) || SCALAR_IS_NAN (rs)) + { + Rs [row] = 1.0 ; + } +#ifndef NRECIPROCAL + rsmin = MIN (rsmin, Rs [row]) ; +#endif + } + +#ifndef NRECIPROCAL + /* multiply by the reciprocal if Rs is not too small */ + do_recip = (rsmin >= RECIPROCAL_TOLERANCE) ; + if (do_recip) + { + /* invert the scale factors */ + for (row = 0 ; row < n ; row++) + { + Rs [row] = 1.0 / Rs [row] ; + } + } +#endif + } + + /* ---------------------------------------------------------------------- */ + /* compute the max in each column and find diagonal */ + /* ---------------------------------------------------------------------- */ + + nweak = 0 ; + +#ifndef NDEBUG + for (k = 0 ; k < n ; k++) + { + ASSERT (Rperm1 [k] >= 0 && Rperm1 [k] < n) ; + ASSERT (InvRperm1 [Rperm1 [k]] == k) ; + } +#endif + + n2 = n - n1 - nempty ; + + /* use Ri to count the number of strong entries in each row */ + for (row = 0 ; row < n2 ; row++) + { + Ri [row] = 0 ; + } + + pp = 0 ; + ctol = 0 ; + dvalue = 1 ; + + /* construct C = pruned submatrix, strong values only, column form */ + + for (k = n1 ; k < n - nempty ; k++) + { + oldcol = Cperm1 [k] ; + newcol = k - n1 ; + Next [newcol] = EMPTY ; + DEBUGm1 (("Column "ID" newcol "ID" oldcol "ID"\n", k, newcol, oldcol)) ; + + Cp [newcol] = pp ; + + dfound = FALSE ; + p1 = Ap [oldcol] ; + p2 = Ap [oldcol+1] ; + if (do_values) + { + cmax = 0 ; + dvalue = 0 ; + + if (!do_scale) + { + /* no scaling */ + for (p = p1 ; p < p2 ; p++) + { + oldrow = Ai [p] ; + ASSERT (oldrow >= 0 && oldrow < n) ; + newrow = InvRperm1 [oldrow] - n1 ; + ASSERT (newrow >= -n1 && newrow < n2) ; + if (newrow < 0) continue ; + ASSIGN (aij, Ax [p], Az [p]) ; + APPROX_ABS (value, aij) ; + /* if either cmax or value is NaN, define cmax as NaN */ + if (!SCALAR_IS_NAN (cmax)) + { + if (SCALAR_IS_NAN (value)) + { + cmax = value ; + } + else + { + cmax = MAX (cmax, value) ; + } + } + if (oldrow == oldcol) + { + /* we found the diagonal entry in this column */ + dvalue = value ; + dfound = TRUE ; + ASSERT (newrow == newcol) ; + } + } + } +#ifndef NRECIPROCAL + else if (do_recip) + { + /* multiply by the reciprocal */ + for (p = p1 ; p < p2 ; p++) + { + oldrow = Ai [p] ; + ASSERT (oldrow >= 0 && oldrow < n) ; + newrow = InvRperm1 [oldrow] - n1 ; + ASSERT (newrow >= -n1 && newrow < n2) ; + if (newrow < 0) continue ; + ASSIGN (aij, Ax [p], Az [p]) ; + APPROX_ABS (value, aij) ; + value *= Rs [oldrow] ; + /* if either cmax or value is NaN, define cmax as NaN */ + if (!SCALAR_IS_NAN (cmax)) + { + if (SCALAR_IS_NAN (value)) + { + cmax = value ; + } + else + { + cmax = MAX (cmax, value) ; + } + } + if (oldrow == oldcol) + { + /* we found the diagonal entry in this column */ + dvalue = value ; + dfound = TRUE ; + ASSERT (newrow == newcol) ; + } + } + } +#endif + else + { + /* divide instead */ + for (p = p1 ; p < p2 ; p++) + { + oldrow = Ai [p] ; + ASSERT (oldrow >= 0 && oldrow < n) ; + newrow = InvRperm1 [oldrow] - n1 ; + ASSERT (newrow >= -n1 && newrow < n2) ; + if (newrow < 0) continue ; + ASSIGN (aij, Ax [p], Az [p]) ; + APPROX_ABS (value, aij) ; + value /= Rs [oldrow] ; + /* if either cmax or value is NaN, define cmax as NaN */ + if (!SCALAR_IS_NAN (cmax)) + { + if (SCALAR_IS_NAN (value)) + { + cmax = value ; + } + else + { + cmax = MAX (cmax, value) ; + } + } + if (oldrow == oldcol) + { + /* we found the diagonal entry in this column */ + dvalue = value ; + dfound = TRUE ; + ASSERT (newrow == newcol) ; + } + } + } + + ctol = tol * cmax ; + DEBUGm1 ((" cmax col "ID" %g ctol %g\n", oldcol, cmax, ctol)) ; + } + else + { + for (p = p1 ; p < p2 ; p++) + { + oldrow = Ai [p] ; + ASSERT (oldrow >= 0 && oldrow < n) ; + newrow = InvRperm1 [oldrow] - n1 ; + ASSERT (newrow >= -n1 && newrow < n2) ; + if (newrow < 0) continue ; + Ci [pp++] = newrow ; + if (oldrow == oldcol) + { + /* we found the diagonal entry in this column */ + ASSERT (newrow == newcol) ; + dfound = TRUE ; + } + /* count the entries in each column */ + Ri [newrow]++ ; + } + } + + /* ------------------------------------------------------------------ */ + /* flag the weak diagonals */ + /* ------------------------------------------------------------------ */ + + if (!dfound) + { + /* no diagonal entry present */ + weak = TRUE ; + } + else + { + /* diagonal entry is present, check its value */ + weak = (do_values) ? WEAK (dvalue, ctol) : FALSE ; + } + if (weak) + { + /* flag this column as weak */ + DEBUG0 (("Weak!\n")) ; + Next [newcol] = IS_WEAK ; + nweak++ ; + } + + /* ------------------------------------------------------------------ */ + /* count entries in each row that are not numerically weak */ + /* ------------------------------------------------------------------ */ + + if (do_values) + { + if (!do_scale) + { + /* no scaling */ + for (p = p1 ; p < p2 ; p++) + { + oldrow = Ai [p] ; + newrow = InvRperm1 [oldrow] - n1 ; + if (newrow < 0) continue ; + ASSIGN (aij, Ax [p], Az [p]) ; + APPROX_ABS (value, aij) ; + weak = WEAK (value, ctol) ; + if (!weak) + { + DEBUG0 ((" strong: row "ID": %g\n", oldrow, value)) ; + Ci [pp++] = newrow ; + Ri [newrow]++ ; + } + } + } +#ifndef NRECIPROCAL + else if (do_recip) + { + /* multiply by the reciprocal */ + for (p = p1 ; p < p2 ; p++) + { + oldrow = Ai [p] ; + newrow = InvRperm1 [oldrow] - n1 ; + if (newrow < 0) continue ; + ASSIGN (aij, Ax [p], Az [p]) ; + APPROX_ABS (value, aij) ; + value *= Rs [oldrow] ; + weak = WEAK (value, ctol) ; + if (!weak) + { + DEBUG0 ((" strong: row "ID": %g\n", oldrow, value)) ; + Ci [pp++] = newrow ; + Ri [newrow]++ ; + } + } + } +#endif + else + { + /* divide instead */ + for (p = p1 ; p < p2 ; p++) + { + oldrow = Ai [p] ; + newrow = InvRperm1 [oldrow] - n1 ; + if (newrow < 0) continue ; + ASSIGN (aij, Ax [p], Az [p]) ; + APPROX_ABS (value, aij) ; + value /= Rs [oldrow] ; + weak = WEAK (value, ctol) ; + if (!weak) + { + DEBUG0 ((" strong: row "ID": %g\n", oldrow, value)) ; + Ci [pp++] = newrow ; + Ri [newrow]++ ; + } + } + } + } + } + Cp [n2] = pp ; + ASSERT (AMD_valid (n2, n2, Cp, Ci)) ; + + if (nweak == 0) + { + /* nothing to do, quick return */ + DEBUGm2 (("\n =============================UMF_2by2: quick return\n")) ; + for (k = 0 ; k < n ; k++) + { + P [k] = k ; + } + *p_nweak = 0 ; + *p_unmatched = 0 ; + return ; + } + +#ifndef NDEBUG + for (k = 0 ; k < n2 ; k++) + { + P [k] = EMPTY ; + } + for (k = 0 ; k < n2 ; k++) + { + ASSERT (Degree [k] >= 0 && Degree [k] < n2) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* find the 2-by-2 permutation */ + /* ---------------------------------------------------------------------- */ + + /* The matrix S is now mapped to the index range 0 to n2-1. We have + * S = A (Rperm [n1 .. n-nempty-1], Cperm [n1 .. n-nempty-1]), and then + * C = pattern of strong entries in S. A weak diagonal k in S is marked + * with Next [k] = IS_WEAK. */ + + unmatched = two_by_two (n2, Cp, Ci, Degree, Next, Ri, P, Rp, Head) ; + + /* ---------------------------------------------------------------------- */ + + *p_nweak = nweak ; + *p_unmatched = unmatched ; + +#ifndef NDEBUG + DEBUGm4 (("UMF_2by2: weak "ID" unmatched "ID"\n", nweak, unmatched)) ; + for (row = 0 ; row < n ; row++) + { + DEBUGm2 (("P ["ID"] = "ID"\n", row, P [row])) ; + } + DEBUGm2 (("\n =============================UMF_2by2: done\n\n")) ; +#endif +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_2by2.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_2by2.h new file mode 100644 index 0000000000..a7bb479af5 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_2by2.h @@ -0,0 +1,37 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_2by2 +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + double tol, + Int scale, + Int Cperm1 [ ], +#ifndef NDEBUG + Int Rperm1 [ ], +#endif + Int InvRperm [ ], + Int n1, + Int nempty, + Int Degree [ ], + Int P [ ], + Int *p_nweak, + Int *p_nmatched, + Int Ri [ ], + Int Rp [ ], + double Rs [ ], + Int Head [ ], + Int Next [ ], + Int Si [ ], + Int Sp [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_analyze.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_analyze.c new file mode 100644 index 0000000000..b4da7e291b --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_analyze.c @@ -0,0 +1,707 @@ +/* ========================================================================== */ +/* === UMF_analyze ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Symbolic LL' factorization of A'*A, to get upper bounds on the size of + L and U for LU = PAQ, and to determine the frontal matrices and + (supernodal) column elimination tree. No fill-reducing column pre-ordering + is used. + + Returns TRUE if successful, FALSE if out of memory. UMF_analyze can only + run out of memory if anzmax (which is Ap [n_row]) is too small. + + Uses workspace of size O(nonzeros in A). On input, the matrix A is + stored in row-form at the tail end of Ai. It is destroyed on output. + The rows of A must be sorted by increasing first column index. + The matrix is assumed to be valid. + + Empty rows and columns have already been removed. + +*/ + +#include "umf_internal.h" +#include "umf_apply_order.h" +#include "umf_malloc.h" +#include "umf_free.h" +#include "umf_fsize.h" + +/* ========================================================================== */ + +GLOBAL Int UMF_analyze +( + Int n_row, /* A is n_row-by-n_col */ + Int n_col, + Int Ai [ ], /* Ai [Ap [0]..Ap[n_row]-1]: column indices */ + /* destroyed on output. Note that this is NOT the */ + /* user's Ai that was passed to UMFPACK_*symbolic */ + /* size of Ai, Ap [n_row] = anzmax >= anz + n_col */ + /* Ap [0] must be => n_col. The space to the */ + /* front of Ai is used as workspace. */ + + Int Ap [ ], /* of size MAX (n_row, n_col) + 1 */ + /* Ap [0..n_row]: row pointers */ + /* Row i is in Ai [Ap [i] ... Ap [i+1]-1] */ + + /* rows must have smallest col index first, or be */ + /* in sorted form. Used as workspace of size n_col */ + /* and destroyed. */ + + /* Note that this is NOT the */ + /* user's Ap that was passed to UMFPACK_*symbolic */ + + Int Up [ ], /* workspace of size n_col, and output column perm. + * for column etree postorder. */ + + Int fixQ, + + /* temporary workspaces: */ + Int W [ ], /* W [0..n_col-1] */ + Int Link [ ], /* Link [0..n_col-1] */ + + /* output: information about each frontal matrix: */ + Int Front_ncols [ ], /* size n_col */ + Int Front_nrows [ ], /* of size n_col */ + Int Front_npivcol [ ], /* of size n_col */ + Int Front_parent [ ], /* of size n_col */ + Int *nfr_out, + + Int *p_ncompactions /* number of compactions in UMF_analyze */ +) +{ + /* ====================================================================== */ + /* ==== local variables ================================================= */ + /* ====================================================================== */ + + Int j, j3, col, k, row, parent, j2, pdest, p, p2, thickness, npivots, nfr, + i, *Winv, kk, npiv, jnext, krow, knext, pfirst, jlast, ncompactions, + *Front_stack, *Front_order, *Front_child, *Front_sibling, + Wflag, npivcol, fallrows, fallcols, fpiv, frows, fcols, *Front_size ; + + nfr = 0 ; + DEBUG0 (("UMF_analyze: anzmax "ID" anrow "ID" ancol "ID"\n", + Ap [n_row], n_row, n_col)) ; + + /* ====================================================================== */ + /* ==== initializations ================================================= */ + /* ====================================================================== */ + +#pragma ivdep + for (j = 0 ; j < n_col ; j++) + { + Link [j] = EMPTY ; + W [j] = EMPTY ; + Up [j] = EMPTY ; + + /* Frontal matrix data structure: */ + Front_npivcol [j] = 0 ; /* number of pivot columns */ + Front_nrows [j] = 0 ; /* number of rows, incl. pivot rows */ + Front_ncols [j] = 0 ; /* number of cols, incl. pivot cols */ + Front_parent [j] = EMPTY ; /* parent front */ + /* Note that only non-pivotal columns are stored in a front (a "row" */ + /* of U) during elimination. */ + } + + /* the rows must be sorted by increasing min col */ + krow = 0 ; + pfirst = Ap [0] ; + jlast = EMPTY ; + jnext = EMPTY ; + Wflag = 0 ; + + /* this test requires the size of Ai to be >= n_col + nz */ + ASSERT (pfirst >= n_col) ; /* Ai must be large enough */ + + /* pdest points to the first free space in Ai */ + pdest = 0 ; + ncompactions = 0 ; + + /* ====================================================================== */ + /* === compute symbolic LL' factorization (unsorted) ==================== */ + /* ====================================================================== */ + + for (j = 0 ; j < n_col ; j = jnext) + { + DEBUG1 (("\n\n============Front "ID" starting. nfr = "ID"\n", j, nfr)) ; + + /* ================================================================== */ + /* === garbage collection =========================================== */ + /* ================================================================== */ + + if (pdest + (n_col-j) > pfirst) + { + /* we might run out ... compact the rows of U */ + +#ifndef NDEBUG + DEBUG0 (("UMF_analyze COMPACTION, j="ID" pfirst="ID"\n", + j, pfirst)) ; + for (row = 0 ; row < j ; row++) + { + if (Up [row] != EMPTY) + { + /* this is a live row of U */ + DEBUG1 (("Live row: "ID" cols: ", row)) ; + p = Up [row] ; + ASSERT (Front_ncols [row] > Front_npivcol [row]) ; + p2 = p + (Front_ncols [row] - Front_npivcol [row]) ; + for ( ; p < p2 ; p++) + { + DEBUG1 ((ID, Ai [p])) ; + ASSERT (p < pfirst) ; + ASSERT (Ai [p] > row && Ai [p] < n_col) ; + } + DEBUG1 (("\n")) ; + } + } + DEBUG1 (("\nStarting to compact:\n")) ; +#endif + + pdest = 0 ; + ncompactions++ ; + for (row = 0 ; row < j ; row++) + { + if (Up [row] != EMPTY) + { + /* this is a live row of U */ + DEBUG1 (("Live row: "ID" cols: ", row)) ; + ASSERT (row < n_col) ; + p = Up [row] ; + ASSERT (Front_ncols [row] > Front_npivcol [row]) ; + p2 = p + (Front_ncols [row] - Front_npivcol [row]) ; + Up [row] = pdest ; + for ( ; p < p2 ; p++) + { + DEBUG1 ((ID, Ai [p])) ; + ASSERT (p < pfirst) ; + ASSERT (Ai [p] > row && Ai [p] < n_col) ; + Ai [pdest++] = Ai [p] ; + ASSERT (pdest <= pfirst) ; + } + DEBUG1 (("\n")) ; + } + } + +#ifndef NDEBUG + DEBUG1 (("\nAFTER COMPACTION, j="ID" pfirst="ID"\n", j, pfirst)) ; + for (row = 0 ; row < j ; row++) + { + if (Up [row] != EMPTY) + { + /* this is a live row of U */ + DEBUG1 (("Live row: "ID" cols: ", row)) ; + p = Up [row] ; + ASSERT (Front_ncols [row] > Front_npivcol [row]) ; + p2 = p + (Front_ncols [row] - Front_npivcol [row]) ; + for ( ; p < p2 ; p++) + { + DEBUG1 ((ID, Ai [p])) ; + ASSERT (p < pfirst) ; + ASSERT (Ai [p] > row && Ai [p] < n_col) ; + } + DEBUG1 (("\n")) ; + } + } +#endif + + } + + if (pdest + (n_col-j) > pfirst) + { + /* :: out of memory in umf_analyze :: */ + /* it can't happen, if pfirst >= n_col */ + return (FALSE) ; /* internal error! */ + } + + /* ------------------------------------------------------------------ */ + /* is the last front a child of this one? */ + /* ------------------------------------------------------------------ */ + + if (jlast != EMPTY && Link [j] == jlast) + { + /* yes - create row j by appending to jlast */ + DEBUG1 (("GOT:last front is child of this one: j "ID" jlast "ID"\n", + j, jlast)) ; + ASSERT (jlast >= 0 && jlast < j) ; + + Up [j] = Up [jlast] ; + Up [jlast] = EMPTY ; + + /* find the parent, delete column j, and update W */ + parent = n_col ; + for (p = Up [j] ; p < pdest ; ) + { + j3 = Ai [p] ; + DEBUG1 (("Initial row of U: col "ID" ", j3)) ; + ASSERT (j3 >= 0 && j3 < n_col) ; + DEBUG1 (("W: "ID" \n", W [j3])) ; + ASSERT (W [j3] == Wflag) ; + if (j == j3) + { + DEBUG1 (("Found column j at p = "ID"\n", p)) ; + Ai [p] = Ai [--pdest] ; + } + else + { + if (j3 < parent) + { + parent = j3 ; + } + p++ ; + } + } + + /* delete jlast from the link list of j */ + Link [j] = Link [jlast] ; + + ASSERT (Front_nrows [jlast] > Front_npivcol [jlast]) ; + thickness = (Front_nrows [jlast] - Front_npivcol [jlast]) ; + DEBUG1 (("initial thickness: "ID"\n", thickness)) ; + + } + else + { + Up [j] = pdest ; + parent = n_col ; + /* thickness: number of (nonpivotal) rows in frontal matrix j */ + thickness = 0 ; + Wflag = j ; + } + + /* ================================================================== */ + /* === compute row j of A*A' ======================================== */ + /* ================================================================== */ + + /* ------------------------------------------------------------------ */ + /* flag the diagonal entry in row U, but do not add to pattern */ + /* ------------------------------------------------------------------ */ + + ASSERT (pdest <= pfirst) ; + W [j] = Wflag ; + + DEBUG1 (("\nComputing row "ID" of A'*A\n", j)) ; + DEBUG2 ((" col: "ID" (diagonal)\n", j)) ; + + /* ------------------------------------------------------------------ */ + /* find the rows the contribute to this column j */ + /* ------------------------------------------------------------------ */ + + jnext = n_col ; + for (knext = krow ; knext < n_row ; knext++) + { + ASSERT (Ap [knext] < Ap [knext+1]) ; + ASSERT (Ap [knext] >= pfirst && Ap [knext] <= Ap [n_row]) ; + jnext = Ai [Ap [knext]] ; + ASSERT (jnext >= j) ; + if (jnext != j) + { + break ; + } + } + + /* rows krow ... knext-1 all have first column index of j */ + /* (or are empty) */ + + /* row knext has first column index of jnext */ + /* if knext = n_row, then jnext is n_col */ + if (knext == n_row) + { + jnext = n_col ; + } + + ASSERT (jnext > j) ; + ASSERT (jnext <= n_col) ; + + /* ------------------------------------------------------------------ */ + /* for each nonzero A (k,j) in column j of A do: */ + /* ------------------------------------------------------------------ */ + + for (k = krow ; k < knext ; k++) + { + p = Ap [k] ; + p2 = Ap [k+1] ; + ASSERT (p < p2) ; + + /* merge row k of A into W */ + DEBUG2 ((" ---- A row "ID" ", k)) ; + ASSERT (k >= 0 && k < n_row) ; + ASSERT (Ai [p] == j) ; + DEBUG2 ((" p "ID" p2 "ID"\n cols:", p, p2)) ; + ASSERT (p >= pfirst && p < Ap [n_row]) ; + ASSERT (p2 > pfirst && p2 <= Ap [n_row]) ; + for ( ; p < p2 ; p++) + { + /* add to pattern if seen for the first time */ + col = Ai [p] ; + ASSERT (col >= j && col < n_col) ; + DEBUG3 ((" "ID, col)) ; + if (W [col] != Wflag) + { + Ai [pdest++] = col ; + ASSERT (pdest <= pfirst) ; + /* flag this column has having been seen for row j */ + W [col] = Wflag ; + if (col < parent) + { + parent = col ; + } + } + } + DEBUG2 (("\n")) ; + thickness++ ; + } + +#ifndef NDEBUG + DEBUG3 (("\nRow "ID" of A'A:\n", j)) ; + for (p = Up [j] ; p < pdest ; p++) + { + DEBUG3 ((" "ID, Ai [p])) ; + } + DEBUG3 (("\n")) ; +#endif + + /* ------------------------------------------------------------------ */ + /* delete rows up to but not including knext */ + /* ------------------------------------------------------------------ */ + + krow = knext ; + pfirst = Ap [knext] ; + + /* we can now use Ai [0..pfirst-1] as workspace for rows of U */ + + /* ================================================================== */ + /* === compute jth row of U ========================================= */ + /* ================================================================== */ + + /* for each nonzero U (k,j) in column j of U (1:j-1,:) do */ + for (k = Link [j] ; k != EMPTY ; k = Link [k]) + { + /* merge row k of U into W */ + DEBUG2 ((" ---- U row "ID, k)) ; + ASSERT (k >= 0 && k < n_col) ; + ASSERT (Up [k] != EMPTY) ; + p = Up [k] ; + ASSERT (Front_ncols [k] > Front_npivcol [k]) ; + p2 = p + (Front_ncols [k] - Front_npivcol [k]) ; + DEBUG2 ((" p "ID" p2 "ID"\n cols:", p, p2)) ; + ASSERT (p <= pfirst) ; + ASSERT (p2 <= pfirst) ; + for ( ; p < p2 ; p++) + { + /* add to pattern if seen for the first time */ + col = Ai [p] ; + ASSERT (col >= j && col < n_col) ; + DEBUG3 ((" "ID, col)) ; + if (W [col] != Wflag) + { + Ai [pdest++] = col ; + ASSERT (pdest <= pfirst) ; + /* flag this col has having been seen for row j */ + W [col] = Wflag ; + if (col < parent) + { + parent = col ; + } + } + } + DEBUG2 (("\n")) ; + + /* mark the row k as deleted */ + Up [k] = EMPTY ; + + ASSERT (Front_nrows [k] > Front_npivcol [k]) ; + thickness += (Front_nrows [k] - Front_npivcol [k]) ; + ASSERT (Front_parent [k] == j) ; + } + +#ifndef NDEBUG + DEBUG3 (("\nRow "ID" of U prior to supercolumn detection:\n", j)); + for (p = Up [j] ; p < pdest ; p++) + { + DEBUG3 ((" "ID, Ai [p])) ; + } + DEBUG3 (("\n")) ; + DEBUG1 (("thickness, prior to supercol detect: "ID"\n", thickness)) ; +#endif + + /* ================================================================== */ + /* === quicky mass elimination ====================================== */ + /* ================================================================== */ + + /* this code detects some supernodes, but it might miss */ + /* some because the elimination tree (created on the fly) */ + /* is not yet post-ordered, and because the pattern of A'*A */ + /* is also computed on the fly. */ + + /* j2 is incremented because the pivot columns are not stored */ + + for (j2 = j+1 ; j2 < jnext ; j2++) + { + ASSERT (j2 >= 0 && j2 < n_col) ; + if (W [j2] != Wflag || Link [j2] != EMPTY) + { + break ; + } + } + + /* the loop above terminated with j2 at the first non-supernode */ + DEBUG1 (("jnext = "ID"\n", jnext)) ; + ASSERT (j2 <= jnext) ; + jnext = j2 ; + j2-- ; + DEBUG1 (("j2 = "ID"\n", j2)) ; + ASSERT (j2 < n_col) ; + + npivots = j2-j+1 ; + DEBUG1 (("Number of pivot columns: "ID"\n", npivots)) ; + + /* rows j:j2 have the same nonzero pattern, except for columns j:j2-1 */ + + if (j2 > j) + { + /* supernode detected, prune the pattern of new row j */ + ASSERT (parent == j+1) ; + ASSERT (j2 < n_col) ; + DEBUG1 (("Supernode detected, j "ID" to j2 "ID"\n", j, j2)) ; + + parent = n_col ; + p2 = pdest ; + pdest = Up [j] ; + for (p = Up [j] ; p < p2 ; p++) + { + col = Ai [p] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (W [col] == Wflag) ; + if (col > j2) + { + /* keep this col in the pattern of the new row j */ + Ai [pdest++] = col ; + if (col < parent) + { + parent = col ; + } + } + } + } + + DEBUG1 (("Parent ["ID"] = "ID"\n", j, parent)) ; + ASSERT (parent > j2) ; + + if (parent == n_col) + { + /* this front has no parent - it is the root of a subtree */ + parent = EMPTY ; + } + +#ifndef NDEBUG + DEBUG3 (("\nFinal row "ID" of U after supercolumn detection:\n", j)) ; + for (p = Up [j] ; p < pdest ; p++) + { + ASSERT (Ai [p] >= 0 && Ai [p] < n_col) ; + DEBUG3 ((" "ID" ("ID")", Ai [p], W [Ai [p]])) ; + ASSERT (W [Ai [p]] == Wflag) ; + } + DEBUG3 (("\n")) ; +#endif + + /* ================================================================== */ + /* === frontal matrix =============================================== */ + /* ================================================================== */ + + /* front has Front_npivcol [j] pivot columns */ + /* entire front is Front_nrows [j] -by- Front_ncols [j] */ + /* j is first column in the front */ + + npivcol = npivots ; + fallrows = thickness ; + fallcols = npivots + pdest - Up [j] ; + + /* number of pivots in the front (rows and columns) */ + fpiv = MIN (npivcol, fallrows) ; + + /* size of contribution block */ + frows = fallrows - fpiv ; + fcols = fallcols - fpiv ; + + if (frows == 0 || fcols == 0) + { + /* front has no contribution block and thus needs no parent */ + DEBUG1 (("Frontal matrix evaporation\n")) ; + Up [j] = EMPTY ; + parent = EMPTY ; + } + + Front_npivcol [j] = npivots ; + Front_nrows [j] = fallrows ; + Front_ncols [j] = fallcols ; + Front_parent [j] = parent ; + ASSERT (npivots > 0) ; + + /* Front_parent [j] is the first column of the parent frontal matrix */ + + DEBUG1 (("\n\n==== Front "ID", nfr "ID" pivot columns "ID":"ID + " all front: "ID"-by-"ID" Parent: "ID"\n", j, nfr, j,j+npivots-1, + Front_nrows [j], Front_ncols [j], Front_parent [j])) ; + nfr++ ; + + /* ================================================================== */ + /* === prepare this row for its parent ============================== */ + /* ================================================================== */ + + if (parent != EMPTY) + { + Link [j] = Link [parent] ; + Link [parent] = j ; + } + + ASSERT (jnext > j) ; + + jlast = j ; + } + + /* ====================================================================== */ + /* === postorder the fronts ============================================= */ + /* ====================================================================== */ + + *nfr_out = nfr ; + + Front_order = W ; /* use W for Front_order [ */ + + if (fixQ) + { + /* do not postorder the fronts if Q is fixed */ + DEBUG1 (("\nNo postorder (Q is fixed)\n")) ; + k = 0 ; + /* Pragma added May 14, 2003. The Intel compiler icl 6.0 (an old + * version) incorrectly vectorizes this loop. */ +#pragma novector + for (j = 0 ; j < n_col ; j++) + { + if (Front_npivcol [j] > 0) + { + Front_order [j] = k++ ; + DEBUG1 (("Front order of j: "ID" is:"ID"\n", j, + Front_order [j])) ; + } + else + { + Front_order [j] = EMPTY ; + } + } + } + else + { + + /* use Ap for Front_child and use Link for Front_sibling [ */ + Front_child = Ap ; + Front_sibling = Link ; + + /* use Ai for Front_stack, size of Ai is >= 2*n_col */ + Front_stack = Ai ; + Front_size = Front_stack + n_col ; + + UMF_fsize (n_col, Front_size, Front_nrows, Front_ncols, + Front_parent, Front_npivcol) ; + + AMD_postorder (n_col, Front_parent, Front_npivcol, Front_size, + Front_order, Front_child, Front_sibling, Front_stack) ; + + /* done with Front_child, Front_sibling, Front_size, and Front_stack ]*/ + + /* ------------------------------------------------------------------ */ + /* construct the column permutation (return in Up) */ + /* ------------------------------------------------------------------ */ + + /* Front_order [i] = k means that front i is kth front in the new order. + * i is in the range 0 to n_col-1, and k is in the range 0 to nfr-1 */ + + /* Use Ai as workspace for Winv [ */ + Winv = Ai ; + for (k = 0 ; k < nfr ; k++) + { + Winv [k] = EMPTY ; + } + + /* compute the inverse of Front_order, so that Winv [k] = i */ + /* if Front_order [i] = k */ + + DEBUG1 (("\n\nComputing output column permutation:\n")) ; + for (i = 0 ; i < n_col ; i++) + { + k = Front_order [i] ; + if (k != EMPTY) + { + DEBUG1 (("Front "ID" new order: "ID"\n", i, k)) ; + ASSERT (k >= 0 && k < nfr) ; + ASSERT (Winv [k] == EMPTY) ; + Winv [k] = i ; + } + } + + /* Use Up as output permutation */ + kk = 0 ; + for (k = 0 ; k < nfr ; k++) + { + i = Winv [k] ; + DEBUG1 (("Old Front "ID" New Front "ID" npivots "ID" nrows "ID + " ncols "ID"\n", + i, k, Front_npivcol [i], Front_nrows [i], Front_ncols [i])) ; + ASSERT (i >= 0 && i < n_col) ; + ASSERT (Front_npivcol [i] > 0) ; + for (npiv = 0 ; npiv < Front_npivcol [i] ; npiv++) + { + Up [kk] = i + npiv ; + DEBUG1 ((" Cperm ["ID"] = "ID"\n", kk, Up [kk])) ; + kk++ ; + } + } + ASSERT (kk == n_col) ; + + /* Winv no longer needed ] */ + } + + /* ---------------------------------------------------------------------- */ + /* apply the postorder traversal to renumber the frontal matrices */ + /* (or pack them in same order, if fixQ) */ + /* ---------------------------------------------------------------------- */ + + /* use Ai as workspace */ + + UMF_apply_order (Front_npivcol, Front_order, Ai, n_col, nfr) ; + UMF_apply_order (Front_nrows, Front_order, Ai, n_col, nfr) ; + UMF_apply_order (Front_ncols, Front_order, Ai, n_col, nfr) ; + UMF_apply_order (Front_parent, Front_order, Ai, n_col, nfr) ; + + /* fix the parent to refer to the new numbering */ + for (i = 0 ; i < nfr ; i++) + { + parent = Front_parent [i] ; + if (parent != EMPTY) + { + ASSERT (parent >= 0 && parent < n_col) ; + ASSERT (Front_order [parent] >= 0 && Front_order [parent] < nfr) ; + Front_parent [i] = Front_order [parent] ; + } + } + + /* Front_order longer needed ] */ + +#ifndef NDEBUG + DEBUG1 (("\nFinal frontal matrices:\n")) ; + for (i = 0 ; i < nfr ; i++) + { + DEBUG1 (("Final front "ID": npiv "ID" nrows "ID" ncols "ID" parent " + ID"\n", i, Front_npivcol [i], Front_nrows [i], + Front_ncols [i], Front_parent [i])) ; + } +#endif + + *p_ncompactions = ncompactions ; + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_analyze.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_analyze.h new file mode 100644 index 0000000000..cb3af8a9fb --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_analyze.h @@ -0,0 +1,24 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_analyze +( + Int n_row, + Int n_col, + Int Ai [ ], + Int Ap [ ], + Int Up [ ], + Int fixQ, + Int Front_ncols [ ], + Int W [ ], + Int Link [ ], + Int Front_nrows [ ], + Int Front_npivcol [ ], + Int Front_parent [ ], + Int *nfr_out, + Int *p_ncompactions +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_apply_order.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_apply_order.c new file mode 100644 index 0000000000..88e9b62d68 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_apply_order.c @@ -0,0 +1,44 @@ +/* ========================================================================== */ +/* === UMF_apply_order ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Apply post-ordering of supernodal elimination tree. +*/ + +#include "umf_internal.h" + +GLOBAL void UMF_apply_order +( + Int Front [ ], /* of size nn on input, size nfr on output */ + const Int Order [ ], /* Order [i] = k, i in the range 0..nn-1, + * and k in the range 0..nfr-1, means that node + * i is the kth node in the postordered tree. */ + Int Temp [ ], /* workspace of size nfr */ + Int nn, /* nodes are numbered in the range 0..nn-1 */ + Int nfr /* the number of nodes actually in use */ +) +{ + Int i, k ; + for (i = 0 ; i < nn ; i++) + { + k = Order [i] ; + ASSERT (k >= EMPTY && k < nfr) ; + if (k != EMPTY) + { + Temp [k] = Front [i] ; + } + } + + for (k = 0 ; k < nfr ; k++) + { + Front [k] = Temp [k] ; + } +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_apply_order.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_apply_order.h new file mode 100644 index 0000000000..73b63a6945 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_apply_order.h @@ -0,0 +1,15 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_apply_order +( + Int Front [ ], + const Int Order [ ], + Int Temp [ ], + Int n_col, + Int nfr +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_assemble.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_assemble.c new file mode 100644 index 0000000000..1e1b0f224e --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_assemble.c @@ -0,0 +1,1216 @@ +/* ========================================================================== */ +/* === UMF_assemble ========================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Degree update and numerical assembly. This is compiled twice (with and + * without FIXQ) for each real/complex int/long version, for a total of 8 + * versions.*/ + +#include "umf_internal.h" +#include "umf_mem_free_tail_block.h" + +/* ========================================================================== */ +/* === row_assemble ========================================================= */ +/* ========================================================================== */ + +PRIVATE void row_assemble +( + Int row, + NumericType *Numeric, + WorkType *Work +) +{ + + Int tpi, e, *E, *Fcpos, *Frpos, *Row_degree, *Row_tuples, *Row_tlen, rdeg0, + f, nrows, ncols, *Rows, *Cols, col, ncolsleft, j ; + Tuple *tp, *tp1, *tp2, *tpend ; + Unit *Memory, *p ; + Element *ep ; + Entry *S, *Fcblock, *Frow ; + +#ifndef FIXQ + Int *Col_degree ; + Col_degree = Numeric->Cperm ; +#endif + + Row_tuples = Numeric->Uip ; + tpi = Row_tuples [row] ; + if (!tpi) return ; + + Memory = Numeric->Memory ; + E = Work->E ; + Fcpos = Work->Fcpos ; + Frpos = Work->Frpos ; + Row_degree = Numeric->Rperm ; + Row_tlen = Numeric->Uilen ; + E = Work->E ; + Memory = Numeric->Memory ; + rdeg0 = Work->rdeg0 ; + Fcblock = Work->Fcblock ; + +#ifndef NDEBUG + DEBUG6 (("SCAN2-row: "ID"\n", row)) ; + UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ; +#endif + + ASSERT (NON_PIVOTAL_ROW (row)) ; + + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Row_tlen [row] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + Rows = Cols + ep->ncols ; + if (Rows [f] == EMPTY) continue ; /* row already assembled */ + ASSERT (row == Rows [f] && row >= 0 && row < Work->n_row) ; + + if (ep->rdeg == rdeg0) + { + /* ------------------------------------------------------ */ + /* this is an old Lson - assemble just one row */ + /* ------------------------------------------------------ */ + + /* flag the row as assembled from the Lson */ + Rows [f] = EMPTY ; + + nrows = ep->nrows ; + ncols = ep->ncols ; + + p += UNITS (Int, ncols + nrows) ; + S = ((Entry *) p) + f ; + + DEBUG6 (("Old LSON: "ID"\n", e)) ; +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + + ncolsleft = ep->ncolsleft ; + + Frow = Fcblock + Frpos [row] ; + DEBUG6 (("LSON found (in scan2-row): "ID"\n", e)) ; + + Row_degree [row] -= ncolsleft ; + + if (ncols == ncolsleft) + { + /* -------------------------------------------------- */ + /* no columns assembled out this Lson yet */ + /* -------------------------------------------------- */ + +#pragma ivdep + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + ASSERT (col >= 0 && col < Work->n_col) ; +#ifndef FIXQ + Col_degree [col] -- ; +#endif + /* Frow [Fcpos [col]] += *S ; */ + ASSEMBLE (Frow [Fcpos [col]], *S) ; + S += nrows ; + } + + } + else + { + /* -------------------------------------------------- */ + /* some columns have been assembled out of this Lson */ + /* -------------------------------------------------- */ + +#pragma ivdep + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { + ASSERT (col < Work->n_col) ; +#ifndef FIXQ + Col_degree [col] -- ; +#endif + /* Frow [Fcpos [col]] += *S ; */ + ASSEMBLE (Frow [Fcpos [col]], *S) ; + } + S += nrows ; + } + + } + ep->nrowsleft-- ; + ASSERT (ep->nrowsleft > 0) ; + } + else + { + *tp2++ = *tp ; /* leave the tuple in the list */ + } + } + Row_tlen [row] = tp2 - tp1 ; + +#ifndef NDEBUG + DEBUG7 (("row assembled in scan2-row: "ID"\n", row)) ; + UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ; + DEBUG7 (("Current frontal matrix: (scan 1b)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif +} + +/* ========================================================================== */ +/* === col_assemble ========================================================= */ +/* ========================================================================== */ + +PRIVATE void col_assemble +( + Int col, + NumericType *Numeric, + WorkType *Work +) +{ + + Int tpi, e, *E, *Fcpos, *Frpos, *Row_degree, *Col_tuples, *Col_tlen, cdeg0, + f, nrows, ncols, *Rows, *Cols, row, nrowsleft, i ; + Tuple *tp, *tp1, *tp2, *tpend ; + Unit *Memory, *p ; + Element *ep ; + Entry *S, *Fcblock, *Fcol ; + +#if !defined (FIXQ) || !defined (NDEBUG) + Int *Col_degree ; + Col_degree = Numeric->Cperm ; +#endif + + Col_tuples = Numeric->Lip ; + tpi = Col_tuples [col] ; + if (!tpi) return ; + + Memory = Numeric->Memory ; + E = Work->E ; + Fcpos = Work->Fcpos ; + Frpos = Work->Frpos ; + Row_degree = Numeric->Rperm ; + Col_tlen = Numeric->Lilen ; + E = Work->E ; + Memory = Numeric->Memory ; + cdeg0 = Work->cdeg0 ; + Fcblock = Work->Fcblock ; + + DEBUG6 (("SCAN2-col: "ID"\n", col)) ; +#ifndef NDEBUG + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; +#endif + + ASSERT (NON_PIVOTAL_COL (col)) ; + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Col_tlen [col] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + + if (Cols [f] == EMPTY) continue ; /* col already assembled */ + ASSERT (col == Cols [f] && col >= 0 && col < Work->n_col) ; + + if (ep->cdeg == cdeg0) + { + /* ------------------------------------------------------ */ + /* this is an old Uson - assemble just one col */ + /* ------------------------------------------------------ */ + + /* flag the col as assembled from the Uson */ + Cols [f] = EMPTY ; + + nrows = ep->nrows ; + ncols = ep->ncols ; + Rows = Cols + ncols ; + p += UNITS (Int, ncols + nrows) ; + S = ((Entry *) p) + f * nrows ; + + DEBUG6 (("Old USON: "ID"\n", e)) ; +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + + nrowsleft = ep->nrowsleft ; + + Fcol = Fcblock + Fcpos [col] ; + DEBUG6 (("USON found (in scan2-col): "ID"\n", e)) ; +#ifndef FIXQ + Col_degree [col] -= nrowsleft ; +#endif + if (nrows == nrowsleft) + { + /* -------------------------------------------------- */ + /* no rows assembled out of this Uson yet */ + /* -------------------------------------------------- */ + +#pragma ivdep + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + ASSERT (row >= 0 && row < Work->n_row) ; + Row_degree [row]-- ; + /* Fcol [Frpos [row]] += S [i] ; */ + ASSEMBLE (Fcol [Frpos [row]], S [i]) ; + } + } + else + { + /* -------------------------------------------------- */ + /* some rows have been assembled out of this Uson */ + /* -------------------------------------------------- */ + +#pragma ivdep + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + ASSERT (row < Work->n_row) ; + Row_degree [row]-- ; + /* Fcol [Frpos [row]] += S [i] ; */ + ASSEMBLE (Fcol [Frpos [row]], S [i]) ; + } + } + } + ep->ncolsleft-- ; + ASSERT (ep->ncolsleft > 0) ; + } + else + { + *tp2++ = *tp ; /* leave the tuple in the list */ + } + } + Col_tlen [col] = tp2 - tp1 ; + +#ifndef NDEBUG + DEBUG7 (("Column assembled in scan2-col: "ID"\n", col)) ; + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; + DEBUG7 (("Current frontal matrix: after scan2-col\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + +} + + +/* ========================================================================== */ +/* === UMF_assemble / UMF_assemble_fixq ===================================== */ +/* ========================================================================== */ + +#ifndef FIXQ +GLOBAL void UMF_assemble +#else +GLOBAL void UMF_assemble_fixq +#endif +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int e, i, row, col, i2, nrows, ncols, f, tpi, extcdeg, extrdeg, rdeg0, + cdeg0, son_list, next, nrows_to_assemble, + ncols_to_assemble, ngetrows, j, j2, + nrowsleft, /* number of rows remaining in S */ + ncolsleft, /* number of columns remaining in S */ + prior_Lson, prior_Uson, *E, *Cols, *Rows, *Wm, *Woo, + *Row_tuples, *Row_degree, *Row_tlen, + *Col_tuples, *Col_tlen ; + Unit *Memory, *p ; + Element *ep ; + Tuple *tp, *tp1, *tp2, *tpend ; + Entry + *S, /* a pointer into the contribution block of a son */ + *Fcblock, /* current contribution block */ + *Fcol ; /* a column of FC */ + Int *Frpos, + *Fcpos, + fnrows, /* number of rows in contribution block in F */ + fncols ; /* number of columns in contribution block in F */ + +#if !defined (FIXQ) || !defined (NDEBUG) + Int *Col_degree ; +#endif + +#ifndef NDEBUG + Int n_row, n_col ; + n_row = Work->n_row ; + n_col = Work->n_col ; + DEBUG3 (("::Assemble SCANS 1-4\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + +#if !defined (FIXQ) || !defined (NDEBUG) + Col_degree = Numeric->Cperm ; /* not updated if FIXQ is true */ +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + fncols = Work->fncols ; + fnrows = Work->fnrows ; + Fcpos = Work->Fcpos ; + Frpos = Work->Frpos ; + Row_degree = Numeric->Rperm ; + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + E = Work->E ; + Memory = Numeric->Memory ; + Wm = Work->Wm ; + Woo = Work->Woo ; + rdeg0 = Work->rdeg0 ; + cdeg0 = Work->cdeg0 ; + +#ifndef NDEBUG + DEBUG6 (("============================================\n")) ; + DEBUG6 (("Degree update, assembly.\n")) ; + DEBUG6 (("pivot row pattern: fncols="ID"\n", fncols)) ; + for (j = 0 ; j < fncols ; j++) + { + col = Work->Fcols [j] ; + DEBUG6 ((ID" ", col)) ; + ASSERT (Fcpos [col] == j * Work->fnr_curr) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + } + ASSERT (Fcpos [Work->pivcol] >= 0) ; + DEBUG6 (("pivcol: "ID" pos "ID" fnr_curr "ID" fncols "ID"\n", + Work->pivcol, Fcpos [Work->pivcol], Work->fnr_curr, fncols)) ; + ASSERT (Fcpos [Work->pivcol] < fncols * Work->fnr_curr) ; + DEBUG6 (("\npivot col pattern: fnrows="ID"\n", fnrows)) ; + for (i = 0 ; i < fnrows ; i++) + { + row = Work->Frows [i] ; + DEBUG6 ((ID" ", row)) ; + ASSERT (Frpos [row] == i) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + } + DEBUG6 (("\n")) ; + ASSERT (Frpos [Work->pivrow] >= 0) ; + ASSERT (Frpos [Work->pivrow] < fnrows) ; + ASSERT (Work->Flublock == (Entry *) (Numeric->Memory + E [0])) ; + ASSERT (Work->Fcblock == Work->Flublock + Work->nb * + (Work->nb + Work->fnr_curr + Work->fnc_curr)) ; +#endif + + Fcblock = Work->Fcblock ; + + /* ---------------------------------------------------------------------- */ + /* determine the largest actual frontal matrix size (for Info only) */ + /* ---------------------------------------------------------------------- */ + + ASSERT (fnrows == Work->fnrows_new + 1) ; + ASSERT (fncols == Work->fncols_new + 1) ; + + Numeric->maxnrows = MAX (Numeric->maxnrows, fnrows) ; + Numeric->maxncols = MAX (Numeric->maxncols, fncols) ; + + /* this is safe from integer overflow, since the current frontal matrix + * is already allocated. */ + Numeric->maxfrsize = MAX (Numeric->maxfrsize, fnrows * fncols) ; + + /* ---------------------------------------------------------------------- */ + /* assemble from prior elements into the current frontal matrix */ + /* ---------------------------------------------------------------------- */ + + DEBUG2 (("New assemble start [prior_element:"ID"\n", Work->prior_element)) ; + + /* Currently no rows or columns are marked. No elements are scanned, */ + /* that is, (ep->next == EMPTY) is true for all elements */ + + son_list = 0 ; /* start creating son_list [ */ + + /* ---------------------------------------------------------------------- */ + /* determine if most recent element is Lson or Uson of current front */ + /* ---------------------------------------------------------------------- */ + + if (!Work->do_extend) + { + prior_Uson = ( Work->pivcol_in_front && !Work->pivrow_in_front) ; + prior_Lson = (!Work->pivcol_in_front && Work->pivrow_in_front) ; + if (prior_Uson || prior_Lson) + { + e = Work->prior_element ; + if (e != EMPTY) + { + ASSERT (E [e]) ; + p = Memory + E [e] ; + ep = (Element *) p ; + ep->next = son_list ; + son_list = e ; +#ifndef NDEBUG + DEBUG2 (("e "ID" is Prior son "ID" "ID"\n", + e, prior_Uson, prior_Lson)) ; + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + ASSERT (E [e]) ; + } + } + } + Work->prior_element = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* SCAN1-row: scan the element lists of each new row in the pivot col */ + /* and compute the external column degree for each frontal */ + /* ---------------------------------------------------------------------- */ + + for (i2 = Work->fscan_row ; i2 < fnrows ; i2++) + { + /* Get a row */ + row = Work->NewRows [i2] ; + if (row < 0) row = FLIP (row) ; + ASSERT (row >= 0 && row < n_row) ; + + DEBUG6 (("SCAN1-row: "ID"\n", row)) ; +#ifndef NDEBUG + UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ; +#endif + + ASSERT (NON_PIVOTAL_ROW (row)) ; + tpi = Row_tuples [row] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Row_tlen [row] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Rows = ((Int *) p) + ep->ncols ; + if (Rows [f] == EMPTY) continue ; /* row already assembled */ + ASSERT (row == Rows [f]) ; + + if (ep->cdeg < cdeg0) + { + /* first time seen in scan1-row */ + ep->cdeg = ep->nrowsleft + cdeg0 ; + DEBUG6 (("e "ID" First seen: cdeg: "ID" ", e, ep->cdeg-cdeg0)) ; + ASSERT (ep->ncolsleft > 0 && ep->nrowsleft > 0) ; + } + + ep->cdeg-- ; /* decrement external column degree */ + DEBUG6 (("e "ID" New ext col deg: "ID"\n", e, ep->cdeg - cdeg0)) ; + + /* this element is not yet in the new son list */ + if (ep->cdeg == cdeg0 && ep->next == EMPTY) + { + /* A new LUson or Uson has been found */ + ep->next = son_list ; + son_list = e ; + } + + ASSERT (ep->cdeg >= cdeg0) ; + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Row_tlen [row] = tp2 - tp1 ; + } + + /* ---------------------------------------------------------------------- */ + /* SCAN1-col: scan the element lists of each new col in the pivot row */ + /* and compute the external row degree for each frontal */ + /* ---------------------------------------------------------------------- */ + + for (j2 = Work->fscan_col ; j2 < fncols ; j2++) + { + /* Get a column */ + col = Work->NewCols [j2] ; + if (col < 0) col = FLIP (col) ; + ASSERT (col >= 0 && col < n_col) ; + + DEBUG6 (("SCAN 1-col: "ID"\n", col)) ; +#ifndef NDEBUG + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; +#endif + + ASSERT (NON_PIVOTAL_COL (col)) ; + tpi = Col_tuples [col] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Col_tlen [col] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + if (Cols [f] == EMPTY) continue ; /* column already assembled */ + ASSERT (col == Cols [f]) ; + + if (ep->rdeg < rdeg0) + { + /* first time seen in scan1-col */ + ep->rdeg = ep->ncolsleft + rdeg0 ; + DEBUG6 (("e "ID" First seen: rdeg: "ID" ", e, ep->rdeg-rdeg0)) ; + ASSERT (ep->ncolsleft > 0 && ep->nrowsleft > 0) ; + } + + ep->rdeg-- ; /* decrement external row degree */ + DEBUG6 (("e "ID" New ext row degree: "ID"\n", e, ep->rdeg-rdeg0)) ; + + if (ep->rdeg == rdeg0 && ep->next == EMPTY) + { + /* A new LUson or Lson has been found */ + ep->next = son_list ; + son_list = e ; + } + + ASSERT (ep->rdeg >= rdeg0) ; + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Col_tlen [col] = tp2 - tp1 ; + } + + /* ---------------------------------------------------------------------- */ + /* assemble new sons via full scans */ + /* ---------------------------------------------------------------------- */ + + next = EMPTY ; + + for (e = son_list ; e > 0 ; e = next) + { + ASSERT (e > 0 && e <= Work->nel && E [e]) ; + p = Memory + E [e] ; + DEBUG2 (("New son: "ID"\n", e)) ; +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, S) ; + nrowsleft = ep->nrowsleft ; + ncolsleft = ep->ncolsleft ; + next = ep->next ; + ep->next = EMPTY ; + + extrdeg = (ep->rdeg < rdeg0) ? ncolsleft : (ep->rdeg - rdeg0) ; + extcdeg = (ep->cdeg < cdeg0) ? nrowsleft : (ep->cdeg - cdeg0) ; + ncols_to_assemble = ncolsleft - extrdeg ; + nrows_to_assemble = nrowsleft - extcdeg ; + DEBUG2 (("extrdeg "ID" extcdeg "ID"\n", extrdeg, extcdeg)) ; + + if (extrdeg == 0 && extcdeg == 0) + { + + /* -------------------------------------------------------------- */ + /* this is an LUson - assemble an entire contribution block */ + /* -------------------------------------------------------------- */ + + DEBUG6 (("LUson found: "ID"\n", e)) ; + + if (nrows == nrowsleft) + { + /* ---------------------------------------------------------- */ + /* no rows assembled out of this LUson yet */ + /* ---------------------------------------------------------- */ + + /* compute the compressed column offset vector*/ + /* [ use Wm [0..nrows-1] for offsets */ +#pragma ivdep + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + Row_degree [row] -= ncolsleft ; + Wm [i] = Frpos [row] ; + } + + if (ncols == ncolsleft) + { + /* ------------------------------------------------------ */ + /* no rows or cols assembled out of LUson yet */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; +#ifndef FIXQ + Col_degree [col] -= nrowsleft ; +#endif + Fcol = Fcblock + Fcpos [col] ; +#pragma ivdep + for (i = 0 ; i < nrows ; i++) + { + /* Fcol [Wm [i]] += S [i] ; */ + ASSEMBLE (Fcol [Wm [i]], S [i]) ; + } + S += nrows ; + } + + + } + else + { + /* ------------------------------------------------------ */ + /* only cols have been assembled out of LUson */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { +#ifndef FIXQ + Col_degree [col] -= nrowsleft ; +#endif + Fcol = Fcblock + Fcpos [col] ; +#pragma ivdep + for (i = 0 ; i < nrows ; i++) + { + /* Fcol [Wm [i]] += S [i] ; */ + ASSEMBLE (Fcol [Wm [i]], S [i]) ; + } + } + S += nrows ; + } + + } + /* ] done using Wm [0..nrows-1] for offsets */ + } + else + { + /* ---------------------------------------------------------- */ + /* some rows have been assembled out of this LUson */ + /* ---------------------------------------------------------- */ + + /* compute the compressed column offset vector*/ + /* [ use Woo,Wm [0..nrowsleft-1] for offsets */ + ngetrows = 0 ; + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + Row_degree [row] -= ncolsleft ; + Woo [ngetrows] = i ; + Wm [ngetrows++] = Frpos [row] ; + } + } + ASSERT (ngetrows == nrowsleft) ; + + if (ncols == ncolsleft) + { + /* ------------------------------------------------------ */ + /* only rows have been assembled out of this LUson */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; +#ifndef FIXQ + Col_degree [col] -= nrowsleft ; +#endif + Fcol = Fcblock + Fcpos [col] ; +#pragma ivdep + for (i = 0 ; i < nrowsleft ; i++) + { + /* Fcol [Wm [i]] += S [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], S [Woo [i]]) ; + } + S += nrows ; + } + + } + else + { + /* ------------------------------------------------------ */ + /* both rows and columns have been assembled out of LUson */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { +#ifndef FIXQ + Col_degree [col] -= nrowsleft ; +#endif + Fcol = Fcblock + Fcpos [col] ; +#pragma ivdep + for (i = 0 ; i < nrowsleft ; i++) + { + /* Fcol [Wm [i]] += S [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], S [Woo [i]]) ; + } + } + S += nrows ; + } + + } + /* ] done using Woo,Wm [0..nrowsleft-1] */ + } + + /* deallocate the element: remove from ordered list */ + UMF_mem_free_tail_block (Numeric, E [e]) ; + E [e] = 0 ; + + } + else if (extcdeg == 0) + { + + /* -------------------------------------------------------------- */ + /* this is a Uson - assemble all possible columns */ + /* -------------------------------------------------------------- */ + + DEBUG6 (("New USON: "ID"\n", e)) ; + ASSERT (extrdeg > 0) ; + + DEBUG6 (("New uson "ID" cols to do "ID"\n", e, ncols_to_assemble)) ; + + if (ncols_to_assemble > 0) + { + + Int skip = FALSE ; + if (ncols_to_assemble * 16 < ncols && nrows == 1) + { + /* this is a tall and thin frontal matrix consisting of + * only one column (most likely an original column). Do + * not assemble it. It cannot be the pivot column, since + * the pivot column element would be an LU son, not an Lson, + * of the current frontal matrix. */ + ASSERT (nrowsleft == 1) ; + ASSERT (Rows [0] >= 0 && Rows [0] < Work->n_row) ; + skip = TRUE ; + Work->any_skip = TRUE ; + } + + if (!skip) + { + + if (nrows == nrowsleft) + { + /* -------------------------------------------------- */ + /* no rows have been assembled out of this Uson yet */ + /* -------------------------------------------------- */ + + /* compute the compressed column offset vector */ + /* [ use Wm [0..nrows-1] for offsets */ +#pragma ivdep + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + ASSERT (row >= 0 && row < n_row) ; + Row_degree [row] -= ncols_to_assemble ; + Wm [i] = Frpos [row] ; + } + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if ((col >= 0) && (Fcpos [col] >= 0)) + { +#ifndef FIXQ + Col_degree [col] -= nrowsleft ; +#endif + Fcol = Fcblock + Fcpos [col] ; +#pragma ivdep + for (i = 0 ; i < nrows ; i++) + { + /* Fcol [Wm [i]] += S [i] ; */ + ASSEMBLE (Fcol [Wm [i]], S [i]) ; + } + /* flag the column as assembled from Uson */ + Cols [j] = EMPTY ; + } + S += nrows ; + } + + + /* ] done using Wm [0..nrows-1] for offsets */ + } + else + { + /* -------------------------------------------------- */ + /* some rows have been assembled out of this Uson */ + /* -------------------------------------------------- */ + + /* compute the compressed column offset vector*/ + /* [ use Woo,Wm [0..nrows-1] for offsets */ + ngetrows = 0 ; + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + Row_degree [row] -= ncols_to_assemble ; + ASSERT (row < n_row && Frpos [row] >= 0) ; + Woo [ngetrows] = i ; + Wm [ngetrows++] = Frpos [row] ; + } + } + ASSERT (ngetrows == nrowsleft) ; + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if ((col >= 0) && (Fcpos [col] >= 0)) + { +#ifndef FIXQ + Col_degree [col] -= nrowsleft ; +#endif + Fcol = Fcblock + Fcpos [col] ; +#pragma ivdep + for (i = 0 ; i < nrowsleft ; i++) + { + /* Fcol [Wm [i]] += S [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], S [Woo [i]]) ; + } + /* flag the column as assembled from Uson */ + Cols [j] = EMPTY ; + } + S += nrows ; + } + + /* ] done using Woo,Wm */ + } + ep->ncolsleft = extrdeg ; + } + } + + } + else + { + + /* -------------------------------------------------------------- */ + /* this is an Lson - assemble all possible rows */ + /* -------------------------------------------------------------- */ + + DEBUG6 (("New LSON: "ID"\n", e)) ; + ASSERT (extrdeg == 0 && extcdeg > 0) ; + + DEBUG6 (("New lson "ID" rows to do "ID"\n", e, nrows_to_assemble)) ; + + if (nrows_to_assemble > 0) + { + + Int skip = FALSE ; + if (nrows_to_assemble * 16 < nrows && ncols == 1) + { + /* this is a tall and thin frontal matrix consisting of + * only one column (most likely an original column). Do + * not assemble it. It cannot be the pivot column, since + * the pivot column element would be an LU son, not an Lson, + * of the current frontal matrix. */ + ASSERT (ncolsleft == 1) ; + ASSERT (Cols [0] >= 0 && Cols [0] < Work->n_col) ; + Work->any_skip = TRUE ; + skip = TRUE ; + } + + if (!skip) + { + + /* compute the compressed column offset vector */ + /* [ use Woo,Wm [0..nrows-1] for offsets */ + ngetrows = 0 ; + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if ((row >= 0) && (Frpos [row] >= 0)) + { + ASSERT (row < n_row) ; + Row_degree [row] -= ncolsleft ; + Woo [ngetrows] = i ; + Wm [ngetrows++] = Frpos [row] ; + /* flag the row as assembled from the Lson */ + Rows [i] = EMPTY ; + } + } + ASSERT (nrowsleft - ngetrows == extcdeg) ; + ASSERT (ngetrows == nrows_to_assemble) ; + + if (ncols == ncolsleft) + { + /* -------------------------------------------------- */ + /* no columns assembled out this Lson yet */ + /* -------------------------------------------------- */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + ASSERT (col >= 0 && col < n_col) ; +#ifndef FIXQ + Col_degree [col] -= nrows_to_assemble ; +#endif + Fcol = Fcblock + Fcpos [col] ; +#pragma ivdep + for (i = 0 ; i < nrows_to_assemble ; i++) + { + /* Fcol [Wm [i]] += S [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], S [Woo [i]]) ; + } + S += nrows ; + } + + + } + else + { + /* -------------------------------------------------- */ + /* some columns have been assembled out of this Lson */ + /* -------------------------------------------------- */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + ASSERT (col < n_col) ; + if (col >= 0) + { +#ifndef FIXQ + Col_degree [col] -= nrows_to_assemble ; +#endif + Fcol = Fcblock + Fcpos [col] ; +#pragma ivdep + for (i = 0 ; i < nrows_to_assemble ; i++) + { + /* Fcol [Wm [i]] += S [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], S [Woo [i]]) ; + } + } + S += nrows ; + } + + } + + /* ] done using Woo,Wm */ + + ep->nrowsleft = extcdeg ; + } + } + } + } + + /* Note that garbage collection, and build tuples */ + /* both destroy the son list. */ + + /* ] son_list now empty */ + + /* ---------------------------------------------------------------------- */ + /* If frontal matrix extended, assemble old L/Usons from new rows/cols */ + /* ---------------------------------------------------------------------- */ + + /* ---------------------------------------------------------------------- */ + /* SCAN2-row: assemble rows of old Lsons from the new rows */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG7 (("Current frontal matrix: (prior to scan2-row)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + /* rescan the pivot row */ + if (Work->any_skip) + { + row_assemble (Work->pivrow, Numeric, Work) ; + } + + if (Work->do_scan2row) + { + for (i2 = Work->fscan_row ; i2 < fnrows ; i2++) + { + /* Get a row */ + row = Work->NewRows [i2] ; + if (row < 0) row = FLIP (row) ; + ASSERT (row >= 0 && row < n_row) ; + if (!(row == Work->pivrow && Work->any_skip)) + { + /* assemble it */ + row_assemble (row, Numeric, Work) ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* SCAN2-col: assemble columns of old Usons from the new columns */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG7 (("Current frontal matrix: (prior to scan2-col)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + /* rescan the pivot col */ + if (Work->any_skip) + { + col_assemble (Work->pivcol, Numeric, Work) ; + } + + if (Work->do_scan2col) + { + + for (j2 = Work->fscan_col ; j2 < fncols ; j2++) + { + /* Get a column */ + col = Work->NewCols [j2] ; + if (col < 0) col = FLIP (col) ; + ASSERT (col >= 0 && col < n_col) ; + if (!(col == Work->pivcol && Work->any_skip)) + { + /* assemble it */ + col_assemble (col, Numeric, Work) ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* done. the remainder of this routine is used only when in debug mode */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + + /* ---------------------------------------------------------------------- */ + /* when debugging: make sure the assembly did everything that it could */ + /* ---------------------------------------------------------------------- */ + + DEBUG3 (("::Assemble done\n")) ; + + for (i2 = 0 ; i2 < fnrows ; i2++) + { + /* Get a row */ + row = Work->Frows [i2] ; + ASSERT (row >= 0 && row < n_row) ; + + DEBUG6 (("DEBUG SCAN 1: "ID"\n", row)) ; + UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ; + + ASSERT (NON_PIVOTAL_ROW (row)) ; + tpi = Row_tuples [row] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tpend = tp + Row_tlen [row] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + Rows = ((Int *) p) + ep->ncols ; + if (Rows [f] == EMPTY) continue ; /* row already assembled */ + ASSERT (row == Rows [f]) ; + extrdeg = (ep->rdeg < rdeg0) ? ep->ncolsleft : (ep->rdeg - rdeg0) ; + extcdeg = (ep->cdeg < cdeg0) ? ep->nrowsleft : (ep->cdeg - cdeg0) ; + DEBUG6 (( + "e "ID" After assembly ext row deg: "ID" ext col degree "ID"\n", + e, extrdeg, extcdeg)) ; + + if (Work->any_skip) + { + /* no Lsons in any row, except for very tall and thin ones */ + ASSERT (extrdeg >= 0) ; + if (extrdeg == 0) + { + /* this is an unassemble Lson */ + ASSERT (ep->ncols == 1) ; + ASSERT (ep->ncolsleft == 1) ; + col = Cols [0] ; + ASSERT (col != Work->pivcol) ; + } + } + else + { + /* no Lsons in any row */ + ASSERT (extrdeg > 0) ; + /* Uson external row degree is = number of cols left */ + ASSERT (IMPLIES (extcdeg == 0, extrdeg == ep->ncolsleft)) ; + } + } + } + + /* ---------------------------------------------------------------------- */ + + for (j2 = 0 ; j2 < fncols ; j2++) + { + /* Get a column */ + col = Work->Fcols [j2] ; + ASSERT (col >= 0 && col < n_col) ; + + DEBUG6 (("DEBUG SCAN 2: "ID"\n", col)) ; +#ifndef FIXQ + UMF_dump_rowcol (1, Numeric, Work, col, TRUE) ; +#else + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; +#endif + + ASSERT (NON_PIVOTAL_COL (col)) ; + tpi = Col_tuples [col] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tpend = tp + Col_tlen [col] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + Rows = ((Int *) p) + ep->ncols ; + if (Cols [f] == EMPTY) continue ; /* column already assembled */ + ASSERT (col == Cols [f]) ; + extrdeg = (ep->rdeg < rdeg0) ? ep->ncolsleft : (ep->rdeg - rdeg0) ; + extcdeg = (ep->cdeg < cdeg0) ? ep->nrowsleft : (ep->cdeg - cdeg0) ; + DEBUG6 (("e "ID" After assembly ext col deg: "ID"\n", e, extcdeg)) ; + + if (Work->any_skip) + { + /* no Usons in any column, except for very tall and thin ones */ + ASSERT (extcdeg >= 0) ; + if (extcdeg == 0) + { + /* this is an unassemble Uson */ + ASSERT (ep->nrows == 1) ; + ASSERT (ep->nrowsleft == 1) ; + row = Rows [0] ; + ASSERT (row != Work->pivrow) ; + } + } + else + { + /* no Usons in any column */ + ASSERT (extcdeg > 0) ; + /* Lson external column degree is = number of rows left */ + ASSERT (IMPLIES (extrdeg == 0, extcdeg == ep->nrowsleft)) ; + } + } + } +#endif /* NDEBUG */ +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_assemble.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_assemble.h new file mode 100644 index 0000000000..4cbfc57556 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_assemble.h @@ -0,0 +1,18 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_assemble +( + NumericType *Numeric, + WorkType *Work +) ; + +GLOBAL void UMF_assemble_fixq +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_blas3_update.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_blas3_update.c new file mode 100644 index 0000000000..acceb5f775 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_blas3_update.c @@ -0,0 +1,165 @@ +/* ========================================================================== */ +/* === UMF_blas3_update ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" + +GLOBAL void UMF_blas3_update +( + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Entry *L, *U, *C, *LU ; + Int k, m, n, d, nb, dc ; + + DEBUG5 (("In UMF_blas3_update "ID" "ID" "ID"\n", + Work->fnpiv, Work->fnrows, Work->fncols)) ; + + k = Work->fnpiv ; + if (k == 0) + { + /* no work to do */ + return ; + } + + m = Work->fnrows ; + n = Work->fncols ; + + d = Work->fnr_curr ; + dc = Work->fnc_curr ; + nb = Work->nb ; + ASSERT (d >= 0 && (d % 2) == 1) ; + C = Work->Fcblock ; /* ldc is fnr_curr */ + L = Work->Flblock ; /* ldl is fnr_curr */ + U = Work->Fublock ; /* ldu is fnc_curr, stored by rows */ + LU = Work->Flublock ; /* nb-by-nb */ + +#ifndef NDEBUG + DEBUG5 (("DO RANK-NB UPDATE of frontal:\n")) ; + DEBUG5 (("DGEMM : "ID" "ID" "ID"\n", k, m, n)) ; + DEBUG7 (("C block: ")) ; UMF_dump_dense (C, d, m, n) ; + DEBUG7 (("A block: ")) ; UMF_dump_dense (L, d, m, k) ; + DEBUG7 (("B' block: ")) ; UMF_dump_dense (U, dc, n, k) ; + DEBUG7 (("LU block: ")) ; UMF_dump_dense (LU, nb, k, k) ; +#endif + + if (k == 1) + { + +#ifdef USE_NO_BLAS + + /* no BLAS available - use plain C code instead */ + Int i, j ; + + /* rank-1 outer product to update the C block */ + for (j = 0 ; j < n ; j++) + { + Entry u_j = U [j] ; + if (IS_NONZERO (u_j)) + { + Entry *c_ij, *l_is ; + c_ij = & C [j*d] ; + l_is = & L [0] ; +#pragma ivdep + for (i = 0 ; i < m ; i++) + { + /* C [i+j*d]-= L [i] * U [j] */ + MULT_SUB (*c_ij, *l_is, u_j) ; + c_ij++ ; + l_is++ ; + } + } + } + +#else + BLAS_GER (m, n, L, U, C, d) ; + +#endif /* USE_NO_BLAS */ + + } + else + { + +#ifdef USE_NO_BLAS + + /* no BLAS available - use plain C code instead */ + Int i, j, s ; + + /* triangular solve to update the U block */ + for (s = 0 ; s < k ; s++) + { + for (i = s+1 ; i < k ; i++) + { + Entry l_is = LU [i+s*nb] ; + if (IS_NONZERO (l_is)) + { + Entry *u_ij, *u_sj ; + u_ij = & U [i*dc] ; + u_sj = & U [s*dc] ; +#pragma ivdep + for (j = 0 ; j < n ; j++) + { + /* U [i*dc+j] -= LU [i+s*nb] * U [s*dc+j] ; */ + MULT_SUB (*u_ij, l_is, *u_sj) ; + u_ij++ ; + u_sj++ ; + } + } + } + } + + /* rank-k outer product to update the C block */ + /* C = C - L*U' (U is stored by rows, not columns) */ + for (s = 0 ; s < k ; s++) + { + for (j = 0 ; j < n ; j++) + { + Entry u_sj = U [j+s*dc] ; + if (IS_NONZERO (u_sj)) + { + Entry *c_ij, *l_is ; + c_ij = & C [j*d] ; + l_is = & L [s*d] ; +#pragma ivdep + for (i = 0 ; i < m ; i++) + { + /* C [i+j*d]-= L [i+s*d] * U [s*dc+j] */ + MULT_SUB (*c_ij, *l_is, u_sj) ; + c_ij++ ; + l_is++ ; + } + } + } + } + +#else + + BLAS_TRSM_RIGHT (n, k, LU, nb, U, dc) ; + BLAS_GEMM (m, n, k, L, U, dc, C, d) ; + +#endif /* USE_NO_BLAS */ + + } + +#ifndef NDEBUG + DEBUG5 (("RANK-NB UPDATE of frontal done:\n")) ; + DEBUG5 (("DGEMM : "ID" "ID" "ID"\n", k, m, n)) ; + DEBUG7 (("C block: ")) ; UMF_dump_dense (C, d, m, n) ; + DEBUG7 (("A block: ")) ; UMF_dump_dense (L, d, m, k) ; + DEBUG7 (("B' block: ")) ; UMF_dump_dense (U, dc, n, k) ; + DEBUG7 (("LU block: ")) ; UMF_dump_dense (LU, nb, k, k) ; +#endif + + DEBUG2 (("blas3 "ID" "ID" "ID"\n", k, Work->fnrows, Work->fncols)) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_blas3_update.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_blas3_update.h new file mode 100644 index 0000000000..2285d97fdc --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_blas3_update.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_blas3_update +( + WorkType *Work +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_build_tuples.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_build_tuples.c new file mode 100644 index 0000000000..cabb7e0239 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_build_tuples.c @@ -0,0 +1,160 @@ +/* ========================================================================== */ +/* === UMF_build_tuples ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Construct the tuple lists from a set of packed elements (no holes in + elements, no internal or external fragmentation, and a packed (0..Work->nel) + element name space). Assume no tuple lists are currently allocated, but + that the tuple lengths have been initialized by UMF_tuple_lengths. + + Returns TRUE if successful, FALSE if not enough memory. +*/ + +#include "umf_internal.h" +#include "umf_mem_alloc_tail_block.h" + +GLOBAL Int UMF_build_tuples +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int e, nrows, ncols, nel, *Rows, *Cols, row, col, n_row, n_col, *E, + *Row_tuples, *Row_degree, *Row_tlen, + *Col_tuples, *Col_degree, *Col_tlen, n1 ; + Element *ep ; + Unit *p ; + Tuple tuple, *tp ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + E = Work->E ; + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + n_row = Work->n_row ; + n_col = Work->n_col ; + nel = Work->nel ; + n1 = Work->n1 ; + + DEBUG3 (("BUILD_TUPLES: n_row "ID" n_col "ID" nel "ID"\n", + n_row, n_col, nel)) ; + + /* ---------------------------------------------------------------------- */ + /* allocate space for the tuple lists */ + /* ---------------------------------------------------------------------- */ + + /* Garbage collection and memory reallocation have already attempted to */ + /* ensure that there is enough memory for all the tuple lists. If */ + /* memory allocation fails here, then there is nothing more to be done. */ + + for (row = n1 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row)) + { + Row_tuples [row] = UMF_mem_alloc_tail_block (Numeric, + UNITS (Tuple, TUPLES (Row_tlen [row]))) ; + if (!Row_tuples [row]) + { + /* :: out of memory for row tuples :: */ + DEBUGm4 (("out of memory: build row tuples\n")) ; + return (FALSE) ; /* out of memory for row tuples */ + } + Row_tlen [row] = 0 ; + } + } + + /* push on stack in reverse order, so column tuples are in the order */ + /* that they will be deleted. */ + for (col = n_col-1 ; col >= n1 ; col--) + { + if (NON_PIVOTAL_COL (col)) + { + Col_tuples [col] = UMF_mem_alloc_tail_block (Numeric, + UNITS (Tuple, TUPLES (Col_tlen [col]))) ; + if (!Col_tuples [col]) + { + /* :: out of memory for col tuples :: */ + DEBUGm4 (("out of memory: build col tuples\n")) ; + return (FALSE) ; /* out of memory for col tuples */ + } + Col_tlen [col] = 0 ; + } + } + +#ifndef NDEBUG + UMF_dump_memory (Numeric) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* create the tuple lists (exclude element 0) */ + /* ---------------------------------------------------------------------- */ + + /* for all elements, in order of creation */ + for (e = 1 ; e <= nel ; e++) + { + DEBUG9 (("Adding tuples for element: "ID" at "ID"\n", e, E [e])) ; + ASSERT (E [e]) ; /* no external fragmentation */ + p = Numeric->Memory + E [e] ; + GET_ELEMENT_PATTERN (ep, p, Cols, Rows, ncols) ; + nrows = ep->nrows ; + ASSERT (e != 0) ; + ASSERT (e == 0 || nrows == ep->nrowsleft) ; + ASSERT (e == 0 || ncols == ep->ncolsleft) ; + tuple.e = e ; + for (tuple.f = 0 ; tuple.f < ncols ; tuple.f++) + { + col = Cols [tuple.f] ; + ASSERT (col >= n1 && col < n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + ASSERT (Col_tuples [col]) ; + tp = ((Tuple *) (Numeric->Memory + Col_tuples [col])) + + Col_tlen [col]++ ; + *tp = tuple ; +#ifndef NDEBUG + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; +#endif + } + for (tuple.f = 0 ; tuple.f < nrows ; tuple.f++) + { + row = Rows [tuple.f] ; + ASSERT (row >= n1 && row < n_row) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + ASSERT (Row_tuples [row]) ; + tp = ((Tuple *) (Numeric->Memory + Row_tuples [row])) + + Row_tlen [row]++ ; + *tp = tuple ; +#ifndef NDEBUG + UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ; +#endif + } + } + + /* ---------------------------------------------------------------------- */ + /* the tuple lists are now valid, and can be scanned */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + UMF_dump_memory (Numeric) ; + UMF_dump_matrix (Numeric, Work, FALSE) ; +#endif + DEBUG3 (("BUILD_TUPLES: done\n")) ; + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_build_tuples.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_build_tuples.h new file mode 100644 index 0000000000..a4b5ed1e6f --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_build_tuples.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_build_tuples +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_colamd.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_colamd.c new file mode 100644 index 0000000000..8a574dd183 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_colamd.c @@ -0,0 +1,3140 @@ +/* ========================================================================== */ +/* === UMF_colamd =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* +UMF_colamd: an approximate minimum degree column ordering algorithm, + used as a preordering for UMFPACK. + +NOTE: if this routine is used outside of UMFPACK, for a sparse Cholesky +factorization of (AQ)'*(AQ) or a QR factorization of A, then one line should +be removed (the "&& pivot_row_thickness > 0" expression). See the comment +regarding the Cholesky factorization, below. + +Purpose: + + Colamd computes a permutation Q such that the Cholesky factorization of + (AQ)'(AQ) has less fill-in and requires fewer floating point operations + than A'A. This also provides a good ordering for sparse partial + pivoting methods, P(AQ) = LU, where Q is computed prior to numerical + factorization, and P is computed during numerical factorization via + conventional partial pivoting with row interchanges. Colamd is the + column ordering method used in SuperLU, part of the ScaLAPACK library. + It is also available as built-in function in MATLAB Version 6, + available from MathWorks, Inc. (http://www.mathworks.com). This + routine can be used in place of colmmd in MATLAB. By default, the \ + and / operators in MATLAB perform a column ordering (using colmmd + or colamd) prior to LU factorization using sparse partial pivoting, + in the built-in MATLAB lu(A) routine. + + This code is derived from Colamd Version 2.0. + +Authors: + + The authors of the COLAMD code itself are Stefan I. Larimore and Timothy A. + Davis, University of Florida. The algorithm was developed in collaboration + with John Gilbert, Xerox PARC, and Esmond Ng, Oak Ridge National Laboratory. + The AMD metric on which this is based is by Patrick Amestoy, T. Davis, + and Iain Duff. + +Date: + + UMFPACK Version: see above. + COLAMD Version 2.0 was released on January 31, 2000. + +Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974, DMS-9803599, and CCR-0203270. + +UMFPACK: Copyright (c) 2003 by Timothy A. Davis. All Rights Reserved. + +See the UMFPACK README file for the License for your use of this code. + +Availability: + + Both UMFPACK and the original unmodified colamd/symamd library are + available at http://www.cise.ufl.edu/research/sparse. + +Changes for inclusion in UMFPACK: + + * symamd, symamd_report, and colamd_report removed + + * additional terms added to RowInfo, ColInfo, and stats + + * Frontal matrix information computed for UMFPACK + + * routines renamed + + * column elimination tree post-ordering incorporated. In the original + version 2.0, this was performed in colamd.m. + +For more information, see: + + Amestoy, P. R. and Davis, T. A. and Duff, I. S., + An approximate minimum degree ordering algorithm, + SIAM J. Matrix Analysis and Applic, vol 17, no 4., pp 886-905, 1996. + + Davis, T. A. and Gilbert, J. R. and Larimore, S. I. and Ng, E. G., + A column approximate minimum degree ordering algorithm, + Univ. of Florida, CISE Dept., TR-00-005, Gainesville, FL + Oct. 2000. Submitted to ACM Trans. Math. Softw. + +*/ + +/* ========================================================================== */ +/* === Description of user-callable routines ================================ */ +/* ========================================================================== */ + +/* + ---------------------------------------------------------------------------- + colamd_recommended: removed for UMFPACK + ---------------------------------------------------------------------------- + + ---------------------------------------------------------------------------- + colamd_set_defaults: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + colamd_set_defaults (double knobs [COLAMD_KNOBS]) ; + + Purpose: + + Sets the default parameters. The use of this routine is optional. + + Arguments: + + double knobs [COLAMD_KNOBS] ; Output only. + + Let c = knobs [COLAMD_DENSE_COL], r = knobs [COLAMD_DENSE_ROW]. + Colamd: rows with more than max (16, r*16*sqrt(n_col)) + entries are removed prior to ordering. Columns with more than + max (16, c*16*sqrt(n_row)) entries are removed prior to + ordering, and placed last in the output column ordering. + + Symamd: removed for UMFPACK. + + COLAMD_DENSE_ROW and COLAMD_DENSE_COL are defined as 0 and 1, + respectively, in colamd.h. Default values of these two knobs + are both 0.5. Currently, only knobs [0] and knobs [1] are + used, but future versions may use more knobs. If so, they will + be properly set to their defaults by the future version of + colamd_set_defaults, so that the code that calls colamd will + not need to change, assuming that you either use + colamd_set_defaults, or pass a (double *) NULL pointer as the + knobs array to colamd or symamd. + + knobs [COLAMD_AGGRESSIVE]: if nonzero, then perform aggressive + absorption. Otherwise, do not. This version does aggressive + absorption by default. COLAMD v2.1 (in MATLAB) always + does aggressive absorption (it doesn't have an option to turn + it off). + + ---------------------------------------------------------------------------- + colamd: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + Int UMF_colamd (Int n_row, Int n_col, Int Alen, Int *A, Int *p, + double knobs [COLAMD_KNOBS], Int stats [COLAMD_STATS]) ; + + Purpose: + + Computes a column ordering (Q) of A such that P(AQ)=LU or + (AQ)'AQ=LL' have less fill-in and require fewer floating point + operations than factorizing the unpermuted matrix A or A'A, + respectively. + + Returns: + + TRUE (1) if successful, FALSE (0) otherwise. + + Arguments: + + Int n_row ; Input argument. + + Number of rows in the matrix A. + Restriction: n_row >= 0. + Colamd returns FALSE if n_row is negative. + + Int n_col ; Input argument. + + Number of columns in the matrix A. + Restriction: n_col >= 0. + Colamd returns FALSE if n_col is negative. + + Int Alen ; Input argument. + + Restriction (see note): + Alen >= 2*nnz + 8*(n_col+1) + 6*(n_row+1) + n_col + Colamd returns FALSE if these conditions are not met. + + Note: this restriction makes an modest assumption regarding + the size of the two typedef's structures in colamd.h. + We do, however, guarantee that + + Alen >= UMF_COLAMD_RECOMMENDED (nnz, n_row, n_col) + + will be sufficient. + + Int A [Alen] ; Input and output argument. + + A is an integer array of size Alen. Alen must be at least as + large as the bare minimum value given above, but this is very + low, and can result in excessive run time. For best + performance, we recommend that Alen be greater than or equal to + UMF_COLAMD_RECOMMENDED (nnz, n_row, n_col), which adds + nnz/5 to the bare minimum value given above. + + On input, the row indices of the entries in column c of the + matrix are held in A [(p [c]) ... (p [c+1]-1)]. The row indices + in a given column c need not be in ascending order, and + duplicate row indices may be be present. However, colamd will + work a little faster if both of these conditions are met + (Colamd puts the matrix into this format, if it finds that the + the conditions are not met). + + The matrix is 0-based. That is, rows are in the range 0 to + n_row-1, and columns are in the range 0 to n_col-1. Colamd + returns FALSE if any row index is out of range. + + A holds the inverse permutation on output. + + Int p [n_col+1] ; Both input and output argument. + + p is an integer array of size n_col+1. On input, it holds the + "pointers" for the column form of the matrix A. Column c of + the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first + entry, p [0], must be zero, and p [c] <= p [c+1] must hold + for all c in the range 0 to n_col-1. The value p [n_col] is + thus the total number of entries in the pattern of the matrix A. + Colamd returns FALSE if these conditions are not met. + + On output, if colamd returns TRUE, the array p holds the column + permutation (Q, for P(AQ)=LU or (AQ)'(AQ)=LL'), where p [0] is + the first column index in the new ordering, and p [n_col-1] is + the last. That is, p [k] = j means that column j of A is the + kth pivot column, in AQ, where k is in the range 0 to n_col-1 + (p [0] = j means that column j of A is the first column in AQ). + + If colamd returns FALSE, then no permutation is returned, and + p is undefined on output. + + double knobs [COLAMD_KNOBS] ; Input argument. + + See colamd_set_defaults for a description. + The behavior is undefined if knobs contains NaN's. + (UMFPACK does not call umf_colamd with NaN-valued knobs). + + Int stats [COLAMD_STATS] ; Output argument. + + Statistics on the ordering, and error status. + See colamd.h for related definitions. + Colamd returns FALSE if stats is not present. + + stats [0]: number of dense or empty rows ignored. + + stats [1]: number of dense or empty columns ignored (and + ordered last in the output permutation p) + Note that a row can become "empty" if it + contains only "dense" and/or "empty" columns, + and similarly a column can become "empty" if it + only contains "dense" and/or "empty" rows. + + stats [2]: number of garbage collections performed. + This can be excessively high if Alen is close + to the minimum required value. + + stats [3]: status code. < 0 is an error code. + > 1 is a warning or notice. + + 0 OK. Each column of the input matrix contained + row indices in increasing order, with no + duplicates. + + -11 Columns of input matrix jumbled + (unsorted columns or duplicate entries). + + stats [4]: the bad column index + stats [5]: the bad row index + + -1 A is a null pointer + + -2 p is a null pointer + + -3 n_row is negative + + stats [4]: n_row + + -4 n_col is negative + + stats [4]: n_col + + -5 number of nonzeros in matrix is negative + + stats [4]: number of nonzeros, p [n_col] + + -6 p [0] is nonzero + + stats [4]: p [0] + + -7 A is too small + + stats [4]: required size + stats [5]: actual size (Alen) + + -8 a column has a zero or negative number of + entries (changed for UMFPACK) + + stats [4]: column with <= 0 entries + stats [5]: number of entries in col + + -9 a row index is out of bounds + + stats [4]: column with bad row index + stats [5]: bad row index + stats [6]: n_row, # of rows of matrx + + -10 unused + + -999 (unused; see symamd.c) + + Future versions may return more statistics in the stats array. + + Example: + + See http://www.cise.ufl.edu/~davis/colamd/example.c + for a complete example. + + To order the columns of a 5-by-4 matrix with 11 nonzero entries in + the following nonzero pattern + + x 0 x 0 + x 0 x x + 0 x x 0 + 0 0 x x + x x 0 0 + + with default knobs and no output statistics, do the following: + + #include "colamd.h" + #define ALEN UMF_COLAMD_RECOMMENDED (11, 5, 4) + Int A [ALEN] = {1, 2, 5, 3, 5, 1, 2, 3, 4, 2, 4} ; + Int p [ ] = {0, 3, 5, 9, 11} ; + Int stats [COLAMD_STATS] ; + UMF_colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ; + + The permutation is returned in the array p, and A is destroyed. + + + ---------------------------------------------------------------------------- + symamd: does not appear in this version for UMFPACK + ---------------------------------------------------------------------------- + + ---------------------------------------------------------------------------- + colamd_report: does not appear in this version for UMFPACK + ---------------------------------------------------------------------------- + + ---------------------------------------------------------------------------- + symamd_report: does not appear in this version for UMFPACK + ---------------------------------------------------------------------------- + +*/ + +/* ========================================================================== */ +/* === Scaffolding code definitions ======================================== */ +/* ========================================================================== */ + +/* UMFPACK debugging control moved to amd_internal.h */ + +/* + Our "scaffolding code" philosophy: In our opinion, well-written library + code should keep its "debugging" code, and just normally have it turned off + by the compiler so as not to interfere with performance. This serves + several purposes: + + (1) assertions act as comments to the reader, telling you what the code + expects at that point. All assertions will always be true (unless + there really is a bug, of course). + + (2) leaving in the scaffolding code assists anyone who would like to modify + the code, or understand the algorithm (by reading the debugging output, + one can get a glimpse into what the code is doing). + + (3) (gasp!) for actually finding bugs. This code has been heavily tested + and "should" be fully functional and bug-free ... but you never know... + + To enable debugging, comment out the "#define NDEBUG" above. For a MATLAB + mexFunction, you will also need to modify mexopts.sh to remove the -DNDEBUG + definition. The code will become outrageously slow when debugging is + enabled. To control the level of debugging output, set an environment + variable D to 0 (little), 1 (some), 2, 3, or 4 (lots). When debugging, + you should see the following message on the standard output: + + colamd: debug version, D = 1 (THIS WILL BE SLOW!) + + or a similar message for symamd. If you don't, then debugging has not + been enabled. + +*/ + +/* ========================================================================== */ +/* === Include files ======================================================== */ +/* ========================================================================== */ + +/* ------------------ */ +/* modified for UMFPACK: */ +#include "umf_internal.h" +#include "umf_colamd.h" +#include "umf_apply_order.h" +#include "umf_fsize.h" +/* ------------------ */ + +/* ========================================================================== */ +/* === Definitions ========================================================== */ +/* ========================================================================== */ + +/* ------------------ */ +/* UMFPACK: duplicate definitions moved to umf_internal.h */ +/* ------------------ */ + +/* Row and column status */ +#define ALIVE (0) +#define DEAD (-1) + +/* Column status */ +#define DEAD_PRINCIPAL (-1) +#define DEAD_NON_PRINCIPAL (-2) + +/* Macros for row and column status update and checking. */ +#define ROW_IS_DEAD(r) ROW_IS_MARKED_DEAD (Row[r].shared2.mark) +#define ROW_IS_MARKED_DEAD(row_mark) (row_mark < ALIVE) +#define ROW_IS_ALIVE(r) (Row [r].shared2.mark >= ALIVE) +#define COL_IS_DEAD(c) (Col [c].start < ALIVE) +#define COL_IS_ALIVE(c) (Col [c].start >= ALIVE) +#define COL_IS_DEAD_PRINCIPAL(c) (Col [c].start == DEAD_PRINCIPAL) +#define KILL_ROW(r) { Row [r].shared2.mark = DEAD ; } +#define KILL_PRINCIPAL_COL(c) { Col [c].start = DEAD_PRINCIPAL ; } +#define KILL_NON_PRINCIPAL_COL(c) { Col [c].start = DEAD_NON_PRINCIPAL ; } + +/* ------------------ */ +/* UMFPACK: Colamd reporting mechanism moved to umf_internal.h */ +/* ------------------ */ + +/* ========================================================================== */ +/* === Prototypes of PRIVATE routines ======================================= */ +/* ========================================================================== */ + +PRIVATE Int init_rows_cols +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int p [] + /* Int stats [COLAMD_STATS] */ +) ; + +PRIVATE void init_scoring +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int head [], + double knobs [COLAMD_KNOBS], + Int *p_n_row2, + Int *p_n_col2, + Int *p_max_deg + /* ------------------ */ + /* added for UMFPACK */ + , Int *p_ndense_row /* number of dense rows */ + , Int *p_nempty_row /* number of original empty rows */ + , Int *p_nnewlyempty_row /* number of newly empty rows */ + , Int *p_ndense_col /* number of dense cols (excl "empty" cols) */ + , Int *p_nempty_col /* number of original empty cols */ + , Int *p_nnewlyempty_col /* number of newly empty cols */ +) ; + +PRIVATE Int find_ordering +( + Int n_row, + Int n_col, + Int Alen, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int head [], + Int n_col2, + Int max_deg, + Int pfree + /* ------------------ */ + /* added for UMFPACK: */ + , Int Front_npivcol [ ] + , Int Front_nrows [ ] + , Int Front_ncols [ ] + , Int Front_parent [ ] + , Int Front_cols [ ] + , Int *p_nfr + , Int aggressive + , Int InFront [ ] + /* ------------------ */ +) ; + +/* ------------------ */ +/* order_children deleted for UMFPACK: */ +/* ------------------ */ + +PRIVATE void detect_super_cols +( + +#ifndef NDEBUG + Int n_col, + Colamd_Row Row [], +#endif /* NDEBUG */ + + Colamd_Col Col [], + Int A [], + Int head [], + Int row_start, + Int row_length +) ; + +PRIVATE Int garbage_collection +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int *pfree +) ; + +PRIVATE Int clear_mark +( + Int n_row, + Colamd_Row Row [] +) ; + +/* ------------------ */ +/* print_report deleted for UMFPACK */ +/* ------------------ */ + +/* ========================================================================== */ +/* === Debugging prototypes and definitions ================================= */ +/* ========================================================================== */ + +#ifndef NDEBUG + +/* ------------------ */ +/* debugging macros moved for UMFPACK */ +/* ------------------ */ + +PRIVATE void debug_deg_lists +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int head [], + Int min_score, + Int should, + Int max_deg +) ; + +PRIVATE void debug_mark +( + Int n_row, + Colamd_Row Row [], + Int tag_mark, + Int max_mark +) ; + +PRIVATE void debug_matrix +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [] +) ; + +PRIVATE void debug_structures +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int n_col2 +) ; + +/* ------------------ */ +/* dump_super added for UMFPACK: */ +PRIVATE void dump_super +( + Int super_c, + Colamd_Col Col [], + Int n_col +) ; +/* ------------------ */ + +#endif /* NDEBUG */ + +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === USER-CALLABLE ROUTINES: ============================================== */ +/* ========================================================================== */ + + +/* ========================================================================== */ +/* === colamd_set_defaults ================================================== */ +/* ========================================================================== */ + +/* + The colamd_set_defaults routine sets the default values of the user- + controllable parameters for colamd: + + knobs [0] rows with knobs[0]*n_col entries or more are removed + prior to ordering in colamd. Rows and columns with + knobs[0]*n_col entries or more are removed prior to + ordering in symamd and placed last in the output + ordering. + + knobs [1] columns with knobs[1]*n_row entries or more are removed + prior to ordering in colamd, and placed last in the + column permutation. Symamd ignores this knob. + + knobs [2] if nonzero, then perform aggressive absorption. + + knobs [3..19] unused, but future versions might use this +*/ + +GLOBAL void UMF_colamd_set_defaults +( + /* === Parameters ======================================================= */ + + double knobs [COLAMD_KNOBS] /* knob array */ +) +{ + /* === Local variables ================================================== */ + + Int i ; + +#if 0 + if (!knobs) + { + return ; /* UMFPACK always passes knobs array */ + } +#endif + for (i = 0 ; i < COLAMD_KNOBS ; i++) + { + knobs [i] = 0 ; + } + knobs [COLAMD_DENSE_ROW] = 0.2 ; /* default changed for UMFPACK */ + knobs [COLAMD_DENSE_COL] = 0.2 ; /* default changed for UMFPACK */ + knobs [COLAMD_AGGRESSIVE] = TRUE ; /* default is to do aggressive + * absorption */ +} + + +/* ========================================================================== */ +/* === symamd removed for UMFPACK =========================================== */ +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === colamd =============================================================== */ +/* ========================================================================== */ + +/* + The colamd routine computes a column ordering Q of a sparse matrix + A such that the LU factorization P(AQ) = LU remains sparse, where P is + selected via partial pivoting. The routine can also be viewed as + providing a permutation Q such that the Cholesky factorization + (AQ)'(AQ) = LL' remains sparse. +*/ + +/* For UMFPACK: colamd always returns TRUE */ + +GLOBAL Int UMF_colamd /* returns TRUE if successful, FALSE otherwise*/ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows in A */ + Int n_col, /* number of columns in A */ + Int Alen, /* length of A */ + Int A [], /* row indices of A */ + Int p [], /* pointers to columns in A */ + double knobs [COLAMD_KNOBS],/* parameters (uses defaults if NULL) */ + Int stats [COLAMD_STATS] /* output statistics and error codes */ + + /* ------------------ */ + /* added for UMFPACK: each Front_ array is of size n_col+1 */ + , Int Front_npivcol [ ] /* # pivot cols in each front */ + , Int Front_nrows [ ] /* # of rows in each front (incl. pivot rows) */ + , Int Front_ncols [ ] /* # of cols in each front (incl. pivot cols) */ + , Int Front_parent [ ] /* parent of each front */ + , Int Front_cols [ ] /* link list of pivot columns for each front */ + , Int *p_nfr /* total number of frontal matrices */ + , Int InFront [ ] /* InFront [row] = f if the original row was + * absorbed into front f. EMPTY if the row was + * empty, dense, or not absorbed. This array + * has size n_row+1 */ + /* ------------------ */ +) +{ + /* === Local variables ================================================== */ + + Int row ; /* row index */ + Int i ; /* loop index */ + Int nnz ; /* nonzeros in A */ + Int Row_size ; /* size of Row [], in integers */ + Int Col_size ; /* size of Col [], in integers */ +#if 0 + Int need ; /* minimum required length of A */ +#endif + Colamd_Row *Row ; /* pointer into A of Row [0..n_row] array */ + Colamd_Col *Col ; /* pointer into A of Col [0..n_col] array */ + Int n_col2 ; /* number of non-dense, non-empty columns */ + Int n_row2 ; /* number of non-dense, non-empty rows */ + Int ngarbage ; /* number of garbage collections performed */ + Int max_deg ; /* maximum row degree */ + Int aggressive ; /* TRUE if doing aggressive absorption */ +#if 0 + double default_knobs [COLAMD_KNOBS] ; /* default knobs array */ +#endif + + /* ------------------ */ + /* debugging initializations moved for UMFPACK */ + /* ------------------ */ + + /* ------------------ */ + /* added for UMFPACK: */ + Int ndense_row, nempty_row, parent, ndense_col, + nempty_col, k, col, nfr, *Front_child, *Front_sibling, *Front_stack, + *Front_order, *Front_size ; + Int nnewlyempty_col, nnewlyempty_row ; + /* ------------------ */ + + /* === Check the input arguments ======================================== */ + +#if 0 + if (!stats) + { + DEBUG0 (("colamd: stats not present\n")) ; + return (FALSE) ; /* UMFPACK: always passes stats [ ] */ + } +#endif + + ASSERT (stats != (Int *) NULL) ; + + for (i = 0 ; i < COLAMD_STATS ; i++) + { + stats [i] = 0 ; + } + stats [COLAMD_STATUS] = COLAMD_OK ; + stats [COLAMD_INFO1] = -1 ; + stats [COLAMD_INFO2] = -1 ; + +#if 0 + if (!A) /* A is not present */ + { + /* UMFPACK: always passes A [ ] */ + DEBUG0 (("colamd: A not present\n")) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; + return (FALSE) ; + } + + if (!p) /* p is not present */ + { + /* UMFPACK: always passes p [ ] */ + DEBUG0 (("colamd: p not present\n")) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; + return (FALSE) ; + } + + if (n_row < 0) /* n_row must be >= 0 */ + { + /* UMFPACK: does not call UMF_colamd if n <= 0 */ + DEBUG0 (("colamd: nrow negative "ID"\n", n_row)) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ; + stats [COLAMD_INFO1] = n_row ; + return (FALSE) ; + } + + if (n_col < 0) /* n_col must be >= 0 */ + { + /* UMFPACK: does not call UMF_colamd if n <= 0 */ + DEBUG0 (("colamd: ncol negative "ID"\n", n_col)) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; + stats [COLAMD_INFO1] = n_col ; + return (FALSE) ; + } +#endif + + ASSERT (A != (Int *) NULL) ; + ASSERT (p != (Int *) NULL) ; + ASSERT (n_row >= 0) ; + ASSERT (n_col >= 0) ; + + nnz = p [n_col] ; + +#if 0 + if (nnz < 0) /* nnz must be >= 0 */ + { + /* UMFPACK: does not call UMF_colamd if nnz < 0 */ + DEBUG0 (("colamd: number of entries negative "ID"\n", nnz)) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; + stats [COLAMD_INFO1] = nnz ; + return (FALSE) ; + } + + if (p [0] != 0) /* p [0] must be exactly zero */ + { + DEBUG0 (("colamd: p[0] not zero "ID"\n", p [0])) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; + stats [COLAMD_INFO1] = p [0] ; + return (FALSE) ; + } +#endif + + ASSERT (nnz >= 0) ; + ASSERT (p [0] == 0) ; + + /* === If no knobs, set default knobs =================================== */ + +#if 0 + if (!knobs) + { + /* UMFPACK: always passes the knobs */ + UMF_colamd_set_defaults (default_knobs) ; + knobs = default_knobs ; + } +#endif + + ASSERT (knobs != (double *) NULL) ; + + /* --------------------- */ + /* added for UMFPACK v4.1: */ + aggressive = (knobs [COLAMD_AGGRESSIVE] != 0) ; + /* --------------------- */ + + /* === Allocate the Row and Col arrays from array A ===================== */ + + Col_size = UMF_COLAMD_C (n_col) ; + Row_size = UMF_COLAMD_R (n_row) ; + +#if 0 + need = MAX (2*nnz, 4*n_col) + n_col + Col_size + Row_size ; + if (need > Alen) + { + /* UMFPACK: always passes enough space */ + /* not enough space in array A to perform the ordering */ + DEBUG0 (("colamd: Need Alen >= "ID", given only Alen = "ID"\n", + need, Alen)) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ; + stats [COLAMD_INFO1] = need ; + stats [COLAMD_INFO2] = Alen ; + return (FALSE) ; + } +#endif + + Alen -= Col_size + Row_size ; + Col = (Colamd_Col *) &A [Alen] ; + Row = (Colamd_Row *) &A [Alen + Col_size] ; + + /* Size of A is now Alen >= MAX (2*nnz, 4*n_col) + n_col. The ordering + * requires Alen >= 2*nnz + n_col, and the postorder requires + * Alen >= 5*n_col. */ + + /* === Construct the row and column data structures ===================== */ + + i = init_rows_cols (n_row, n_col, Row, Col, A, p) ; + +#if 0 + if (!i) + { + /* input matrix is invalid */ + DEBUG0 (("colamd: Matrix invalid\n")) ; + return (FALSE) ; + } +#endif + + ASSERT (i) ; + + /* === UMFPACK: Initialize front info =================================== */ + + for (col = 0 ; col < n_col ; col++) + { + Front_npivcol [col] = 0 ; + Front_nrows [col] = 0 ; + Front_ncols [col] = 0 ; + Front_parent [col] = EMPTY ; + Front_cols [col] = EMPTY ; + } + + /* === Initialize scores, kill dense rows/columns ======================= */ + + init_scoring (n_row, n_col, Row, Col, A, p, knobs, + &n_row2, &n_col2, &max_deg + /* ------------------ */ + /* added for UMFPACK: */ + , &ndense_row, &nempty_row, &nnewlyempty_row + , &ndense_col, &nempty_col, &nnewlyempty_col + /* ------------------ */ + ) ; + ASSERT (n_row2 == n_row - nempty_row - nnewlyempty_row - ndense_row) ; + ASSERT (n_col2 == n_col - nempty_col - nnewlyempty_col - ndense_col) ; + + /* === Order the supercolumns =========================================== */ + + ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p, + n_col2, max_deg, 2*nnz + /* ------------------ */ + /* added for UMFPACK: */ + , Front_npivcol, Front_nrows, Front_ncols, Front_parent, Front_cols + , &nfr, aggressive, InFront + /* ------------------ */ + ) ; + + /* ------------------ */ + /* changed for UMFPACK: */ + + /* A is no longer needed, so use A [0..5*nfr-1] as workspace [ [ */ + /* This step requires Alen >= 5*n_col */ + Front_child = A ; + Front_sibling = Front_child + nfr ; + Front_stack = Front_sibling + nfr ; + Front_order = Front_stack + nfr ; + Front_size = Front_order + nfr ; + + UMF_fsize (nfr, Front_size, Front_nrows, Front_ncols, + Front_parent, Front_npivcol) ; + + AMD_postorder (nfr, Front_parent, Front_npivcol, Front_size, + Front_order, Front_child, Front_sibling, Front_stack) ; + + /* Front_size, Front_stack, Front_child, Front_sibling no longer needed ] */ + + /* use A [0..nfr-1] as workspace */ + UMF_apply_order (Front_npivcol, Front_order, A, nfr, nfr) ; + UMF_apply_order (Front_nrows, Front_order, A, nfr, nfr) ; + UMF_apply_order (Front_ncols, Front_order, A, nfr, nfr) ; + UMF_apply_order (Front_parent, Front_order, A, nfr, nfr) ; + UMF_apply_order (Front_cols, Front_order, A, nfr, nfr) ; + + /* fix the parent to refer to the new numbering */ + for (i = 0 ; i < nfr ; i++) + { + parent = Front_parent [i] ; + if (parent != EMPTY) + { + Front_parent [i] = Front_order [parent] ; + } + } + + /* fix InFront to refer to the new numbering */ + for (row = 0 ; row < n_row ; row++) + { + i = InFront [row] ; + ASSERT (i >= EMPTY && i < nfr) ; + if (i != EMPTY) + { + InFront [row] = Front_order [i] ; + } + } + + /* Front_order longer needed ] */ + + /* === Order the columns in the fronts ================================== */ + + /* use A [0..n_col-1] as inverse permutation */ + for (i = 0 ; i < n_col ; i++) + { + A [i] = EMPTY ; + } + k = 0 ; + for (i = 0 ; i < nfr ; i++) + { + ASSERT (Front_npivcol [i] > 0) ; + for (col = Front_cols [i] ; col != EMPTY ; col = Col [col].nextcol) + { + ASSERT (col >= 0 && col < n_col) ; + DEBUG1 (("Colamd output ordering: k "ID" col "ID"\n", k, col)) ; + p [k] = col ; + ASSERT (A [col] == EMPTY) ; + A [col] = k ; + k++ ; + } + } + + /* === Order the "dense" and null columns =============================== */ + + ASSERT (k == n_col2) ; + if (n_col2 < n_col) + { + for (col = 0 ; col < n_col ; col++) + { + if (A [col] == EMPTY) + { + k = Col [col].shared2.order ; + ASSERT (k >= n_col2 && k < n_col) ; + DEBUG1 (("Colamd output ordering: k "ID" col "ID + " (dense or null col)\n", k, col)) ; + p [k] = col ; + A [col] = k ; + } + } + } + + /* ------------------ */ + + /* === Return statistics in stats ======================================= */ + + /* ------------------ */ + /* modified for UMFPACK */ + stats [COLAMD_DENSE_ROW] = ndense_row ; + stats [COLAMD_EMPTY_ROW] = nempty_row ; + stats [COLAMD_NEWLY_EMPTY_ROW] = nnewlyempty_row ; + stats [COLAMD_DENSE_COL] = ndense_col ; + stats [COLAMD_EMPTY_COL] = nempty_col ; + stats [COLAMD_NEWLY_EMPTY_COL] = nnewlyempty_col ; + ASSERT (ndense_col + nempty_col + nnewlyempty_col == n_col - n_col2) ; + /* ------------------ */ + stats [COLAMD_DEFRAG_COUNT] = ngarbage ; + *p_nfr = nfr ; + DEBUG1 (("colamd: done.\n")) ; + return (TRUE) ; +} + + + + +/* ========================================================================== */ +/* === colamd_report removed for UMFPACK ==================================== */ +/* ========================================================================== */ + +/* ========================================================================== */ +/* === symamd_report removed for UMFPACK ==================================== */ +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === NON-USER-CALLABLE ROUTINES: ========================================== */ +/* ========================================================================== */ + +/* There are no user-callable routines beyond this point in the file */ + + +/* ========================================================================== */ +/* === init_rows_cols ======================================================= */ +/* ========================================================================== */ + +/* + Takes the column form of the matrix in A and creates the row form of the + matrix. Also, row and column attributes are stored in the Col and Row + structs. If the columns are un-sorted or contain duplicate row indices, + this routine will also sort and remove duplicate row indices from the + column form of the matrix. Returns FALSE if the matrix is invalid, + TRUE otherwise. Not user-callable. +*/ + +/* For UMFPACK, this always returns TRUE */ + +PRIVATE Int init_rows_cols /* returns TRUE if OK, or FALSE otherwise */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* row indices of A, of size Alen */ + Int p [] /* pointers to columns in A, of size n_col+1 */ +/* + Int stats [COLAMD_STATS] colamd statistics, removed for UMFPACK +*/ +) +{ + /* === Local variables ================================================== */ + + Int col ; /* a column index */ + Int row ; /* a row index */ + Int *cp ; /* a column pointer */ + Int *cp_end ; /* a pointer to the end of a column */ + + /* === Initialize columns, and check column pointers ==================== */ + + for (col = 0 ; col < n_col ; col++) + { + Col [col].start = p [col] ; + Col [col].length = p [col+1] - p [col] ; + +#if 0 + if (Col [col].length < 0) + { + /* column pointers must be non-decreasing */ + stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = Col [col].length ; + DEBUG0 (("colamd: col "ID" length "ID" <= 0\n", + col, Col [col].length)); + return (FALSE) ; + } +#endif + + ASSERT (Col [col].length >= 0) ; + + /* added for UMFPACK v4.1 */ + ASSERT (Col [col].length > 0) ; + + Col [col].shared1.thickness = 1 ; + Col [col].shared2.score = 0 ; + Col [col].shared3.prev = EMPTY ; + Col [col].shared4.degree_next = EMPTY ; + + /* ------------------ */ + /* added for UMFPACK: */ + Col [col].nextcol = EMPTY ; + Col [col].lastcol = col ; + /* ------------------ */ + } + + /* p [0..n_col] no longer needed, used as "head" in subsequent routines */ + + /* === Scan columns, compute row degrees, and check row indices ========= */ + + /* ------------------ */ + /* stats [COLAMD_INFO3] = 0 ; */ + /* number of duplicate or unsorted row indices - not computed in UMFPACK */ + /* ------------------ */ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].length = 0 ; + /* ------------------ */ + /* removed for UMFPACK */ + /* Row [row].shared2.mark = -1 ; */ + /* ------------------ */ + /* ------------------ */ + /* added for UMFPACK: */ + Row [row].thickness = 1 ; + Row [row].front = EMPTY ; + /* ------------------ */ + } + + for (col = 0 ; col < n_col ; col++) + { +#ifndef NDEBUG + Int last_row = -1 ; +#endif + + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + + while (cp < cp_end) + { + row = *cp++ ; + +#if 0 + /* make sure row indices within range */ + if (row < 0 || row >= n_row) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + /* ------------------ */ + /* not needed in UMFPACK: */ + /* stats [COLAMD_INFO3] = n_row ; */ + /* ------------------ */ + DEBUG0 (("colamd: row "ID" col "ID" out of bounds\n", row,col)); + return (FALSE) ; + } +#endif + + ASSERT (row >= 0 && row < n_row) ; + +#if 0 + /* ------------------ */ + /* changed for UMFPACK */ + if (row <= last_row) + { + /* row index are unsorted or repeated (or both), thus col */ + /* is jumbled. This is an error condition for UMFPACK */ + stats [COLAMD_STATUS] = COLAMD_ERROR_jumbled_matrix ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + DEBUG1 (("colamd: row "ID" col "ID" unsorted/duplicate\n", + row, col)) ; + return (FALSE) ; + } + /* ------------------ */ +#endif + + ASSERT (row > last_row) ; + + /* ------------------ */ + /* changed for UMFPACK - jumbled columns not tolerated */ + Row [row].length++ ; + /* ------------------ */ + +#ifndef NDEBUG + last_row = row ; +#endif + } + } + + /* === Compute row pointers ============================================= */ + + /* row form of the matrix starts directly after the column */ + /* form of matrix in A */ + Row [0].start = p [n_col] ; + Row [0].shared1.p = Row [0].start ; + /* ------------------ */ + /* removed for UMFPACK */ + /* Row [0].shared2.mark = -1 ; */ + /* ------------------ */ + for (row = 1 ; row < n_row ; row++) + { + Row [row].start = Row [row-1].start + Row [row-1].length ; + Row [row].shared1.p = Row [row].start ; + /* ------------------ */ + /* removed for UMFPACK */ + /* Row [row].shared2.mark = -1 ; */ + /* ------------------ */ + } + + /* === Create row form ================================================== */ + + /* ------------------ */ + /* jumbled matrix case removed for UMFPACK */ + /* ------------------ */ + + for (col = 0 ; col < n_col ; col++) + { + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + while (cp < cp_end) + { + A [(Row [*cp++].shared1.p)++] = col ; + } + } + + /* === Clear the row marks and set row degrees ========================== */ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].shared2.mark = 0 ; + Row [row].shared1.degree = Row [row].length ; + } + + /* ------------------ */ + /* recreate columns for jumbled matrix case removed for UMFPACK */ + /* ------------------ */ + + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === init_scoring ========================================================= */ +/* ========================================================================== */ + +/* + Kills dense or empty columns and rows, calculates an initial score for + each column, and places all columns in the degree lists. Not user-callable. +*/ + +PRIVATE void init_scoring +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* column form and row form of A */ + Int head [], /* of size n_col+1 */ + double knobs [COLAMD_KNOBS],/* parameters */ + Int *p_n_row2, /* number of non-dense, non-empty rows */ + Int *p_n_col2, /* number of non-dense, non-empty columns */ + Int *p_max_deg /* maximum row degree */ + /* ------------------ */ + /* added for UMFPACK */ + , Int *p_ndense_row /* number of dense rows */ + , Int *p_nempty_row /* number of original empty rows */ + , Int *p_nnewlyempty_row /* number of newly empty rows */ + , Int *p_ndense_col /* number of dense cols (excl "empty" cols) */ + , Int *p_nempty_col /* number of original empty cols */ + , Int *p_nnewlyempty_col /* number of newly empty cols */ + /* ------------------ */ +) +{ + /* === Local variables ================================================== */ + + Int c ; /* a column index */ + Int r, row ; /* a row index */ + Int *cp ; /* a column pointer */ + Int deg ; /* degree of a row or column */ + Int *cp_end ; /* a pointer to the end of a column */ + Int *new_cp ; /* new column pointer */ + Int col_length ; /* length of pruned column */ + Int score ; /* current column score */ + Int n_col2 ; /* number of non-dense, non-empty columns */ + Int n_row2 ; /* number of non-dense, non-empty rows */ + Int dense_row_count ; /* remove rows with more entries than this */ + Int dense_col_count ; /* remove cols with more entries than this */ + Int min_score ; /* smallest column score */ + Int max_deg ; /* maximum row degree */ + Int next_col ; /* Used to add to degree list.*/ + + /* ------------------ */ + /* added for UMFPACK */ + Int ndense_row ; /* number of dense rows */ + Int nempty_row ; /* number of empty rows */ + Int nnewlyempty_row ; /* number of newly empty rows */ + Int ndense_col ; /* number of dense cols (excl "empty" cols) */ + Int nempty_col ; /* number of original empty cols */ + Int nnewlyempty_col ; /* number of newly empty cols */ + Int ne ; + /* ------------------ */ + +#ifndef NDEBUG + Int debug_count ; /* debug only. */ +#endif /* NDEBUG */ + + /* === Extract knobs ==================================================== */ + + /* --------------------- */ + /* old dense row/column knobs: + dense_row_count = MAX (0, MIN (knobs [COLAMD_DENSE_ROW] * n_col, n_col)) ; + dense_col_count = MAX (0, MIN (knobs [COLAMD_DENSE_COL] * n_row, n_row)) ; + */ + /* new, for UMFPACK: */ + /* Note: if knobs contains a NaN, this is undefined: */ + dense_row_count = + UMFPACK_DENSE_DEGREE_THRESHOLD (knobs [COLAMD_DENSE_ROW], n_col) ; + dense_col_count = + UMFPACK_DENSE_DEGREE_THRESHOLD (knobs [COLAMD_DENSE_COL], n_row) ; + /* Make sure dense_*_count is between 0 and n: */ + dense_row_count = MAX (0, MIN (dense_row_count, n_col)) ; + dense_col_count = MAX (0, MIN (dense_col_count, n_row)) ; + /* --------------------- */ + + DEBUG1 (("colamd: densecount: "ID" "ID"\n", + dense_row_count, dense_col_count)) ; + max_deg = 0 ; + n_col2 = n_col ; + n_row2 = n_row ; + + /* --------------------- */ + /* added for UMFPACK */ + ndense_col = 0 ; + nempty_col = 0 ; + nnewlyempty_col = 0 ; + ndense_row = 0 ; + nempty_row = 0 ; + nnewlyempty_row = 0 ; + /* --------------------- */ + + /* === Kill empty columns =============================================== */ + + /* removed for UMFPACK v4.1. prune_singletons has already removed empty + * columns and empty rows */ + +#if 0 + /* Put the empty columns at the end in their natural order, so that LU */ + /* factorization can proceed as far as possible. */ + for (c = n_col-1 ; c >= 0 ; c--) + { + deg = Col [c].length ; + if (deg == 0) + { + /* this is a empty column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + /* --------------------- */ + /* added for UMFPACK */ + nempty_col++ ; + /* --------------------- */ + } + } + DEBUG1 (("colamd: null columns killed: "ID"\n", n_col - n_col2)) ; +#endif + +#ifndef NDEBUG + for (c = 0 ; c < n_col ; c++) + { + ASSERT (Col [c].length > 0) ; + } +#endif + + /* === Count null rows ================================================== */ + +#if 0 + for (r = 0 ; r < n_row ; r++) + { + deg = Row [r].shared1.degree ; + if (deg == 0) + { + /* this is an original empty row */ + nempty_row++ ; + } + } +#endif + +#ifndef NDEBUG + for (r = 0 ; r < n_row ; r++) + { + ASSERT (Row [r].shared1.degree > 0) ; + ASSERT (Row [r].length > 0) ; + } +#endif + + /* === Kill dense columns =============================================== */ + + /* Put the dense columns at the end, in their natural order */ + for (c = n_col-1 ; c >= 0 ; c--) + { + + /* ----------------------------------------------------------------- */ +#if 0 + /* removed for UMFPACK v4.1: no empty columns */ + /* skip any dead columns */ + if (COL_IS_DEAD (c)) + { + continue ; + } +#endif + ASSERT (COL_IS_ALIVE (c)) ; + ASSERT (Col [c].length > 0) ; + /* ----------------------------------------------------------------- */ + + deg = Col [c].length ; + if (deg > dense_col_count) + { + /* this is a dense column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + /* --------------------- */ + /* added for UMFPACK */ + ndense_col++ ; + /* --------------------- */ + /* decrement the row degrees */ + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + Row [*cp++].shared1.degree-- ; + } + KILL_PRINCIPAL_COL (c) ; + } + } + DEBUG1 (("colamd: Dense and null columns killed: "ID"\n", n_col - n_col2)) ; + + /* === Kill dense and empty rows ======================================== */ + + /* Note that there can now be empty rows, since dense columns have + * been deleted. These are "newly" empty rows. */ + + ne = 0 ; + for (r = 0 ; r < n_row ; r++) + { + deg = Row [r].shared1.degree ; + ASSERT (deg >= 0 && deg <= n_col) ; + /* --------------------- */ + /* added for UMFPACK */ + if (deg > dense_row_count) + { + /* There is at least one dense row. Continue ordering, but */ + /* symbolic factorization will be redone after UMF_colamd is done.*/ + ndense_row++ ; + } + if (deg == 0) + { + /* this is a newly empty row, or original empty row */ + ne++ ; + } + /* --------------------- */ + if (deg > dense_row_count || deg == 0) + { + /* kill a dense or empty row */ + KILL_ROW (r) ; + /* --------------------- */ + /* added for UMFPACK */ + Row [r].thickness = 0 ; + /* --------------------- */ + --n_row2 ; + } + else + { + /* keep track of max degree of remaining rows */ + max_deg = MAX (max_deg, deg) ; + } + } + nnewlyempty_row = ne - nempty_row ; + DEBUG1 (("colamd: Dense rows killed: "ID"\n", ndense_row)) ; + DEBUG1 (("colamd: Dense and null rows killed: "ID"\n", n_row - n_row2)) ; + + /* === Compute initial column scores ==================================== */ + + /* At this point the row degrees are accurate. They reflect the number */ + /* of "live" (non-dense) columns in each row. No empty rows exist. */ + /* Some "live" columns may contain only dead rows, however. These are */ + /* pruned in the code below. */ + + /* now find the initial matlab score for each column */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* skip dead column */ + if (COL_IS_DEAD (c)) + { + continue ; + } + score = 0 ; + cp = &A [Col [c].start] ; + new_cp = cp ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + /* skip if dead */ + if (ROW_IS_DEAD (row)) + { + continue ; + } + /* compact the column */ + *new_cp++ = row ; + /* add row's external degree */ + score += Row [row].shared1.degree - 1 ; + /* guard against integer overflow */ + score = MIN (score, n_col) ; + } + /* determine pruned column length */ + col_length = (Int) (new_cp - &A [Col [c].start]) ; + if (col_length == 0) + { + /* a newly-made null column (all rows in this col are "dense" */ + /* and have already been killed) */ + DEBUG2 (("Newly null killed: "ID"\n", c)) ; + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + /* --------------------- */ + /* added for UMFPACK */ + nnewlyempty_col++ ; + /* --------------------- */ + } + else + { + /* set column length and set score */ + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + Col [c].length = col_length ; + Col [c].shared2.score = score ; + } + } + DEBUG1 (("colamd: Dense, null, and newly-null columns killed: "ID"\n", + n_col-n_col2)) ; + + /* At this point, all empty rows and columns are dead. All live columns */ + /* are "clean" (containing no dead rows) and simplicial (no supercolumns */ + /* yet). Rows may contain dead columns, but all live rows contain at */ + /* least one live column. */ + +#ifndef NDEBUG + debug_structures (n_row, n_col, Row, Col, A, n_col2) ; +#endif /* NDEBUG */ + + /* === Initialize degree lists ========================================== */ + +#ifndef NDEBUG + debug_count = 0 ; +#endif /* NDEBUG */ + + /* clear the hash buckets */ + for (c = 0 ; c <= n_col ; c++) + { + head [c] = EMPTY ; + } + min_score = n_col ; + /* place in reverse order, so low column indices are at the front */ + /* of the lists. This is to encourage natural tie-breaking */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* only add principal columns to degree lists */ + if (COL_IS_ALIVE (c)) + { + DEBUG4 (("place "ID" score "ID" minscore "ID" ncol "ID"\n", + c, Col [c].shared2.score, min_score, n_col)) ; + + /* === Add columns score to DList =============================== */ + + score = Col [c].shared2.score ; + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + ASSERT (head [score] >= EMPTY) ; + + /* now add this column to dList at proper score location */ + next_col = head [score] ; + Col [c].shared3.prev = EMPTY ; + Col [c].shared4.degree_next = next_col ; + + /* if there already was a column with the same score, set its */ + /* previous pointer to this new column */ + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = c ; + } + head [score] = c ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, score) ; + +#ifndef NDEBUG + debug_count++ ; +#endif /* NDEBUG */ + + } + } + +#ifndef NDEBUG + DEBUG1 (("colamd: Live cols "ID" out of "ID", non-princ: "ID"\n", + debug_count, n_col, n_col-debug_count)) ; + ASSERT (debug_count == n_col2) ; + debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2, max_deg) ; +#endif /* NDEBUG */ + + /* === Return number of remaining columns, and max row degree =========== */ + + *p_n_col2 = n_col2 ; + *p_n_row2 = n_row2 ; + *p_max_deg = max_deg ; + + /* --------------------- */ + /* added for UMFPACK */ + *p_ndense_row = ndense_row ; + *p_nempty_row = nempty_row ; /* original empty rows */ + *p_nnewlyempty_row = nnewlyempty_row ; + *p_ndense_col = ndense_col ; + *p_nempty_col = nempty_col ; /* original empty cols */ + *p_nnewlyempty_col = nnewlyempty_col ; + /* --------------------- */ +} + + +/* ========================================================================== */ +/* === find_ordering ======================================================== */ +/* ========================================================================== */ + +/* + Order the principal columns of the supercolumn form of the matrix + (no supercolumns on input). Uses a minimum approximate column minimum + degree ordering method. Not user-callable. +*/ + +PRIVATE Int find_ordering /* return the number of garbage collections */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Int Alen, /* size of A, 2*nnz + n_col or larger */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* column form and row form of A */ + Int head [], /* of size n_col+1 */ + Int n_col2, /* Remaining columns to order */ + Int max_deg, /* Maximum row degree */ + Int pfree /* index of first free slot (2*nnz on entry) */ + /* ------------------ */ + /* added for UMFPACK: */ + , Int Front_npivcol [ ] + , Int Front_nrows [ ] + , Int Front_ncols [ ] + , Int Front_parent [ ] + , Int Front_cols [ ] + , Int *p_nfr /* number of fronts */ + , Int aggressive + , Int InFront [ ] + /* ------------------ */ +) +{ + /* === Local variables ================================================== */ + + Int k ; /* current pivot ordering step */ + Int pivot_col ; /* current pivot column */ + Int *cp ; /* a column pointer */ + Int *rp ; /* a row pointer */ + Int pivot_row ; /* current pivot row */ + Int *new_cp ; /* modified column pointer */ + Int *new_rp ; /* modified row pointer */ + Int pivot_row_start ; /* pointer to start of pivot row */ + Int pivot_row_degree ; /* number of columns in pivot row */ + Int pivot_row_length ; /* number of supercolumns in pivot row */ + Int pivot_col_score ; /* score of pivot column */ + Int needed_memory ; /* free space needed for pivot row */ + Int *cp_end ; /* pointer to the end of a column */ + Int *rp_end ; /* pointer to the end of a row */ + Int row ; /* a row index */ + Int col ; /* a column index */ + Int max_score ; /* maximum possible score */ + Int cur_score ; /* score of current column */ + unsigned Int hash ; /* hash value for supernode detection */ + Int head_column ; /* head of hash bucket */ + Int first_col ; /* first column in hash bucket */ + Int tag_mark ; /* marker value for mark array */ + Int row_mark ; /* Row [row].shared2.mark */ + Int set_difference ; /* set difference size of row with pivot row */ + Int min_score ; /* smallest column score */ + Int col_thickness ; /* "thickness" (no. of columns in a supercol) */ + Int max_mark ; /* maximum value of tag_mark */ + Int pivot_col_thickness ; /* number of columns represented by pivot col */ + Int prev_col ; /* Used by Dlist operations. */ + Int next_col ; /* Used by Dlist operations. */ + Int ngarbage ; /* number of garbage collections performed */ + +#ifndef NDEBUG + Int debug_d ; /* debug loop counter */ + Int debug_step = 0 ; /* debug loop counter */ +#endif /* NDEBUG */ + + /* ------------------ */ + /* added for UMFPACK: */ + Int pivot_row_thickness ; /* number of rows represented by pivot row */ + Int nfr = 0 ; /* number of fronts */ + Int child ; + /* ------------------ */ + + /* === Initialization and clear mark ==================================== */ + + max_mark = MAX_MARK (n_col) ; /* defined in umfpack.h */ + tag_mark = clear_mark (n_row, Row) ; + min_score = 0 ; + ngarbage = 0 ; + DEBUG1 (("colamd: Ordering, n_col2="ID"\n", n_col2)) ; + + for (row = 0 ; row < n_row ; row++) + { + InFront [row] = EMPTY ; + } + + /* === Order the columns ================================================ */ + + for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */) + { + +#ifndef NDEBUG + if (debug_step % 100 == 0) + { + DEBUG2 (("\n... Step k: "ID" out of n_col2: "ID"\n", k, n_col2)) ; + } + else + { + DEBUG3 (("\n-----Step k: "ID" out of n_col2: "ID"\n", k, n_col2)) ; + } + debug_step++ ; + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + + /* === Select pivot column, and order it ============================ */ + + /* make sure degree list isn't empty */ + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (head [min_score] >= EMPTY) ; + +#ifndef NDEBUG + for (debug_d = 0 ; debug_d < min_score ; debug_d++) + { + ASSERT (head [debug_d] == EMPTY) ; + } +#endif /* NDEBUG */ + + /* get pivot column from head of minimum degree list */ + while (head [min_score] == EMPTY && min_score < n_col) + { + min_score++ ; + } + pivot_col = head [min_score] ; + ASSERT (pivot_col >= 0 && pivot_col <= n_col) ; + next_col = Col [pivot_col].shared4.degree_next ; + head [min_score] = next_col ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = EMPTY ; + } + + ASSERT (COL_IS_ALIVE (pivot_col)) ; + DEBUG3 (("Pivot col: "ID"\n", pivot_col)) ; + + /* remember score for defrag check */ + pivot_col_score = Col [pivot_col].shared2.score ; + + /* the pivot column is the kth column in the pivot order */ + Col [pivot_col].shared2.order = k ; + + /* increment order count by column thickness */ + pivot_col_thickness = Col [pivot_col].shared1.thickness ; + /* ------------------ */ + /* changed for UMFPACK: */ + k += pivot_col_thickness ; + /* ------------------ */ + ASSERT (pivot_col_thickness > 0) ; + + /* === Garbage_collection, if necessary ============================= */ + + needed_memory = MIN (pivot_col_score, n_col - k) ; + if (pfree + needed_memory >= Alen) + { + pfree = garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ; + ngarbage++ ; + /* after garbage collection we will have enough */ + ASSERT (pfree + needed_memory < Alen) ; + /* garbage collection has wiped out the Row[].shared2.mark array */ + tag_mark = clear_mark (n_row, Row) ; + +#ifndef NDEBUG + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + } + + /* === Compute pivot row pattern ==================================== */ + + /* get starting location for this new merged row */ + pivot_row_start = pfree ; + + /* initialize new row counts to zero */ + pivot_row_degree = 0 ; + + /* ------------------ */ + /* added for UMFPACK: */ + pivot_row_thickness = 0 ; + /* ------------------ */ + + /* [ tag pivot column as having been visited so it isn't included */ + /* in merged pivot row */ + Col [pivot_col].shared1.thickness = -pivot_col_thickness ; + + /* pivot row is the union of all rows in the pivot column pattern */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + DEBUG4 (("Pivot col pattern %d "ID"\n", ROW_IS_ALIVE(row), row)) ; + /* skip if row is dead */ + if (ROW_IS_DEAD (row)) + { + continue ; + } + + /* ------------------ */ + /* added for UMFPACK: */ + /* sum the thicknesses of all the rows */ + /* ASSERT (Row [row].thickness > 0) ; */ + pivot_row_thickness += Row [row].thickness ; + /* ------------------ */ + + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + /* add the column, if alive and untagged */ + col_thickness = Col [col].shared1.thickness ; + if (col_thickness > 0 && COL_IS_ALIVE (col)) + { + /* tag column in pivot row */ + Col [col].shared1.thickness = -col_thickness ; + ASSERT (pfree < Alen) ; + /* place column in pivot row */ + A [pfree++] = col ; + pivot_row_degree += col_thickness ; + /* ------------------ */ + /* added for UMFPACK: */ + DEBUG4 (("\t\t\tNew live column in pivot row: "ID"\n",col)); + /* ------------------ */ + } + /* ------------------ */ + /* added for UMFPACK */ +#ifndef NDEBUG + if (col_thickness < 0 && COL_IS_ALIVE (col)) + { + DEBUG4 (("\t\t\tOld live column in pivot row: "ID"\n",col)); + } +#endif + /* ------------------ */ + } + } + + /* ------------------ */ + /* added for UMFPACK: */ + /* pivot_row_thickness is the number of rows in frontal matrix */ + /* both pivotal rows and nonpivotal rows */ + /* ------------------ */ + + /* clear tag on pivot column */ + Col [pivot_col].shared1.thickness = pivot_col_thickness ; /* ] */ + max_deg = MAX (max_deg, pivot_row_degree) ; + +#ifndef NDEBUG + DEBUG3 (("check2\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Kill all rows used to construct pivot row ==================== */ + + /* also kill pivot row, temporarily */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* may be killing an already dead row */ + row = *cp++ ; + + DEBUG2 (("Kill row in pivot col: "ID" alive? %d, front "ID"\n", + row, ROW_IS_ALIVE (row), Row [row].front)) ; + + /* added for UMFPACK: */ + if (ROW_IS_ALIVE (row)) + { + if (Row [row].front != EMPTY) + { + /* This row represents a frontal matrix. */ + /* Row [row].front is a child of current front */ + child = Row [row].front ; + Front_parent [child] = nfr ; + DEBUG1 (("Front "ID" => front "ID", normal\n", child, nfr)); + } + else + { + /* This is an original row. Keep track of which front + * is its parent in the row-merge tree. */ + InFront [row] = nfr ; + DEBUG1 (("Row "ID" => front "ID", normal\n", row, nfr)) ; + } + } + + KILL_ROW (row) ; + + /* ------------------ */ + /* added for UMFPACK: */ + Row [row].thickness = 0 ; + /* ------------------ */ + } + + /* === Select a row index to use as the new pivot row =============== */ + + pivot_row_length = pfree - pivot_row_start ; + if (pivot_row_length > 0) + { + /* pick the "pivot" row arbitrarily (first row in col) */ + pivot_row = A [Col [pivot_col].start] ; + DEBUG3 (("Pivotal row is "ID"\n", pivot_row)) ; + } + else + { + /* there is no pivot row, since it is of zero length */ + pivot_row = EMPTY ; + ASSERT (pivot_row_length == 0) ; + } + ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ; + + /* === Approximate degree computation =============================== */ + + /* Here begins the computation of the approximate degree. The column */ + /* score is the sum of the pivot row "length", plus the size of the */ + /* set differences of each row in the column minus the pattern of the */ + /* pivot row itself. The column ("thickness") itself is also */ + /* excluded from the column score (we thus use an approximate */ + /* external degree). */ + + /* The time taken by the following code (compute set differences, and */ + /* add them up) is proportional to the size of the data structure */ + /* being scanned - that is, the sum of the sizes of each column in */ + /* the pivot row. Thus, the amortized time to compute a column score */ + /* is proportional to the size of that column (where size, in this */ + /* context, is the column "length", or the number of row indices */ + /* in that column). The number of row indices in a column is */ + /* monotonically non-decreasing, from the length of the original */ + /* column on input to colamd. */ + + /* === Compute set differences ====================================== */ + + DEBUG3 (("** Computing set differences phase. **\n")) ; + + /* pivot row is currently dead - it will be revived later. */ + + DEBUG3 (("Pivot row: \n")) ; + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + DEBUG3 ((" Col: "ID"\n", col)) ; + + /* clear tags used to construct pivot row pattern */ + col_thickness = -Col [col].shared1.thickness ; + ASSERT (col_thickness > 0) ; + Col [col].shared1.thickness = col_thickness ; + + /* === Remove column from degree list =========================== */ + + cur_score = Col [col].shared2.score ; + prev_col = Col [col].shared3.prev ; + next_col = Col [col].shared4.degree_next ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (cur_score >= EMPTY) ; + if (prev_col == EMPTY) + { + head [cur_score] = next_col ; + } + else + { + Col [prev_col].shared4.degree_next = next_col ; + } + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = prev_col ; + } + + /* === Scan the column ========================================== */ + + cp = &A [Col [col].start] ; + cp_end = cp + Col [col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + continue ; + } + ASSERT (row != pivot_row) ; + set_difference = row_mark - tag_mark ; + /* check if the row has been seen yet */ + if (set_difference < 0) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + set_difference = Row [row].shared1.degree ; + } + /* subtract column thickness from this row's set difference */ + set_difference -= col_thickness ; + ASSERT (set_difference >= 0) ; + ASSERT (ROW_IS_ALIVE (row)) ; + + /* absorb this row if the set difference becomes zero */ + if (set_difference == 0 && aggressive) + { + /* v4.1: do aggressive absorption */ + DEBUG3 (("aggressive absorption. Row: "ID"\n", row)) ; + + if (Row [row].front != EMPTY) + { + /* Row [row].front is a child of current front. */ + child = Row [row].front ; + Front_parent [child] = nfr ; + DEBUG1 (("Front "ID" => front "ID", aggressive\n", + child, nfr)) ; + } + else + { + /* this is an original row. Keep track of which front + * assembles it, for the row-merge tree */ + InFront [row] = nfr ; + DEBUG1 (("Row "ID" => front "ID", aggressive\n", + row, nfr)) ; + } + + KILL_ROW (row) ; + + /* sum the thicknesses of all the rows */ + /* ASSERT (Row [row].thickness > 0) ; */ + pivot_row_thickness += Row [row].thickness ; + Row [row].thickness = 0 ; + + } + else + { + /* save the new mark */ + Row [row].shared2.mark = set_difference + tag_mark ; + } + } + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k-pivot_row_degree, max_deg) ; +#endif /* NDEBUG */ + + /* === Add up set differences for each column ======================= */ + + DEBUG3 (("** Adding set differences phase. **\n")) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + hash = 0 ; + cur_score = 0 ; + cp = &A [Col [col].start] ; + /* compact the column */ + new_cp = cp ; + cp_end = cp + Col [col].length ; + + DEBUG4 (("Adding set diffs for Col: "ID".\n", col)) ; + + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + ASSERT(row >= 0 && row < n_row) ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + /* ------------------ */ + /* changed for UMFPACK: */ + DEBUG4 ((" Row "ID", dead\n", row)) ; + /* ------------------ */ + continue ; + } + /* ------------------ */ + /* changed for UMFPACK: */ + /* ASSERT (row_mark > tag_mark) ; */ + DEBUG4 ((" Row "ID", set diff "ID"\n", row, row_mark-tag_mark)); + ASSERT (row_mark >= tag_mark) ; + /* ------------------ */ + /* compact the column */ + *new_cp++ = row ; + /* compute hash function */ + hash += row ; + /* add set difference */ + cur_score += row_mark - tag_mark ; + /* integer overflow... */ + cur_score = MIN (cur_score, n_col) ; + } + + /* recompute the column's length */ + Col [col].length = (Int) (new_cp - &A [Col [col].start]) ; + + /* === Further mass elimination ================================= */ + + if (Col [col].length == 0) + { + DEBUG4 (("further mass elimination. Col: "ID"\n", col)) ; + /* nothing left but the pivot row in this column */ + KILL_PRINCIPAL_COL (col) ; + pivot_row_degree -= Col [col].shared1.thickness ; + ASSERT (pivot_row_degree >= 0) ; + /* order it */ + Col [col].shared2.order = k ; + /* increment order count by column thickness */ + k += Col [col].shared1.thickness ; + + /* ------------------ */ + /* added for UMFPACK: */ + pivot_col_thickness += Col [col].shared1.thickness ; + + /* add to column list of front ... */ +#ifndef NDEBUG + DEBUG1 (("Mass")) ; + dump_super (col, Col, n_col) ; +#endif + Col [Col [col].lastcol].nextcol = Front_cols [nfr] ; + Front_cols [nfr] = col ; + /* ------------------ */ + + } + else + { + /* === Prepare for supercolumn detection ==================== */ + + DEBUG4 (("Preparing supercol detection for Col: "ID".\n", col)); + + /* save score so far */ + Col [col].shared2.score = cur_score ; + + /* add column to hash table, for supercolumn detection */ + /* NOTE: hash is an unsigned Int to avoid a problem in ANSI C. + * The sign of the expression a % b is not defined when a and/or + * b are negative. Since hash is unsigned and n_col >= 0, + * this problem is avoided. */ + hash %= n_col + 1 ; + + DEBUG4 ((" Hash = "ID", n_col = "ID".\n", (Int) hash, n_col)) ; + ASSERT (((Int) hash) <= n_col) ; + + head_column = head [hash] ; + if (head_column > EMPTY) + { + /* degree list "hash" is non-empty, use prev (shared3) of */ + /* first column in degree list as head of hash bucket */ + first_col = Col [head_column].shared3.headhash ; + Col [head_column].shared3.headhash = col ; + } + else + { + /* degree list "hash" is empty, use head as hash bucket */ + first_col = - (head_column + 2) ; + head [hash] = - (col + 2) ; + } + Col [col].shared4.hash_next = first_col ; + + /* save hash function in Col [col].shared3.hash */ + Col [col].shared3.hash = (Int) hash ; + ASSERT (COL_IS_ALIVE (col)) ; + } + } + + /* The approximate external column degree is now computed. */ + + /* === Supercolumn detection ======================================== */ + + DEBUG3 (("** Supercolumn detection phase. **\n")) ; + + detect_super_cols ( + +#ifndef NDEBUG + n_col, Row, +#endif /* NDEBUG */ + + Col, A, head, pivot_row_start, pivot_row_length) ; + + /* === Kill the pivotal column ====================================== */ + + KILL_PRINCIPAL_COL (pivot_col) ; + + /* ------------------ */ + /* added for UMFPACK: */ + /* add columns to column list of front */ +#ifndef NDEBUG + DEBUG1 (("Pivot")) ; + dump_super (pivot_col, Col, n_col) ; +#endif + Col [Col [pivot_col].lastcol].nextcol = Front_cols [nfr] ; + Front_cols [nfr] = pivot_col ; + /* ------------------ */ + + /* === Clear mark =================================================== */ + + tag_mark += (max_deg + 1) ; + if (tag_mark >= max_mark) + { + DEBUG2 (("clearing tag_mark\n")) ; + tag_mark = clear_mark (n_row, Row) ; + } + +#ifndef NDEBUG + DEBUG3 (("check3\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Finalize the new pivot row, and column scores ================ */ + + DEBUG3 (("** Finalize scores phase. **\n")) ; + DEBUG3 (("pivot_row_degree "ID"\n", pivot_row_degree)) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + /* compact the pivot row */ + new_rp = rp ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + DEBUG3 (("Col "ID" \n", col)) ; + /* skip dead columns */ + if (COL_IS_DEAD (col)) + { + DEBUG3 (("dead\n")) ; + continue ; + } + *new_rp++ = col ; + /* add new pivot row to column */ + A [Col [col].start + (Col [col].length++)] = pivot_row ; + + /* retrieve score so far and add on pivot row's degree. */ + /* (we wait until here for this in case the pivot */ + /* row's degree was reduced due to mass elimination). */ + cur_score = Col [col].shared2.score + pivot_row_degree ; + DEBUG3 ((" cur_score "ID" ", cur_score)) ; + + /* calculate the max possible score as the number of */ + /* external columns minus the 'k' value minus the */ + /* columns thickness */ + max_score = n_col - k - Col [col].shared1.thickness ; + DEBUG3 ((" max_score "ID" ", max_score)) ; + + /* make the score the external degree of the union-of-rows */ + cur_score -= Col [col].shared1.thickness ; + DEBUG3 ((" cur_score "ID" ", cur_score)) ; + + /* make sure score is less or equal than the max score */ + cur_score = MIN (cur_score, max_score) ; + ASSERT (cur_score >= 0) ; + + /* store updated score */ + Col [col].shared2.score = cur_score ; + DEBUG3 ((" "ID"\n", cur_score)) ; + + /* === Place column back in degree list ========================= */ + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (head [cur_score] >= EMPTY) ; + next_col = head [cur_score] ; + Col [col].shared4.degree_next = next_col ; + Col [col].shared3.prev = EMPTY ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = col ; + } + head [cur_score] = col ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, cur_score) ; + + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; +#endif /* NDEBUG */ + + /* ------------------ */ + /* added for UMFPACK: */ + /* frontal matrix can have more pivot cols than pivot rows for */ + /* singular matrices. */ + + /* number of candidate pivot columns */ + Front_npivcol [nfr] = pivot_col_thickness ; + + /* all rows (not just size of contrib. block) */ + Front_nrows [nfr] = pivot_row_thickness ; + + /* all cols */ + Front_ncols [nfr] = pivot_col_thickness + pivot_row_degree ; + + Front_parent [nfr] = EMPTY ; + + pivot_row_thickness -= pivot_col_thickness ; + DEBUG1 (("Front "ID" Pivot_row_thickness after pivot cols elim: "ID"\n", + nfr, pivot_row_thickness)) ; + pivot_row_thickness = MAX (0, pivot_row_thickness) ; + /* ------------------ */ + + /* === Resurrect the new pivot row ================================== */ + + if (pivot_row_degree > 0 + /* ------------------ */ + /* added for UMFPACK. Note that this part of the expression should be + * removed if this routine is used outside of UMFPACK, for a Cholesky + * factorization of (AQ)'(AQ) */ + && pivot_row_thickness > 0 + /* ------------------ */ + ) + { + /* update pivot row length to reflect any cols that were killed */ + /* during super-col detection and mass elimination */ + Row [pivot_row].start = pivot_row_start ; + Row [pivot_row].length = (Int) (new_rp - &A[pivot_row_start]) ; + ASSERT (Row [pivot_row].length > 0) ; + Row [pivot_row].shared1.degree = pivot_row_degree ; + Row [pivot_row].shared2.mark = 0 ; + /* ------------------ */ + /* added for UMFPACK: */ + Row [pivot_row].thickness = pivot_row_thickness ; + Row [pivot_row].front = nfr ; + /* ------------------ */ + /* pivot row is no longer dead */ + } + + /* ------------------ */ + /* added for UMFPACK: */ + +#ifndef NDEBUG + DEBUG1 (("Front "ID" : "ID" "ID" "ID" ", nfr, + Front_npivcol [nfr], Front_nrows [nfr], Front_ncols [nfr])) ; + DEBUG1 ((" cols:[ ")) ; + debug_d = 0 ; + for (col = Front_cols [nfr] ; col != EMPTY ; col = Col [col].nextcol) + { + DEBUG1 ((" "ID, col)) ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (COL_IS_DEAD (col)) ; + debug_d++ ; + ASSERT (debug_d <= pivot_col_thickness) ; + } + ASSERT (debug_d == pivot_col_thickness) ; + DEBUG1 ((" ]\n ")) ; +#endif + nfr++ ; /* one more front */ + /* ------------------ */ + + } + + /* === All principal columns have now been ordered ====================== */ + + /* ------------------ */ + /* added for UMFPACK: */ + *p_nfr = nfr ; + /* ------------------ */ + + return (ngarbage) ; +} + + +/* ========================================================================== */ +/* === order_children deleted for UMFPACK =================================== */ +/* ========================================================================== */ + +/* ========================================================================== */ +/* === detect_super_cols ==================================================== */ +/* ========================================================================== */ + +/* + Detects supercolumns by finding matches between columns in the hash buckets. + Check amongst columns in the set A [row_start ... row_start + row_length-1]. + The columns under consideration are currently *not* in the degree lists, + and have already been placed in the hash buckets. + + The hash bucket for columns whose hash function is equal to h is stored + as follows: + + if head [h] is >= 0, then head [h] contains a degree list, so: + + head [h] is the first column in degree bucket h. + Col [head [h]].headhash gives the first column in hash bucket h. + + otherwise, the degree list is empty, and: + + -(head [h] + 2) is the first column in hash bucket h. + + For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous + column" pointer. Col [c].shared3.hash is used instead as the hash number + for that column. The value of Col [c].shared4.hash_next is the next column + in the same hash bucket. + + Assuming no, or "few" hash collisions, the time taken by this routine is + linear in the sum of the sizes (lengths) of each column whose score has + just been computed in the approximate degree computation. + Not user-callable. +*/ + +PRIVATE void detect_super_cols +( + /* === Parameters ======================================================= */ + +#ifndef NDEBUG + /* these two parameters are only needed when debugging is enabled: */ + Int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ +#endif /* NDEBUG */ + + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* row indices of A */ + Int head [], /* head of degree lists and hash buckets */ + Int row_start, /* pointer to set of columns to check */ + Int row_length /* number of columns to check */ +) +{ + /* === Local variables ================================================== */ + + Int hash ; /* hash value for a column */ + Int *rp ; /* pointer to a row */ + Int c ; /* a column index */ + Int super_c ; /* column index of the column to absorb into */ + Int *cp1 ; /* column pointer for column super_c */ + Int *cp2 ; /* column pointer for column c */ + Int length ; /* length of column super_c */ + Int prev_c ; /* column preceding c in hash bucket */ + Int i ; /* loop counter */ + Int *rp_end ; /* pointer to the end of the row */ + Int col ; /* a column index in the row to check */ + Int head_column ; /* first column in hash bucket or degree list */ + Int first_col ; /* first column in hash bucket */ + + /* === Consider each column in the row ================================== */ + + rp = &A [row_start] ; + rp_end = rp + row_length ; + while (rp < rp_end) + { + col = *rp++ ; + if (COL_IS_DEAD (col)) + { + continue ; + } + + /* get hash number for this column */ + hash = Col [col].shared3.hash ; + ASSERT (hash <= n_col) ; + + /* === Get the first column in this hash bucket ===================== */ + + head_column = head [hash] ; + if (head_column > EMPTY) + { + first_col = Col [head_column].shared3.headhash ; + } + else + { + first_col = - (head_column + 2) ; + } + + /* === Consider each column in the hash bucket ====================== */ + + for (super_c = first_col ; super_c != EMPTY ; + super_c = Col [super_c].shared4.hash_next) + { + ASSERT (COL_IS_ALIVE (super_c)) ; + ASSERT (Col [super_c].shared3.hash == hash) ; + length = Col [super_c].length ; + + /* prev_c is the column preceding column c in the hash bucket */ + prev_c = super_c ; + + /* === Compare super_c with all columns after it ================ */ + + for (c = Col [super_c].shared4.hash_next ; + c != EMPTY ; c = Col [c].shared4.hash_next) + { + ASSERT (c != super_c) ; + ASSERT (COL_IS_ALIVE (c)) ; + ASSERT (Col [c].shared3.hash == hash) ; + + /* not identical if lengths or scores are different */ + if (Col [c].length != length || + Col [c].shared2.score != Col [super_c].shared2.score) + { + prev_c = c ; + continue ; + } + + /* compare the two columns */ + cp1 = &A [Col [super_c].start] ; + cp2 = &A [Col [c].start] ; + + for (i = 0 ; i < length ; i++) + { + /* the columns are "clean" (no dead rows) */ + ASSERT (ROW_IS_ALIVE (*cp1)) ; + ASSERT (ROW_IS_ALIVE (*cp2)) ; + /* row indices will same order for both supercols, */ + /* no gather scatter nessasary */ + if (*cp1++ != *cp2++) + { + break ; + } + } + + /* the two columns are different if the for-loop "broke" */ + if (i != length) + { + prev_c = c ; + continue ; + } + + /* === Got it! two columns are identical =================== */ + + ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ; + + Col [super_c].shared1.thickness += Col [c].shared1.thickness ; + Col [c].shared1.parent = super_c ; + KILL_NON_PRINCIPAL_COL (c) ; + + Col [c].shared2.order = EMPTY ; + /* remove c from hash bucket */ + Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ; + + /* ------------------ */ + /* added for UMFPACK: */ + /* add c to end of list of super_c */ + ASSERT (Col [super_c].lastcol >= 0) ; + ASSERT (Col [super_c].lastcol < n_col) ; + Col [Col [super_c].lastcol].nextcol = c ; + Col [super_c].lastcol = Col [c].lastcol ; +#ifndef NDEBUG + /* dump the supercolumn */ + DEBUG1 (("Super")) ; + dump_super (super_c, Col, n_col) ; +#endif + /* ------------------ */ + + } + } + + /* === Empty this hash bucket ======================================= */ + + if (head_column > EMPTY) + { + /* corresponding degree list "hash" is not empty */ + Col [head_column].shared3.headhash = EMPTY ; + } + else + { + /* corresponding degree list "hash" is empty */ + head [hash] = EMPTY ; + } + } +} + + +/* ========================================================================== */ +/* === garbage_collection =================================================== */ +/* ========================================================================== */ + +/* + Defragments and compacts columns and rows in the workspace A. Used when + all avaliable memory has been used while performing row merging. Returns + the index of the first free position in A, after garbage collection. The + time taken by this routine is linear is the size of the array A, which is + itself linear in the number of nonzeros in the input matrix. + Not user-callable. +*/ + +PRIVATE Int garbage_collection /* returns the new value of pfree */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows */ + Int n_col, /* number of columns */ + Colamd_Row Row [], /* row info */ + Colamd_Col Col [], /* column info */ + Int A [], /* A [0 ... Alen-1] holds the matrix */ + Int *pfree /* &A [0] ... pfree is in use */ +) +{ + /* === Local variables ================================================== */ + + Int *psrc ; /* source pointer */ + Int *pdest ; /* destination pointer */ + Int j ; /* counter */ + Int r ; /* a row index */ + Int c ; /* a column index */ + Int length ; /* length of a row or column */ + +#ifndef NDEBUG + Int debug_rows ; + DEBUG2 (("Defrag..\n")) ; + for (psrc = &A[0] ; psrc < pfree ; psrc++) ASSERT (*psrc >= 0) ; + debug_rows = 0 ; +#endif /* NDEBUG */ + + /* === Defragment the columns =========================================== */ + + pdest = &A[0] ; + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + psrc = &A [Col [c].start] ; + + /* move and compact the column */ + ASSERT (pdest <= psrc) ; + Col [c].start = (Int) (pdest - &A [0]) ; + length = Col [c].length ; + for (j = 0 ; j < length ; j++) + { + r = *psrc++ ; + if (ROW_IS_ALIVE (r)) + { + *pdest++ = r ; + } + } + Col [c].length = (Int) (pdest - &A [Col [c].start]) ; + } + } + + /* === Prepare to defragment the rows =================================== */ + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + if (Row [r].length == 0) + { + /* :: defrag row kill :: */ + /* This row is of zero length. cannot compact it, so kill it. + * NOTE: in the current version, there are no zero-length live + * rows when garbage_collection is called. So this code will + * never trigger. However, if the code is modified, or if + * garbage_collection is called at a different place, then rows + * can be of zero length. So this test is kept, just in case. + */ + DEBUGm4 (("Defrag row kill\n")) ; + KILL_ROW (r) ; + } + else + { + /* save first column index in Row [r].shared2.first_column */ + psrc = &A [Row [r].start] ; + Row [r].shared2.first_column = *psrc ; + ASSERT (ROW_IS_ALIVE (r)) ; + /* flag the start of the row with the one's complement of row */ + *psrc = ONES_COMPLEMENT (r) ; +#ifndef NDEBUG + debug_rows++ ; +#endif /* NDEBUG */ + } + } + } + + /* === Defragment the rows ============================================== */ + + psrc = pdest ; + while (psrc < pfree) + { + /* find a negative number ... the start of a row */ + if (*psrc++ < 0) + { + psrc-- ; + /* get the row index */ + r = ONES_COMPLEMENT (*psrc) ; + ASSERT (r >= 0 && r < n_row) ; + /* restore first column index */ + *psrc = Row [r].shared2.first_column ; + ASSERT (ROW_IS_ALIVE (r)) ; + + /* move and compact the row */ + ASSERT (pdest <= psrc) ; + Row [r].start = (Int) (pdest - &A [0]) ; + length = Row [r].length ; + for (j = 0 ; j < length ; j++) + { + c = *psrc++ ; + if (COL_IS_ALIVE (c)) + { + *pdest++ = c ; + } + } + Row [r].length = (Int) (pdest - &A [Row [r].start]) ; + +#ifndef NDEBUG + debug_rows-- ; +#endif /* NDEBUG */ + + } + } + /* ensure we found all the rows */ + ASSERT (debug_rows == 0) ; + + /* === Return the new value of pfree ==================================== */ + + return ((Int) (pdest - &A [0])) ; +} + + +/* ========================================================================== */ +/* === clear_mark =========================================================== */ +/* ========================================================================== */ + +/* + Clears the Row [].shared2.mark array, and returns the new tag_mark. + Return value is the new tag_mark. Not user-callable. +*/ + +PRIVATE Int clear_mark /* return the new value for tag_mark */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows in A */ + Colamd_Row Row [] /* Row [0 ... n-1].shared2.mark is set to zero */ +) +{ + /* === Local variables ================================================== */ + + Int r ; + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + Row [r].shared2.mark = 0 ; + } + } + + /* ------------------ */ + return (1) ; + /* ------------------ */ + +} + + +/* ========================================================================== */ +/* === print_report removed for UMFPACK ===================================== */ +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === colamd debugging routines ============================================ */ +/* ========================================================================== */ + +/* When debugging is disabled, the remainder of this file is ignored. */ + +#ifndef NDEBUG + + +/* ========================================================================== */ +/* === debug_structures ===================================================== */ +/* ========================================================================== */ + +/* + At this point, all empty rows and columns are dead. All live columns + are "clean" (containing no dead rows) and simplicial (no supercolumns + yet). Rows may contain dead columns, but all live rows contain at + least one live column. +*/ + +PRIVATE void debug_structures +( + /* === Parameters ======================================================= */ + + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int n_col2 +) +{ + /* === Local variables ================================================== */ + + Int i ; + Int c ; + Int *cp ; + Int *cp_end ; + Int len ; + Int score ; + Int r ; + Int *rp ; + Int *rp_end ; + Int deg ; + + /* === Check A, Row, and Col ============================================ */ + + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + len = Col [c].length ; + score = Col [c].shared2.score ; + DEBUG4 (("initial live col "ID" "ID" "ID"\n", c, len, score)) ; + ASSERT (len > 0) ; + ASSERT (score >= 0) ; + ASSERT (Col [c].shared1.thickness == 1) ; + cp = &A [Col [c].start] ; + cp_end = cp + len ; + while (cp < cp_end) + { + r = *cp++ ; + ASSERT (ROW_IS_ALIVE (r)) ; + } + } + else + { + i = Col [c].shared2.order ; + ASSERT (i >= n_col2 && i < n_col) ; + } + } + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + i = 0 ; + len = Row [r].length ; + deg = Row [r].shared1.degree ; + ASSERT (len > 0) ; + ASSERT (deg > 0) ; + rp = &A [Row [r].start] ; + rp_end = rp + len ; + while (rp < rp_end) + { + c = *rp++ ; + if (COL_IS_ALIVE (c)) + { + i++ ; + } + } + ASSERT (i > 0) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_deg_lists ====================================================== */ +/* ========================================================================== */ + +/* + Prints the contents of the degree lists. Counts the number of columns + in the degree list and compares it to the total it should have. Also + checks the row degrees. +*/ + +PRIVATE void debug_deg_lists +( + /* === Parameters ======================================================= */ + + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int head [], + Int min_score, + Int should, + Int max_deg +) +{ + /* === Local variables ================================================== */ + + Int deg ; + Int col ; + Int have ; + Int row ; + + /* === Check the degree lists =========================================== */ + + if (n_col > 10000 && UMF_debug <= 0) + { + return ; + } + have = 0 ; + DEBUG4 (("Degree lists: "ID"\n", min_score)) ; + for (deg = 0 ; deg <= n_col ; deg++) + { + col = head [deg] ; + if (col == EMPTY) + { + continue ; + } + DEBUG4 ((ID":", deg)) ; + while (col != EMPTY) + { + DEBUG4 ((" "ID, col)) ; + have += Col [col].shared1.thickness ; + ASSERT (COL_IS_ALIVE (col)) ; + col = Col [col].shared4.degree_next ; + } + DEBUG4 (("\n")) ; + } + DEBUG4 (("should "ID" have "ID"\n", should, have)) ; + ASSERT (should == have) ; + + /* === Check the row degrees ============================================ */ + + if (n_row > 10000 && UMF_debug <= 0) + { + return ; + } + for (row = 0 ; row < n_row ; row++) + { + if (ROW_IS_ALIVE (row)) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_mark =========================================================== */ +/* ========================================================================== */ + +/* + Ensures that the tag_mark is less that the maximum and also ensures that + each entry in the mark array is less than the tag mark. +*/ + +PRIVATE void debug_mark +( + /* === Parameters ======================================================= */ + + Int n_row, + Colamd_Row Row [], + Int tag_mark, + Int max_mark +) +{ + /* === Local variables ================================================== */ + + Int r ; + + /* === Check the Row marks ============================================== */ + + ASSERT (tag_mark > 0 && tag_mark <= max_mark) ; + if (n_row > 10000 && UMF_debug <= 0) + { + return ; + } + for (r = 0 ; r < n_row ; r++) + { + ASSERT (Row [r].shared2.mark < tag_mark) ; + } +} + + +/* ========================================================================== */ +/* === debug_matrix ========================================================= */ +/* ========================================================================== */ + +/* + Prints out the contents of the columns and the rows. +*/ + +PRIVATE void debug_matrix +( + /* === Parameters ======================================================= */ + + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [] +) +{ + /* === Local variables ================================================== */ + + Int r ; + Int c ; + Int *rp ; + Int *rp_end ; + Int *cp ; + Int *cp_end ; + + /* === Dump the rows and columns of the matrix ========================== */ + + if (UMF_debug < 3) + { + return ; + } + DEBUG3 (("DUMP MATRIX:\n")) ; + for (r = 0 ; r < n_row ; r++) + { + DEBUG3 (("Row "ID" alive? %d\n", r, ROW_IS_ALIVE (r))) ; + if (ROW_IS_DEAD (r)) + { + continue ; + } + + /* ------------------ */ + /* changed for UMFPACK: */ + DEBUG3 (("start "ID" length "ID" degree "ID" thickness "ID"\n", + Row [r].start, Row [r].length, Row [r].shared1.degree, + Row [r].thickness)) ; + /* ------------------ */ + + rp = &A [Row [r].start] ; + rp_end = rp + Row [r].length ; + while (rp < rp_end) + { + c = *rp++ ; + DEBUG4 ((" %d col "ID"\n", COL_IS_ALIVE (c), c)) ; + } + } + + for (c = 0 ; c < n_col ; c++) + { + DEBUG3 (("Col "ID" alive? %d\n", c, COL_IS_ALIVE (c))) ; + if (COL_IS_DEAD (c)) + { + continue ; + } + /* ------------------ */ + /* changed for UMFPACK: */ + DEBUG3 (("start "ID" length "ID" shared1[thickness,parent] "ID + " shared2 [order,score] "ID"\n", Col [c].start, Col [c].length, + Col [c].shared1.thickness, Col [c].shared2.score)); + /* ------------------ */ + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + r = *cp++ ; + DEBUG4 ((" %d row "ID"\n", ROW_IS_ALIVE (r), r)) ; + } + + /* ------------------ */ + /* added for UMFPACK: */ + DEBUG1 (("Col")) ; + dump_super (c, Col, n_col) ; + /* ------------------ */ + + } +} + +/* ------------------ */ +/* dump_super added for UMFPACK: */ +PRIVATE void dump_super +( + Int super_c, + Colamd_Col Col [], + Int n_col +) +{ + Int col, ncols ; + + DEBUG1 ((" =[ ")) ; + ncols = 0 ; + for (col = super_c ; col != EMPTY ; col = Col [col].nextcol) + { + DEBUG1 ((" "ID, col)) ; + ASSERT (col >= 0 && col < n_col) ; + if (col != super_c) + { + ASSERT (COL_IS_DEAD (col)) ; + } + if (Col [col].nextcol == EMPTY) + { + ASSERT (col == Col [super_c].lastcol) ; + } + ncols++ ; + ASSERT (ncols <= Col [super_c].shared1.thickness) ; + } + ASSERT (ncols == Col [super_c].shared1.thickness) ; + DEBUG1 (("]\n")) ; +} +/* ------------------ */ + + +#endif /* NDEBUG */ diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_colamd.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_colamd.h new file mode 100644 index 0000000000..b33ebe197d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_colamd.h @@ -0,0 +1,256 @@ +/* ========================================================================== */ +/* === umf_colamd.h ========================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + +Authors: + + The authors of the COLAMD code itself are Stefan I. Larimore and Timothy A. + Davis, University of Florida. The algorithm was developed in collaboration + with John Gilbert, Xerox PARC, and Esmond Ng, Oak Ridge National Laboratory. + +Date: + + UMFPACK Version: see above. + COLAMD Version 2.0 was released on January 31, 2000. + +Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974, DMS-9803599, and CCR-0203270. + +UMFPACK: Copyright (c) 2003 by Timothy A. Davis. All Rights Reserved. + +See the UMFPACK README file for the License for your use of this code. + +Availability: + + Both UMFPACK and the original unmodified colamd/symamd library are + available at http://www.cise.ufl.edu/research/sparse. + +*/ + +#ifndef COLAMD_H +#define COLAMD_H + +/* ========================================================================== */ +/* === Include files ======================================================== */ +/* ========================================================================== */ + +#include + +/* ========================================================================== */ +/* === Knob and statistics definitions ====================================== */ +/* ========================================================================== */ + +/* size of the knobs [ ] array. Only knobs [0..2] are currently used. */ +#define COLAMD_KNOBS 20 + +/* number of output statistics. Only stats [0..8] are currently used. */ +#define COLAMD_STATS 20 + +/* knobs [0] and stats [0]: dense row knob and output statistic. */ +#define COLAMD_DENSE_ROW 0 + +/* knobs [1] and stats [1]: dense column knob and output statistic. */ +#define COLAMD_DENSE_COL 1 + +/* knobs [2]: aggressive absorption option */ +#define COLAMD_AGGRESSIVE 2 + +/* stats [2]: memory defragmentation count output statistic */ +#define COLAMD_DEFRAG_COUNT 2 + +/* stats [3]: colamd status: zero OK, > 0 warning or notice, < 0 error */ +#define COLAMD_STATUS 3 + +/* stats [4..6]: error info, or info on jumbled columns */ +#define COLAMD_INFO1 4 +#define COLAMD_INFO2 5 +#define COLAMD_INFO3 6 + +/* ------------------ */ +/* added for UMFPACK: */ +/* stats [7]: number of originally empty rows */ +#define COLAMD_EMPTY_ROW 7 +/* stats [8]: number of originally empty cols */ +#define COLAMD_EMPTY_COL 8 +/* stats [9]: number of rows with entries only in dense cols */ +#define COLAMD_NEWLY_EMPTY_ROW 9 +/* stats [10]: number of cols with entries only in dense rows */ +#define COLAMD_NEWLY_EMPTY_COL 10 +/* ------------------ */ + +/* error codes returned in stats [3]: */ +#define COLAMD_OK (0) +#define COLAMD_ERROR_jumbled_matrix (-11) +#define COLAMD_ERROR_A_not_present (-1) +#define COLAMD_ERROR_p_not_present (-2) +#define COLAMD_ERROR_nrow_negative (-3) +#define COLAMD_ERROR_ncol_negative (-4) +#define COLAMD_ERROR_nnz_negative (-5) +#define COLAMD_ERROR_p0_nonzero (-6) +#define COLAMD_ERROR_A_too_small (-7) +#define COLAMD_ERROR_col_length_negative (-8) +#define COLAMD_ERROR_row_index_out_of_bounds (-9) +#define COLAMD_ERROR_out_of_memory (-10) +#define COLAMD_ERROR_internal_error (-999) + +/* ========================================================================== */ +/* === Row and Column structures ============================================ */ +/* ========================================================================== */ + +/* User code that makes use of the colamd/symamd routines need not directly */ +/* reference these structures. They are used only for the COLAMD_RECOMMENDED */ +/* macro. */ + +typedef struct Colamd_Col_struct +{ + Int start ; /* index for A of first row in this column, or DEAD */ + /* if column is dead */ + Int length ; /* number of rows in this column */ + union + { + Int thickness ; /* number of original columns represented by this */ + /* col, if the column is alive */ + Int parent ; /* parent in parent tree super-column structure, if */ + /* the column is dead */ + } shared1 ; + union + { + Int score ; /* the score used to maintain heap, if col is alive */ + Int order ; /* pivot ordering of this column, if col is dead */ + } shared2 ; + union + { + Int headhash ; /* head of a hash bucket, if col is at the head of */ + /* a degree list */ + Int hash ; /* hash value, if col is not in a degree list */ + Int prev ; /* previous column in degree list, if col is in a */ + /* degree list (but not at the head of a degree list) */ + } shared3 ; + union + { + Int degree_next ; /* next column, if col is in a degree list */ + Int hash_next ; /* next column, if col is in a hash list */ + } shared4 ; + + /* ------------------ */ + /* added for UMFPACK: */ + Int nextcol ; /* next column in this supercolumn */ + Int lastcol ; /* last column in this supercolumn */ + /* ------------------ */ + +} Colamd_Col ; + +typedef struct Colamd_Row_struct +{ + Int start ; /* index for A of first col in this row */ + Int length ; /* number of principal columns in this row */ + union + { + Int degree ; /* number of principal & non-principal columns in row */ + Int p ; /* used as a row pointer in init_rows_cols () */ + } shared1 ; + union + { + Int mark ; /* for computing set differences and marking dead rows*/ + Int first_column ;/* first column in row (used in garbage collection) */ + } shared2 ; + + /* ------------------ */ + /* added for UMFPACK: */ + Int thickness ; /* number of original rows represented by this row */ + /* that are not yet pivotal */ + Int front ; /* -1 if an original row */ + /* k if this row represents the kth frontal matrix */ + /* where k goes from 0 to at most n_col-1 */ + /* ------------------ */ + +} Colamd_Row ; + + + +/* ========================================================================== */ +/* === Colamd recommended memory size ======================================= */ +/* ========================================================================== */ + +/* + The recommended length Alen of the array A passed to colamd is given by + the COLAMD_RECOMMENDED (nnz, n_row, n_col) macro. It returns -1 if any + argument is negative. 2*nnz space is required for the row and column + indices of the matrix. COLAMD_C (n_col) + COLAMD_R (n_row) space is + required for the Col and Row arrays, respectively, which are internal to + colamd. An additional n_col space is the minimal amount of "elbow room", + and nnz/5 more space is recommended for run time efficiency. + + This macro is not needed when using symamd. +*/ + +/* about 8*(n_col+1) integers: */ +#define UMF_COLAMD_C(n_col) ((n_col + 1) * sizeof (Colamd_Col) / sizeof (Int)) + +/* about 6*(n_row+1) integers: */ +#define UMF_COLAMD_R(n_row) ((n_row + 1) * sizeof (Colamd_Row) / sizeof (Int)) + +/* UMFPACK: make sure Alen is >= 5*n_col + size of Col and Row structures. + * Alen is typically about 2.2*nz + 9*n_col + 6*n_row, or 2.2nz+15n for + * square matrices. */ +#define UMF_COLAMD_RECOMMENDED(nnz, n_row, n_col) \ +( \ +((nnz) < 0 || (n_row) < 0 || (n_col) < 0) \ +? \ + (-1) \ +: \ + (MAX (2 * (nnz), 4 * (n_col)) + \ + (Int) UMF_COLAMD_C (n_col) + \ + (Int) UMF_COLAMD_R (n_row) + (n_col) + ((nnz) / 5)) \ +) + +/* ========================================================================== */ +/* === Prototypes of user-callable routines ================================= */ +/* ========================================================================== */ + +/* colamd_recommended removed for UMFPACK */ + +void UMF_colamd_set_defaults /* sets default parameters */ +( /* knobs argument is modified on output */ + double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ +) ; + +Int UMF_colamd /* returns (1) if successful, (0) otherwise*/ +( /* A and p arguments are modified on output */ + Int n_row, /* number of rows in A */ + Int n_col, /* number of columns in A */ + Int Alen, /* size of the array A */ + Int A [], /* row indices of A, of size Alen */ + Int p [], /* column pointers of A, of size n_col+1 */ + double knobs [COLAMD_KNOBS],/* parameter settings for colamd */ + Int stats [COLAMD_STATS] /* colamd output statistics and error codes */ + /* ------------------ */ + /* added for UMFPACK: */ + , Int Front_npivcol [ ] + , Int Front_nrows [ ] + , Int Front_ncols [ ] + , Int Front_parent [ ] + , Int Front_cols [ ] + , Int *p_nfr + , Int InFront [ ] + /* ------------------ */ +) ; + +/* symamd deleted for UMFPACK */ + +/* colamd_report deleted for UMFPACK */ + +/* symamd_report deleted for UMFPACK */ + +#endif /* COLAMD_H */ diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_config.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_config.h new file mode 100644 index 0000000000..ea4b641418 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_config.h @@ -0,0 +1,920 @@ +/* ========================================================================== */ +/* === umf_config.h ========================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + This file controls the compile-time configuration of UMFPACK. Modify the + Makefile, the architecture-dependent Make.* file, and this file if + necessary, to control these options. The following flags may be given + as options to your C compiler (as in "cc -DNBLAS", for example). These + flags are normally placed in your CONFIG string, defined in your Make.*. + + All of these options, except for the timer, are for accessing the BLAS. + + -DNBLAS + + BLAS mode. If -DNBLAS is set, then no BLAS will be used. Vanilla + C code will be used instead. This is portable, and easier to + install, but you won't get the best performance. + + If -DNBLAS is not set, then externally-available BLAS routines + (dgemm, dger, and dgemv or the equivalent C-BLAS routines) will be + used. This will give you the best performance, but perhaps at the + expense of portability. + + The default is to use the BLAS, for both the C-callable libumfpack.a + library and the MATLAB mexFunction. If you have trouble installing + UMFPACK, set -DNBLAS (but then UMFPACK will be slow). + + -DCBLAS + + If -DCBLAS is set, then the C-BLAS interface to the BLAS is + used. If your vendor-supplied BLAS library does not have a C-BLAS + interface, you can obtain the ATLAS BLAS, available at + http://www.netlib.org/atlas. + + This flag is ignored if -DNBLAS is set. + + -DLP64 + + This should be defined if you are compiling in the LP64 model + (32 bit int's, 64 bit long's, and 64 bit pointers). In Solaris, + this is obtained with the flags -xtarget=ultra -xarch=v9 for + the cc compiler (for example). + + -DLONGBLAS + + If not defined, then the BLAS are not called in the long integer + version of UMFPACK (the umfpack_*l_* routines). The most common + definitions of the BLAS, unfortunately, use int arguments, and + are thus not suitable for use in the LP64 model. Only the Sun + Performance Library, as far as I can tell, has a version of the + BLAS that allows long integer (64-bit) input arguments. This + flag is set automatically in Sun Solaris if you are using the + Sun Performance BLAS. You can set it yourself, too, if your BLAS + routines can take long integer input arguments. + + -DNSUNPERF + + Applies only to Sun Solaris. If -DNSUNPERF is set, then the Sun + Performance Library BLAS will not be used. + + The Sun Performance Library BLAS is used by default when compiling + the C-callable libumfpack.a library on Sun Solaris. + + This flag is ignored if -DNBLAS is set. + + -DNSCSL + + Applies only to SGI IRIX. If -DSCSL is set, then the SGI SCSL + Scientific Library BLAS will not be used. + + The SGI SCSL Scientific Library BLAS is used by default when + compiling the C-callable libumfpack.a library on SGI IRIX. + + This flag is ignored if -DNBLAS is set. + + -DNPOSIX + + If -DNPOSIX is set, then your Unix operating system is not POSIX- + compliant, and the POSIX routines sysconf ( ) and times ( ) + routines are not used. These routines provide CPU time and + wallclock time information. If -DNPOSIX is set, then the ANSI + C clock ( ) routine is used. If -DNPOSIX is not set, then + sysconf ( ) and times ( ) are used in umfpack_tic and umfpack_toc. + See umfpack_tictoc.c for more information. + The default is to use the POSIX routines, except for Windows, + which is not POSIX-compliant. + + -DGETRUSAGE + + If -DGETRUSAGE is set, then your system's getrusage ( ) routine + will be used for getting the process CPU time. Otherwise the ANSI + C clock ( ) routine will be used. The default is to use getrusage + ( ) on Unix systems, and to use clock on all other architectures. + + -DNO_TIMER + + If -DNO_TIMER is set, then no timing routines are used at all. + + -DNUTIL + + If -DNUTIL is set, then the internal MATLAB utMalloc, utFree, and + utRealloc routines are not used in the UMFPACK mexFunction. The + regular mxMalloc, mxFree, and mxRealloc routines are used instead. + These routines are not documented, but are available for use. For + Windows, -DNUTIL is defined below, because access to the ut* + routines is not available by default. + + -DNRECIPROCAL + + This option controls a tradeoff between speed and accuracy. Using + -DNRECIPROCAL can lead to more accurate results, but with perhaps + some cost in performance, particularly if floating-point division + is much more costly than floating-point multiplication. + + This option determines the method used to scale the pivot column. + If set, or if the absolute value of the pivot is < 1e-12 (or is a + NaN), then the pivot column is divided by the pivot value. + Otherwise, the reciprocal of the pivot value is computed, and the + pivot column is multiplied by (1/pivot). Multiplying by the + reciprocal can be slightly less accurate than dividing by the + pivot, but it is often faster. See umf_scale.c. + + This has a small effect on the performance of UMFPACK, at least on + a Pentium 4M. It may have a larger effect on other architectures + where floating-point division is much more costly than floating- + point multiplication. The RS 6000 is one such example. + + By default, the method chosen is to multiply by the reciprocal + (sacrificing accuracy for speed), except when compiling UMFPACK + as a built-in routine in MATLAB, or when gcc is being used. + + When MATHWORKS is defined, -DNRECIPROCAL is forced on, and the pivot + column is divided by the pivot value. The only way of using the + other method in this case is to edit this file. + + If -DNRECIPROCAL is enabled, then the row scaling factors are always + applied by dividing each row by the scale factor, rather than + multiplying by the reciprocal. If -DNRECIPROCAL is not enabled + (the default case), then the scale factors are normally applied by + multiplying by the reciprocal. If, however, the smallest scale + factor is tiny, then the scale factors are applied via division. + + -DNO_DIVIDE_BY_ZERO + + If the pivot is zero, and this flag is set, then no divide-by-zero + occurs. + + You should normally not set these flags yourself: + + -DBLAS_BY_VALUE if scalars are passed by value, not reference + -DBLAS_NO_UNDERSCORE if no underscore should be appended + -DBLAS_CHAR_ARG if BLAS options are single char's, not strings + + The BLAS options are normally set automatically. If your + architecture cannot be determined (see UMFPACK_ARCHITECTURE, below) + then you may need to set these flags yourself. + + The following options are controlled by amd_internal.h: + + -DMATLAB_MEX_FILE + + This flag is turned on when compiling the umfpack mexFunction for + use in MATLAB. When compiling the MATLAB mexFunction, the MATLAB + BLAS are used (unless -DNBLAS is set). The -DCBLAS, -DNSCSL, and + -DNSUNPERF flags are all ignored. The -DNRECIPROCAL flag is + forced on. Otherwise, [L,U,P,Q,R] = umfpack (A) would return + either L*U = P*(R\A)*Q or L*U = P*R*A*Q. Rather than returning a + flag stating how the scale factors R are to be applied, the umfpack + mexFunction always takes the more accurate route and returns + L*U = P*(R\A)*Q. + + -DMATHWORKS + + This flag is turned on when compiling umfpack as a built-in routine + in MATLAB. The MATLAB BLAS are used for all architectures (-DNBLAS, + -DCBLAS, -DNSCSL, and -DNSUNPERF flags are all ignored). Internal + routines utMalloc, utFree, utRealloc, utPrintf, utDivideComplex, + and utFdlibm_hypot are used, and the "util.h" file is included. + This avoids the problem discussed in the User Guide regarding memory + allocation in MATLAB. utMalloc returns NULL on failure, instead of + terminating the mexFunction (which is what mxMalloc does). However, + the ut* routines are not documented by The MathWorks, Inc., so I + cannot guarantee that you will always be able to use them. + The -DNRECIPROCAL flag is turned on. + + -DNDEBUG + + Debugging mode (if NDEBUG is not defined). The default, of course, + is no debugging. Turning on debugging takes some work (see below). + If you do not edit this file, then debugging is turned off anyway, + regardless of whether or not -DNDEBUG is specified in your compiler + options. +*/ + +/* ========================================================================== */ +/* === AMD configuration ==================================================== */ +/* ========================================================================== */ + +/* NDEBUG, PRINTF defined in amd_internal.h */ + +/* ========================================================================== */ +/* === reciprocal option ==================================================== */ +/* ========================================================================== */ + +/* Force the definition NRECIPROCAL when MATHWORKS or MATLAB_MEX_FILE + * are defined. Do not multiply by the reciprocal in those cases. */ + +#ifndef NRECIPROCAL +#if defined (MATHWORKS) || defined (MATLAB_MEX_FILE) +#define NRECIPROCAL +#endif +#endif + +/* ========================================================================== */ +/* === Microsoft Windows configuration ====================================== */ +/* ========================================================================== */ + +#ifdef UMF_WINDOWS +/* Windows can't access the ut* routines, and it isn't Unix. */ +#define NUTIL +#define NPOSIX +#endif + +/* ========================================================================== */ +/* === 0-based or 1-based printing ========================================== */ +/* ========================================================================== */ + +#if defined (MATLAB_MEX_FILE) && defined (NDEBUG) +/* In MATLAB, matrices are 1-based to the user, but 0-based internally. */ +/* One is added to all row and column indices when printing matrices */ +/* for the MATLAB user. The +1 shift is turned off when debugging. */ +#define INDEX(i) ((i)+1) +#else +/* In ANSI C, matrices are 0-based and indices are reported as such. */ +/* This mode is also used for debug mode, and if MATHWORKS is defined rather */ +/* than MATLAB_MEX_FILE. */ +#define INDEX(i) (i) +#endif + +/* ========================================================================== */ +/* === Timer ================================================================ */ +/* ========================================================================== */ + +/* + If you have the getrusage routine (all Unix systems I've test do), then use + that. Otherwise, use the ANSI C clock function. Note that on many + systems, the ANSI clock function wraps around after only 2147 seconds, or + about 36 minutes. BE CAREFUL: if you compare the run time of UMFPACK with + other sparse matrix packages, be sure to use the same timer. See + umfpack_tictoc.c for the timer used internally by UMFPACK. See also + umfpack_timer.c for the timer used in an earlier version of UMFPACK (V4.0). + That timer is still available as a user-callable routine, but it is no + longer used internally by UMFPACK. +*/ + +/* Sun Solaris, SGI Irix, Linux, Compaq Alpha, and IBM RS 6000 all have */ +/* getrusage. It's in BSD unix, so perhaps all unix systems have it. */ +#if defined (UMF_SOL2) || defined (UMF_SGI) || defined (UMF_LINUX) \ +|| defined (UMF_ALPHA) || defined (UMF_AIX) +#define GETRUSAGE +#endif + + +/* ========================================================================== */ +/* === BLAS ================================================================= */ +/* ========================================================================== */ + +/* + The adventure begins. Figure out how to call the BLAS ... + + This works, but it is incredibly ugly. The C-BLAS was supposed to solve + this problem, and make it easier to interface a C program to the BLAS. + Unfortunately, the C-BLAS does not have a "long" integer (64 bit) version. + Various vendors have done their own 64-bit BLAS. Sun has dgemm_64 routines + with "long" integers, SGI has a 64-bit dgemm in their scsl_blas_i8 library + with "long long" integers, and so on. + + Different vendors also have different ways of defining a complex number, + some using struct's. That's a bad idea. See umf_version.h for the better + way to do it (the method that was also chosen for the complex C-BLAS, + which is compatible and guaranteed to be portable with ANSI C). + + To make matters worse, SGI's SCSL BLAS has a C-BLAS interface which + differs from the ATLAS C-BLAS interface (see immediately below); + although a more recent version of SGI's C-BLAS interface is correct + if SCSL_VOID_ARGS is defined. +*/ + + +/* -------------------------------------------------------------------------- */ +/* Determine which BLAS to use. */ +/* -------------------------------------------------------------------------- */ + +#if defined (MATHWORKS) +#define USE_MATLAB_BLAS + +#elif defined (NBLAS) +#define USE_NO_BLAS + +#elif defined (MATLAB_MEX_FILE) +#define USE_MATLAB_BLAS + +#elif defined (CBLAS) +#define USE_C_BLAS + +#elif defined (UMF_SOL2) && !defined (NSUNPERF) +#define USE_SUNPERF_BLAS + +#elif defined (UMF_SGI) && !defined (NSCSL) +#define USE_SCSL_BLAS + +#else +#define USE_FORTRAN_BLAS +#endif + +/* -------------------------------------------------------------------------- */ +/* int vs. long integer arguments */ +/* -------------------------------------------------------------------------- */ + +/* + Determine if the BLAS exists for the long integer version. It exists if + LONGBLAS is defined in the Makefile, or if using the BLAS from the + Sun Performance Library, or SGI's SCSL Scientific Library. +*/ + +#if defined (USE_SUNPERF_BLAS) || defined (USE_SCSL_BLAS) +#ifndef LONGBLAS +#define LONGBLAS +#endif +#endif + +/* do not use the BLAS if Int's are long and LONGBLAS is not defined */ +#if defined (LONG_INTEGER) && !defined (LONGBLAS) && !defined (USE_NO_BLAS) +#define USE_NO_BLAS +#endif + + +/* -------------------------------------------------------------------------- */ +/* Use (void *) arguments for the SGI */ +/* -------------------------------------------------------------------------- */ + +#if defined (UMF_SGI) +/* + Use (void *) pointers for complex types in SCSL. + The ATLAS C-BLAS, and the SGI C-BLAS differ. The former uses (void *) + arguments, the latter uses SCSL_ZOMPLEX_T, which are either scsl_zomplex + or (void *). Using (void *) is simpler, and is selected by defining + SCSL_VOID_ARGS, below. The cc compiler doesn't complain, but gcc is + more picky, and generates a warning without this next statement. + With gcc and the 07/09/98 version of SGI's cblas.h, spurious warnings + about complex BLAS arguments will be reported anyway. This is because this + older version of SGI's cblas.h does not make use of the SCSL_VOID_ARGS + parameter, which is present in the 12/6/01 version of SGI's cblas.h. You + can safely ignore these warnings. +*/ +#define SCSL_VOID_ARGS +#endif + + +/* -------------------------------------------------------------------------- */ +/* The BLAS exists, construct appropriate macros */ +/* -------------------------------------------------------------------------- */ + +#if !defined (USE_NO_BLAS) /* { */ + +/* + If the compile-time flag -DNBLAS is defined, then the BLAS are not used, + portable vanilla C code is used instead, and the remainder of this file + is ignored. + + Using the BLAS is much faster, but how C calls the Fortran BLAS is + machine-dependent and thus can cause portability problems. Thus, use + -DNBLAS to ensure portability (at the expense of speed). + + Preferences: + + *** The best interface to use, regardless of the option you select + below, is the standard C-BLAS interface. Not all BLAS libraries + use this interface. The only problem with this interface is that + it does not extend to the LP64 model. The C-BLAS does not provide + for a 64-bit integer. In addition, SGI's older cblas.h can cause + spurious warnings when using the C-BLAS interface. + + 1) often the most preferred (but see option (3)): use the + optimized vendor-supplied library (such as the Sun Performance + Library, or IBM's ESSL). This is often the fastest, but might not + be portable and might not always be available. When compiling a + MATLAB mexFunction it might be difficult get the mex compiler + script to recognize the vendor- supplied BLAS. Note that the + freely-available BLAS (option 3) can be faster than the vendor- + specific BLAS. You are encourage to try both option (1) and (3). + + 2) When compiling the UMFPACK mexFunction to use UMFPACK in MATLAB, use + the BLAS provided by The Mathworks, Inc. This assumes you are using + MATLAB V6 or higher, since the BLAS are not incorporated in V5 or + earlier versions. On my Sun workstation, the MATLAB BLAS gave + slightly worse performance than the Sun Perf. BLAS. The advantage + of using the MATLAB BLAS is that it's available on any computer that + has MATLAB V6 or higher. I have not tried using MATLAB BLAS outside + of a mexFunction in a stand-alone C code, but MATLAB (V6) allows for + this. This is well worth trying if you have MATLAB and don't want + to bother installing the ATLAS BLAS (option 3a, below). The only + glitch to this is that MATLAB does not provide a portable interface + to the BLAS (an underscore is required for some but not all + architectures). For Windows and MATLAB 6.0 or 6.1, you also need + to copy the libmwlapack.dll file into your MATLAB installation + directory; see the User Guide for details. + + In the current distribution, the only BLAS that the UMFPACK + mexFunction will use is the internal MATLAB BLAS. It's possible to + use other BLAS, but handling the porting of using the mex compiler + with different BLAS libraries is not trivial. + + As of MATLAB 6.5, the BLAS used internally in MATLAB is the ATLAS + BLAS. + + 3) Use a freely-available high-performance BLAS library: + + (a) The BLAS by Kazashige Goto and Robert van de Geijn, at + http://www.cs.utexas.edu/users/flame/goto. This BLAS increased + the performance of UMFPACK by almost 50% as compared to the + ATLAS BLAS (v3.2). + + (b) The ATLAS BLAS, available at http://www.netlib.org/atlas, + by R. Clint Whaley, Antoine Petitet, and Jack Dongarra. + This has a standard C interface, and thus the interface to it is + fully portable. Its performance rivals, and sometimes exceeds, + the vendor-supplied BLAS on many computers. + + (b) The Fortran RISC BLAS by Michel Dayde', Iain Duff, Antoine + Petitet, and Abderrahim Qrichi Aniba, available via anonymous + ftp to ftp.enseeiht.fr in the pub/numerique/BLAS/RISC directory, + See M. J. Dayde' and I. S. Duff, "The RISC BLAS: A blocked + implementation of level 3 BLAS for RISC processors, ACM Trans. + Math. Software, vol. 25, no. 3., Sept. 1999. This will give + you good performance, but with the same C-to-Fortran portability + problems as option (1). + + 4) Use UMFPACK's built-in vanilla C code by setting -DNBLAS at compile + time. The key advantage is portability, which is guaranteed if you + have an ANSI C compliant compiler. You also don't need to download + any other package - UMFPACK is stand-alone. No Fortran is used + anywhere in UMFPACK. UMFPACK will be much slower than when using + options (1) through (3), however. + + 5) least preferred: use the standard Fortran implementation of the + BLAS, also available at Netlib (http://www.netlib.org/blas). This + will be no faster than option (4), and not portable because of + C-to-Fortran calling conventions. Don't bother trying option (5). + + The mechanics of how C calls the BLAS on various computers are as follows: + + * C-BLAS (from the ATLAS library, for example): + The same interface is used on all computers. + + * Defaults for calling the Fortran BLAS: + add underscore, pass scalars by reference, use string arguments. + + * The Fortran BLAS on Sun Solaris (when compiling the MATLAB mexFunction + or when using the Fortran RISC BLAS), SGI IRIX, Linux, and Compaq + Alpha: use defaults. + + * Sun Solaris (when using the C-callable Sun Performance library): + no underscore, pass scalars by value, use character arguments. + + * The Fortran BLAS (ESSL Library) on the IBM RS 6000, and HP Unix: + no underscore, pass scalars by reference, use string arguments. + + * The Fortran BLAS on Windows: + no underscore, pass scalars by reference, use string arguments. + If you compile the umfpack mexFunction using umfpack_make, and are + using the lcc compiler bundled with MATLAB, then you must first + copy the umfpack\lcc_lib\libmwlapack.lib file into the + \extern\lib\win32\lcc\ directory, where is the + directory in which MATLAB is installed. Next, type mex -setup + at the MATLAB prompt, and ask MATLAB to select the lcc compiler. + MATLAB has built-in BLAS, but it cannot be accessed by a program + compiled by lcc without first copying this file. +*/ + + + +/* -------------------------------------------------------------------------- */ +#ifdef USE_C_BLAS /* { */ +/* -------------------------------------------------------------------------- */ + + +/* -------------------------------------------------------------------------- */ +/* use the C-BLAS (any computer) */ +/* -------------------------------------------------------------------------- */ + +/* + C-BLAS is the default interface, with the following exceptions. Solaris + uses the Sun Performance BLAS for libumfpack.a (the C-callable library). + SGI IRIX uses the SCSL BLAS for libumfpack.a. All architectures use + MATLAB's internal BLAS for the mexFunction on any architecture. These + options are set in the Make.* files. The Make.generic file uses no BLAS + at all. + + If you use the ATLAS C-BLAS, then be sure to set the -I flag to + -I/path/ATLAS/include, where /path/ATLAS is the ATLAS installation + directory. See Make.solaris for an example. You do not need to do this + for the SGI, which has a /usr/include/cblas.h. +*/ + +#include "cblas.h" + +#ifdef COMPLEX +#define BLAS_GEMM_ROUTINE cblas_zgemm +#define BLAS_TRSM_ROUTINE cblas_ztrsm +#define BLAS_TRSV_ROUTINE cblas_ztrsv +#define BLAS_GEMV_ROUTINE cblas_zgemv +#define BLAS_GER_ROUTINE cblas_zgeru +#define BLAS_SCAL_ROUTINE cblas_zscal +#define BLAS_COPY_ROUTINE cblas_zcopy +#define BLAS_DECLARE_SCALAR(x) double x [2] +#define BLAS_ASSIGN(x,xr,xi) { x [0] = xr ; x [1] = xi ; } +#else +#define BLAS_GEMM_ROUTINE cblas_dgemm +#define BLAS_TRSM_ROUTINE cblas_dtrsm +#define BLAS_TRSV_ROUTINE cblas_dtrsv +#define BLAS_GEMV_ROUTINE cblas_dgemv +#define BLAS_GER_ROUTINE cblas_dger +#define BLAS_SCAL_ROUTINE cblas_dscal +#define BLAS_COPY_ROUTINE cblas_dcopy +#define BLAS_DECLARE_SCALAR(x) double x +#define BLAS_ASSIGN(x,xr,xi) { x = xr ; } +#endif + +#define BLAS_LOWER CblasLower +#define BLAS_UNIT_DIAGONAL CblasUnit +#define BLAS_RIGHT CblasRight +#define BLAS_NO_TRANSPOSE CblasNoTrans +#define BLAS_TRANSPOSE CblasTrans +#define BLAS_COLUMN_MAJOR_ORDER CblasColMajor, +#define BLAS_SCALAR(x) x +#define BLAS_INT_SCALAR(n) n +#define BLAS_ARRAY(a) a + + + +/* -------------------------------------------------------------------------- */ +#else /* } USE_C_BLAS { */ +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* use Fortran (or other architecture-specific) BLAS */ +/* -------------------------------------------------------------------------- */ + +/* No such argument when not using the C-BLAS */ +#define BLAS_COLUMN_MAJOR_ORDER + +/* Determine which architecture we're on and set options accordingly. */ +/* The default, if nothing is defined is to add an underscore, */ +/* pass scalars by reference, and use string arguments. */ + +/* ---------------------------------- */ +/* Sun Performance BLAS */ +/* ---------------------------------- */ + +#ifdef USE_SUNPERF_BLAS +#ifdef _SUNPERF_H +/* has been included somehow anyway, outside of umf_config.h */ +#error "sunperf.h must NOT be #include'd. See umf_config.h for details." +#endif +#define BLAS_BY_VALUE +#define BLAS_NO_UNDERSCORE +#define BLAS_CHAR_ARG +#endif /* USE_SUNPERF_BLAS */ + +/* ---------------------------------- */ +/* SGI SCSL BLAS */ +/* ---------------------------------- */ + +#ifdef USE_SCSL_BLAS +#if defined (LP64) +#include +#else +#include +#endif +#define BLAS_BY_VALUE +#define BLAS_NO_UNDERSCORE +#endif /* USE_SCSL_BLAS */ + +/* ---------------------------------- */ +/* IBM AIX, Windows, and HP Fortran BLAS */ +/* ---------------------------------- */ + +#if defined (UMF_AIX) || defined (UMF_WINDOWS) || defined (UMF_HP) +#define BLAS_NO_UNDERSCORE +#endif + + +/* -------------------------------------------------------------------------- */ +/* BLAS names */ +/* -------------------------------------------------------------------------- */ + +#if defined (LP64) && defined (USE_SUNPERF_BLAS) && defined (LONG_INTEGER) + +/* 64-bit sunperf BLAS, for Sun Solaris only */ +#ifdef COMPLEX +#define BLAS_GEMM_ROUTINE zgemm_64 +#define BLAS_TRSM_ROUTINE ztrsm_64 +#define BLAS_TRSV_ROUTINE ztrsv_64 +#define BLAS_GEMV_ROUTINE zgemv_64 +#define BLAS_GER_ROUTINE zgeru_64 +#define BLAS_SCAL_ROUTINE zscal_64 +#define BLAS_COPY_ROUTINE zcopy_64 +#else +#define BLAS_GEMM_ROUTINE dgemm_64 +#define BLAS_TRSM_ROUTINE dtrsm_64 +#define BLAS_TRSV_ROUTINE dtrsv_64 +#define BLAS_GEMV_ROUTINE dgemv_64 +#define BLAS_GER_ROUTINE dger_64 +#define BLAS_SCAL_ROUTINE dscal_64 +#define BLAS_COPY_ROUTINE dcopy_64 +#endif /* COMPLEX */ + +#else + +#ifdef COMPLEX + +/* naming convention (use underscore, or not) */ +#ifdef BLAS_NO_UNDERSCORE +#define BLAS_GEMM_ROUTINE zgemm +#define BLAS_TRSM_ROUTINE ztrsm +#define BLAS_TRSV_ROUTINE ztrsv +#define BLAS_GEMV_ROUTINE zgemv +#define BLAS_GER_ROUTINE zgeru +#define BLAS_SCAL_ROUTINE zscal +#define BLAS_COPY_ROUTINE zcopy +#else +/* default: add underscore */ +#define BLAS_GEMM_ROUTINE zgemm_ +#define BLAS_TRSM_ROUTINE ztrsm_ +#define BLAS_TRSV_ROUTINE ztrsv_ +#define BLAS_GEMV_ROUTINE zgemv_ +#define BLAS_GER_ROUTINE zgeru_ +#define BLAS_SCAL_ROUTINE zscal_ +#define BLAS_COPY_ROUTINE zcopy_ +#endif + +#else + +/* naming convention (use underscore, or not) */ +#ifdef BLAS_NO_UNDERSCORE +#define BLAS_GEMM_ROUTINE dgemm +#define BLAS_TRSM_ROUTINE dtrsm +#define BLAS_TRSV_ROUTINE dtrsv +#define BLAS_GEMV_ROUTINE dgemv +#define BLAS_GER_ROUTINE dger +#define BLAS_SCAL_ROUTINE dscal +#define BLAS_COPY_ROUTINE dcopy +#else +/* default: add underscore */ +#define BLAS_GEMM_ROUTINE dgemm_ +#define BLAS_TRSM_ROUTINE dtrsm_ +#define BLAS_TRSV_ROUTINE dtrsv_ +#define BLAS_GEMV_ROUTINE dgemv_ +#define BLAS_GER_ROUTINE dger_ +#define BLAS_SCAL_ROUTINE dscal_ +#define BLAS_COPY_ROUTINE dcopy_ +#endif + +#endif /* COMPLEX */ + +#endif /* LP64 && USE_SUNPERF_BLAS */ + + +/* -------------------------------------------------------------------------- */ +/* BLAS real or complex floating-point scalars */ +/* -------------------------------------------------------------------------- */ + +#ifdef COMPLEX + +/* + The SunPerf BLAS expects to see a doublecomplex scalar, but it + also will accept an array of size 2. See the manual, normally at + file:///opt/SUNWspro/WS6U1/lib/locale/C/html/manuals/perflib/user_guide + /plug_using_perflib.html . This manual is inconsistent with the man pages + for zgemm, zgemv, and zgeru and also inconsistent with the + include file. Use this instead, for SunPerf (only works if you do NOT + include sunperf.h). Fortunately, this file (umf_config.h) is not included + in any user code that calls UMFPACK. Thus, the caller may include + sunperf.h in his or her own code, and that is safely ignored here. + SGI's SCSL BLAS has yet a different kind of struct, but we can use a + double array of size 2 instead (since SCSL_VOID_ARGS is defined). + Most BLAS expect complex scalars as pointers to double arrays of size 2. +*/ + +#define BLAS_DECLARE_SCALAR(x) double x [2] +#define BLAS_ASSIGN(x,xr,xi) { x [0] = xr ; x [1] = xi ; } +#define BLAS_SCALAR(x) x + +#else + +#define BLAS_DECLARE_SCALAR(x) double x +#define BLAS_ASSIGN(x,xr,xi) { x = xr ; } +#ifdef BLAS_BY_VALUE +#define BLAS_SCALAR(x) x +#else +#define BLAS_SCALAR(x) &(x) +#endif + +#endif /* COMPLEX */ + + +/* -------------------------------------------------------------------------- */ +/* BLAS integer scalars */ +/* -------------------------------------------------------------------------- */ + +/* + Fortran requires integers to be passed by reference. + The SCSL BLAS requires long long arguments in LP64 mode. +*/ + +#if defined (USE_SCSL_BLAS) && defined (LP64) +#define BLAS_INT_SCALAR(n) ((long long) n) +#else +#ifdef BLAS_BY_VALUE +#define BLAS_INT_SCALAR(n) n +#else +#define BLAS_INT_SCALAR(n) &(n) +#endif +#endif + + +/* -------------------------------------------------------------------------- */ +/* BLAS strings */ +/* -------------------------------------------------------------------------- */ + +/* + The Sun Performance BLAS wants a character instead of a string. +*/ + +#ifdef BLAS_CHAR_ARG +#define BLAS_NO_TRANSPOSE 'N' +#define BLAS_TRANSPOSE 'T' +#define BLAS_LEFT 'L' +#define BLAS_RIGHT 'R' +#define BLAS_LOWER 'L' +#define BLAS_UNIT_DIAGONAL 'U' +#else +#define BLAS_NO_TRANSPOSE "N" +#define BLAS_TRANSPOSE "T" +#define BLAS_LEFT "L" +#define BLAS_RIGHT "R" +#define BLAS_LOWER "L" +#define BLAS_UNIT_DIAGONAL "U" +#endif + + +/* -------------------------------------------------------------------------- */ +/* BLAS arrays */ +/* -------------------------------------------------------------------------- */ + +/* + The complex SunPerf BLAS expects to see a doublecomplex array of size s. + This is broken (see above, regarding complex scalars in sunperf.h). + For SunPerf BLAS, just pass a pointer to the array, and ignore sunperf.h. + With sunperf.h, you would need: + + #define BLAS_ARRAY(a) ((doublecomplex *)(a)) + + SGI's SCSL BLAS has yet a different kind of struct, but we can use a + double array of size 2 instead (since SCSL_VOID_ARGS is defined). + + The real versions all use just a (double *) pointer. + + In all cases, no typecast is required. This will break if is + included. + + If you have read this far, I hope you see now why (void *) a much better + choice for complex BLAS prototypes, and why double x [2] is better than + an architecture dependent struct { double real ; double imag ; } + type definition. + +*/ + +#define BLAS_ARRAY(a) (a) + + +/* -------------------------------------------------------------------------- */ +#endif /* USE_C_BLAS } */ +/* -------------------------------------------------------------------------- */ + + + + + +/* -------------------------------------------------------------------------- */ +/* BLAS macros, for all interfaces */ +/* -------------------------------------------------------------------------- */ + +/* + All architecture dependent issues have now been taken into consideration, + and folded into the macros BLAS_DECLARE_SCALAR, BLAS_ASSIGN, BLAS_*_ROUTINE, + BLAS_COLUMN_MAJOR_ORDER, BLAS_NO_TRANSPOSE, BLAS_TRANSPOSE, BLAS_SCALAR, + BLAS_INT_SCALAR, BLAS_ARRAY, and Int. + + You will note that there is not a *** single *** name, declaration, or + argument to the BLAS which is not somehow different in one or more versions + of the BLAS! +*/ + + +/* C = C - A*B', where: + * A is m-by-k with leading dimension ldac + * B is k-by-n with leading dimension ldb + * C is m-by-n with leading dimension ldac */ +#define BLAS_GEMM(m,n,k,A,B,ldb,C,ldac) \ +{ \ + BLAS_DECLARE_SCALAR (alpha) ; \ + BLAS_DECLARE_SCALAR (beta) ; \ + BLAS_ASSIGN (alpha, -1.0, 0.0) ; \ + BLAS_ASSIGN (beta, 1.0, 0.0) ; \ + (void) BLAS_GEMM_ROUTINE (BLAS_COLUMN_MAJOR_ORDER \ + BLAS_NO_TRANSPOSE, BLAS_TRANSPOSE, \ + BLAS_INT_SCALAR (m), BLAS_INT_SCALAR (n), BLAS_INT_SCALAR (k), \ + BLAS_SCALAR (alpha), \ + BLAS_ARRAY (A), BLAS_INT_SCALAR (ldac), \ + BLAS_ARRAY (B), BLAS_INT_SCALAR (ldb), BLAS_SCALAR (beta), \ + BLAS_ARRAY (C), BLAS_INT_SCALAR (ldac)) ; \ +} + +/* A = A - x*y', where: + * A is m-by-n with leading dimension d + x is a column vector with stride 1 + y is a column vector with stride 1 */ +#define BLAS_GER(m,n,x,y,A,d) \ +{ \ + Int one = 1 ; \ + BLAS_DECLARE_SCALAR (alpha) ; \ + BLAS_ASSIGN (alpha, -1.0, 0.0) ; \ + (void) BLAS_GER_ROUTINE (BLAS_COLUMN_MAJOR_ORDER \ + BLAS_INT_SCALAR (m), BLAS_INT_SCALAR (n), \ + BLAS_SCALAR (alpha), \ + BLAS_ARRAY (x), BLAS_INT_SCALAR (one), \ + BLAS_ARRAY (y), BLAS_INT_SCALAR (one), \ + BLAS_ARRAY (A), BLAS_INT_SCALAR (d)) ; \ +} + +/* y = y - A*x, where A is m-by-n with leading dimension d, + x is a column vector with stride 1 + y is a column vector with stride 1 */ +#define BLAS_GEMV(m,n,A,x,y,d) \ +{ \ + Int one = 1 ; \ + BLAS_DECLARE_SCALAR (alpha) ; \ + BLAS_DECLARE_SCALAR (beta) ; \ + BLAS_ASSIGN (alpha, -1.0, 0.0) ; \ + BLAS_ASSIGN (beta, 1.0, 0.0) ; \ + (void) BLAS_GEMV_ROUTINE (BLAS_COLUMN_MAJOR_ORDER \ + BLAS_NO_TRANSPOSE, \ + BLAS_INT_SCALAR (m), BLAS_INT_SCALAR (n), \ + BLAS_SCALAR (alpha), \ + BLAS_ARRAY (A), BLAS_INT_SCALAR (d), \ + BLAS_ARRAY (x), BLAS_INT_SCALAR (one), BLAS_SCALAR (beta), \ + BLAS_ARRAY (y), BLAS_INT_SCALAR (one)) ; \ +} + + +/* solve Lx=b, where: + * B is a column vector (m-by-1) with leading dimension d + * A is m-by-m with leading dimension d */ +#define BLAS_TRSV(m,A,b,d) \ +{ \ + Int one = 1 ; \ + (void) BLAS_TRSV_ROUTINE (BLAS_COLUMN_MAJOR_ORDER \ + BLAS_LOWER, BLAS_NO_TRANSPOSE, BLAS_UNIT_DIAGONAL, \ + BLAS_INT_SCALAR (m), \ + BLAS_ARRAY (A), BLAS_INT_SCALAR (d), \ + BLAS_ARRAY (b), BLAS_INT_SCALAR (one)) ; \ +} + +/* solve XL'=B where: + * B is m-by-n with leading dimension ldb + * A is n-by-n with leading dimension lda */ +#define BLAS_TRSM_RIGHT(m,n,A,lda,B,ldb) \ +{ \ + BLAS_DECLARE_SCALAR (alpha) ; \ + BLAS_ASSIGN (alpha, 1.0, 0.0) ; \ + (void) BLAS_TRSM_ROUTINE (BLAS_COLUMN_MAJOR_ORDER \ + BLAS_RIGHT, BLAS_LOWER, BLAS_TRANSPOSE, BLAS_UNIT_DIAGONAL, \ + BLAS_INT_SCALAR (m), BLAS_INT_SCALAR (n), \ + BLAS_SCALAR (alpha), \ + BLAS_ARRAY (A), BLAS_INT_SCALAR (lda), \ + BLAS_ARRAY (B), BLAS_INT_SCALAR (ldb)) ; \ +} + +/* x = s*x, where x is a stride-1 vector of length n */ +#define BLAS_SCAL(n,s,x) \ +{ \ + Int one = 1 ; \ + BLAS_DECLARE_SCALAR (alpha) ; \ + BLAS_ASSIGN (alpha, REAL_COMPONENT (s), IMAG_COMPONENT (s)) ; \ + (void) BLAS_SCAL_ROUTINE ( \ + BLAS_INT_SCALAR (n), BLAS_SCALAR (alpha), \ + BLAS_ARRAY (x), BLAS_INT_SCALAR (one)) ; \ +} + +/* x = y, where x and y are a stride-1 vectors of length n */ +#define BLAS_COPY(n,x,y) \ +{ \ + Int one = 1 ; \ + (void) BLAS_COPY_ROUTINE ( \ + BLAS_INT_SCALAR (n), \ + BLAS_ARRAY (x), BLAS_INT_SCALAR (one), \ + BLAS_ARRAY (y), BLAS_INT_SCALAR (one)) ; \ +} + +#endif /* !defined (USE_NO_BLAS) } */ diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_create_element.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_create_element.c new file mode 100644 index 0000000000..48b1d5907c --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_create_element.c @@ -0,0 +1,605 @@ +/* ========================================================================== */ +/* === UMF_create_element =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Factorization of a frontal matrix is complete. Create a new element for + later assembly into a subsequent frontal matrix. Returns TRUE if + successful, FALSE if out of memory. +*/ + +#include "umf_internal.h" +#include "umf_mem_alloc_element.h" +#include "umf_mem_alloc_tail_block.h" +#include "umf_mem_free_tail_block.h" +#include "umf_get_memory.h" + +/* ========================================================================== */ +/* === copy_column ========================================================== */ +/* ========================================================================== */ + +PRIVATE void copy_column (Int len, Entry *X, Entry *Y) +{ + Int i ; +#pragma ivdep + for (i = 0 ; i < len ; i++) + { + Y [i] = X [i] ; + } +} + +/* ========================================================================== */ +/* === UMF_create_element =================================================== */ +/* ========================================================================== */ + +GLOBAL Int UMF_create_element +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int j, col, row, *Fcols, *Frows, fnrows, fncols, *Cols, len, needunits, t1, + t2, size, e, i, *E, *Fcpos, *Frpos, *Rows, eloc, fnr_curr, f, + got_memory, *Row_tuples, *Row_degree, *Row_tlen, *Col_tuples, max_mark, + *Col_degree, *Col_tlen, nn, n_row, n_col, r2, c2, do_Fcpos ; + Entry *C, *Fcol ; + Element *ep ; + Unit *p, *Memory ; + Tuple *tp, *tp1, *tp2, tuple, *tpend ; +#ifndef NDEBUG + DEBUG2 (("FRONTAL WRAPUP\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + ASSERT (Work->fnpiv == 0) ; + ASSERT (Work->fnzeros == 0) ; + Row_degree = Numeric->Rperm ; + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Col_degree = Numeric->Cperm ; + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + n_row = Work->n_row ; + n_col = Work->n_col ; + nn = MAX (n_row, n_col) ; + Fcols = Work->Fcols ; + Frows = Work->Frows ; + Fcpos = Work->Fcpos ; + Frpos = Work->Frpos ; + Memory = Numeric->Memory ; + fncols = Work->fncols ; + fnrows = Work->fnrows ; + + tp = (Tuple *) NULL ; + tp1 = (Tuple *) NULL ; + tp2 = (Tuple *) NULL ; + + /* ---------------------------------------------------------------------- */ + /* add the current frontal matrix to the degrees of each column */ + /* ---------------------------------------------------------------------- */ + + if (!Symbolic->fixQ) + { + /* but only if the column ordering is not fixed */ +#pragma ivdep + for (j = 0 ; j < fncols ; j++) + { + /* add the current frontal matrix to the degree */ + ASSERT (Fcols [j] >= 0 && Fcols [j] < n_col) ; + Col_degree [Fcols [j]] += fnrows ; + } + } + + /* ---------------------------------------------------------------------- */ + /* add the current frontal matrix to the degrees of each row */ + /* ---------------------------------------------------------------------- */ + +#pragma ivdep + for (i = 0 ; i < fnrows ; i++) + { + /* add the current frontal matrix to the degree */ + ASSERT (Frows [i] >= 0 && Frows [i] < n_row) ; + Row_degree [Frows [i]] += fncols ; + } + + /* ---------------------------------------------------------------------- */ + /* Reset the external degree counters */ + /* ---------------------------------------------------------------------- */ + + E = Work->E ; + max_mark = MAX_MARK (nn) ; + + if (!Work->pivcol_in_front) + { + /* clear the external column degrees. no more Usons of current front */ + Work->cdeg0 += (nn + 1) ; + if (Work->cdeg0 >= max_mark) + { + /* guard against integer overflow. This is very rare */ + DEBUG1 (("Integer overflow, cdeg\n")) ; + Work->cdeg0 = 1 ; +#pragma ivdep + for (e = 1 ; e <= Work->nel ; e++) + { + if (E [e]) + { + ep = (Element *) (Memory + E [e]) ; + ep->cdeg = 0 ; + } + } + } + } + + if (!Work->pivrow_in_front) + { + /* clear the external row degrees. no more Lsons of current front */ + Work->rdeg0 += (nn + 1) ; + if (Work->rdeg0 >= max_mark) + { + /* guard against integer overflow. This is very rare */ + DEBUG1 (("Integer overflow, rdeg\n")) ; + Work->rdeg0 = 1 ; +#pragma ivdep + for (e = 1 ; e <= Work->nel ; e++) + { + if (E [e]) + { + ep = (Element *) (Memory + E [e]) ; + ep->rdeg = 0 ; + } + } + } + } + + /* ---------------------------------------------------------------------- */ + /* clear row/col offsets */ + /* ---------------------------------------------------------------------- */ + + if (!Work->pivrow_in_front) + { +#pragma ivdep + for (j = 0 ; j < fncols ; j++) + { + Fcpos [Fcols [j]] = EMPTY ; + } + } + + if (!Work->pivcol_in_front) + { +#pragma ivdep + for (i = 0 ; i < fnrows ; i++) + { + Frpos [Frows [i]] = EMPTY ; + } + } + + if (fncols <= 0 || fnrows <= 0) + { + /* no element to create */ + DEBUG2 (("Element evaporation\n")) ; + Work->prior_element = EMPTY ; + return (TRUE) ; + } + + /* ---------------------------------------------------------------------- */ + /* create element for later assembly */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) + { + double rrr = ((double) (rand ( ))) / (((double) RAND_MAX) + 1) ; + DEBUG4 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; + if (UMF_allocfail) DEBUGm2 (("Random garbage collection (create)\n")); + } +#endif + + needunits = 0 ; + got_memory = FALSE ; + eloc = UMF_mem_alloc_element (Numeric, fnrows, fncols, &Rows, &Cols, &C, + &needunits, &ep) ; + + /* if UMF_get_memory needs to be called */ + if (Work->do_grow) + { + /* full compaction of current frontal matrix, since UMF_grow_front will + * be called next anyway. */ + r2 = fnrows ; + c2 = fncols ; + do_Fcpos = FALSE ; + } + else + { + /* partial compaction. */ + r2 = MAX (fnrows, Work->fnrows_new + 1) ; + c2 = MAX (fncols, Work->fncols_new + 1) ; + /* recompute Fcpos if pivot row is in the front */ + do_Fcpos = Work->pivrow_in_front ; + } + + if (!eloc) + { + /* Do garbage collection, realloc, and try again. */ + /* Compact the current front if it needs to grow anyway. */ + /* Note that there are no pivot rows or columns in the current front */ + DEBUGm3 (("get_memory from umf_create_element, 1\n")) ; + if (!UMF_get_memory (Numeric, Work, needunits, r2, c2, do_Fcpos)) + { + /* :: out of memory in umf_create_element (1) :: */ + DEBUGm4 (("out of memory: create element (1)\n")) ; + return (FALSE) ; /* out of memory */ + } + got_memory = TRUE ; + Memory = Numeric->Memory ; + eloc = UMF_mem_alloc_element (Numeric, fnrows, fncols, &Rows, &Cols, &C, + &needunits, &ep) ; + ASSERT (eloc >= 0) ; + if (!eloc) + { + /* :: out of memory in umf_create_element (2) :: */ + DEBUGm4 (("out of memory: create element (2)\n")) ; + return (FALSE) ; /* out of memory */ + } + } + + e = ++(Work->nel) ; /* get the name of this new frontal matrix */ + Work->prior_element = e ; + DEBUG8 (("wrapup e "ID" nel "ID"\n", e, Work->nel)) ; + + ASSERT (e > 0 && e < Work->elen) ; + ASSERT (E [e] == 0) ; + E [e] = eloc ; + + if (Work->pivcol_in_front) + { + /* the new element is a Uson of the next frontal matrix */ + ep->cdeg = Work->cdeg0 ; + } + + if (Work->pivrow_in_front) + { + /* the new element is an Lson of the next frontal matrix */ + ep->rdeg = Work->rdeg0 ; + } + + /* ---------------------------------------------------------------------- */ + /* copy frontal matrix into the new element */ + /* ---------------------------------------------------------------------- */ + +#pragma ivdep + for (i = 0 ; i < fnrows ; i++) + { + Rows [i] = Frows [i] ; + } +#pragma ivdep + for (i = 0 ; i < fncols ; i++) + { + Cols [i] = Fcols [i] ; + } + Fcol = Work->Fcblock ; + DEBUG0 (("copy front "ID" by "ID"\n", fnrows, fncols)) ; + fnr_curr = Work->fnr_curr ; + ASSERT (fnr_curr >= 0 && fnr_curr % 2 == 1) ; + for (j = 0 ; j < fncols ; j++) + { + copy_column (fnrows, Fcol, C) ; +#if 0 +#ifdef USE_NO_BLAS + copy_column (fnrows, Fcol, C) ; +#else + could also use BLAS-COPY (fnrows, Fcol, C) here, but it is typically + not as fast as the inlined copy_column subroutine, above. +#endif + for (i = 0 ; i < fnrows ; i++) + { + C [i] = Fcol [i] ; + } +#endif + Fcol += fnr_curr ; + C += fnrows ; + } + + DEBUG8 (("element copied\n")) ; + + /* ---------------------------------------------------------------------- */ + /* add tuples for the new element */ + /* ---------------------------------------------------------------------- */ + + tuple.e = e ; + + if (got_memory) + { + + /* ------------------------------------------------------------------ */ + /* UMF_get_memory ensures enough space exists for each new tuple */ + /* ------------------------------------------------------------------ */ + + /* place (e,f) in the element list of each column */ + for (tuple.f = 0 ; tuple.f < fncols ; tuple.f++) + { + col = Fcols [tuple.f] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + ASSERT (Col_tuples [col]) ; + tp = ((Tuple *) (Memory + Col_tuples [col])) + Col_tlen [col]++ ; + *tp = tuple ; + } + + /* ------------------------------------------------------------------ */ + + /* place (e,f) in the element list of each row */ + for (tuple.f = 0 ; tuple.f < fnrows ; tuple.f++) + { + row = Frows [tuple.f] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + ASSERT (Row_tuples [row]) ; + tp = ((Tuple *) (Memory + Row_tuples [row])) + Row_tlen [row]++ ; + *tp = tuple ; + } + + } + else + { + + /* ------------------------------------------------------------------ */ + /* place (e,f) in the element list of each column */ + /* ------------------------------------------------------------------ */ + + /* might not have enough space for each tuple */ + + for (tuple.f = 0 ; tuple.f < fncols ; tuple.f++) + { + col = Fcols [tuple.f] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + t1 = Col_tuples [col] ; + DEBUG1 (("Placing on col:"ID" , tuples at "ID"\n", + col, Col_tuples [col])) ; + + size = 0 ; + len = 0 ; + + if (t1) + { + p = Memory + t1 ; + tp = (Tuple *) p ; + size = GET_BLOCK_SIZE (p) ; + len = Col_tlen [col] ; + tp2 = tp + len ; + } + + needunits = UNITS (Tuple, len + 1) ; + DEBUG1 (("len: "ID" size: "ID" needunits: "ID"\n", + len, size, needunits)); + + if (needunits > size && t1) + { + /* prune the tuples */ + tp1 = tp ; + tp2 = tp ; + tpend = tp + len ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + ; + if (Cols [f] == EMPTY) continue ; /* already assembled */ + ASSERT (col == Cols [f]) ; + *tp2++ = *tp ; /* leave the tuple in the list */ + } + len = tp2 - tp1 ; + Col_tlen [col] = len ; + needunits = UNITS (Tuple, len + 1) ; + } + + if (needunits > size) + { + /* no room exists - reallocate elsewhere */ + DEBUG1 (("REALLOCATE Col: "ID", size "ID" to "ID"\n", + col, size, 2*needunits)) ; + +#ifndef NDEBUG + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) /* a double relop, but ignore NaN case */ + { + double rrr = ((double) (rand ( ))) / + (((double) RAND_MAX) + 1) ; + DEBUG1 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; + if (UMF_allocfail) DEBUGm2 (("Random gar. (col tuple)\n")) ; + } +#endif + + needunits = MIN (2*needunits, (Int) UNITS (Tuple, nn)) ; + t2 = UMF_mem_alloc_tail_block (Numeric, needunits) ; + if (!t2) + { + /* :: get memory in umf_create_element (1) :: */ + /* get memory, reconstruct all tuple lists, and return */ + /* Compact the current front if it needs to grow anyway. */ + /* Note: no pivot rows or columns in the current front */ + DEBUGm4 (("get_memory from umf_create_element, 1\n")) ; + return (UMF_get_memory (Numeric, Work, 0, r2, c2,do_Fcpos)); + } + Col_tuples [col] = t2 ; + tp2 = (Tuple *) (Memory + t2) ; + if (t1) + { + for (i = 0 ; i < len ; i++) + { + *tp2++ = *tp1++ ; + } + UMF_mem_free_tail_block (Numeric, t1) ; + } + } + + /* place the new (e,f) tuple in the element list of the column */ + Col_tlen [col]++ ; + *tp2 = tuple ; + } + + /* ------------------------------------------------------------------ */ + /* place (e,f) in the element list of each row */ + /* ------------------------------------------------------------------ */ + + for (tuple.f = 0 ; tuple.f < fnrows ; tuple.f++) + { + row = Frows [tuple.f] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + t1 = Row_tuples [row] ; + DEBUG1 (("Placing on row:"ID" , tuples at "ID"\n", + row, Row_tuples [row])) ; + + size = 0 ; + len = 0 ; + if (t1) + { + p = Memory + t1 ; + tp = (Tuple *) p ; + size = GET_BLOCK_SIZE (p) ; + len = Row_tlen [row] ; + tp2 = tp + len ; + } + + needunits = UNITS (Tuple, len + 1) ; + DEBUG1 (("len: "ID" size: "ID" needunits: "ID"\n", + len, size, needunits)) ; + + if (needunits > size && t1) + { + /* prune the tuples */ + tp1 = tp ; + tp2 = tp ; + tpend = tp + len ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) + { + continue ; /* element already deallocated */ + } + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + Rows = Cols + (ep->ncols) ; + if (Rows [f] == EMPTY) continue ; /* already assembled */ + ASSERT (row == Rows [f]) ; + *tp2++ = *tp ; /* leave the tuple in the list */ + } + len = tp2 - tp1 ; + Row_tlen [row] = len ; + needunits = UNITS (Tuple, len + 1) ; + } + + if (needunits > size) + { + /* no room exists - reallocate elsewhere */ + DEBUG1 (("REALLOCATE Row: "ID", size "ID" to "ID"\n", + row, size, 2*needunits)) ; + +#ifndef NDEBUG + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) /* a double relop, but ignore NaN case */ + { + double rrr = ((double) (rand ( ))) / + (((double) RAND_MAX) + 1) ; + DEBUG1 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; + if (UMF_allocfail) DEBUGm2 (("Random gar. (row tuple)\n")) ; + } +#endif + + needunits = MIN (2*needunits, (Int) UNITS (Tuple, nn)) ; + t2 = UMF_mem_alloc_tail_block (Numeric, needunits) ; + if (!t2) + { + /* :: get memory in umf_create_element (2) :: */ + /* get memory, reconstruct all tuple lists, and return */ + /* Compact the current front if it needs to grow anyway. */ + /* Note: no pivot rows or columns in the current front */ + DEBUGm4 (("get_memory from umf_create_element, 2\n")) ; + return (UMF_get_memory (Numeric, Work, 0, r2, c2,do_Fcpos)); + } + Row_tuples [row] = t2 ; + tp2 = (Tuple *) (Memory + t2) ; + if (t1) + { + for (i = 0 ; i < len ; i++) + { + *tp2++ = *tp1++ ; + } + UMF_mem_free_tail_block (Numeric, t1) ; + } + } + + /* place the new (e,f) tuple in the element list of the row */ + Row_tlen [row]++ ; + *tp2 = tuple ; + } + + } + + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG1 (("Done extending\nFINAL: element row pattern: len="ID"\n", fncols)); + for (j = 0 ; j < fncols ; j++) DEBUG1 ((""ID"\n", Fcols [j])) ; + DEBUG1 (("FINAL: element col pattern: len="ID"\n", fnrows)) ; + for (j = 0 ; j < fnrows ; j++) DEBUG1 ((""ID"\n", Frows [j])) ; + for (j = 0 ; j < fncols ; j++) + { + col = Fcols [j] ; + ASSERT (col >= 0 && col < n_col) ; + UMF_dump_rowcol (1, Numeric, Work, col, !Symbolic->fixQ) ; + } + for (j = 0 ; j < fnrows ; j++) + { + row = Frows [j] ; + ASSERT (row >= 0 && row < n_row) ; + UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ; + } + if (n_row < 1000 && n_col < 1000) + { + UMF_dump_memory (Numeric) ; + } + DEBUG1 (("New element, after filling with stuff: "ID"\n", e)) ; + UMF_dump_element (Numeric, Work, e, TRUE) ; + if (nn < 1000) + { + DEBUG4 (("Matrix dump, after New element: "ID"\n", e)) ; + UMF_dump_matrix (Numeric, Work, TRUE) ; + } + DEBUG3 (("FRONTAL WRAPUP DONE\n")) ; +#endif + + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_create_element.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_create_element.h new file mode 100644 index 0000000000..81df97f4d3 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_create_element.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_create_element +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_dump.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_dump.c new file mode 100644 index 0000000000..9990c2c49b --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_dump.c @@ -0,0 +1,1220 @@ +/* ========================================================================== */ +/* === UMF_dump ============================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* These routines, and external variables, are used only when debugging. */ +/* If debugging is disabled (for normal operation) then this entire file */ +/* becomes empty */ + +#include "umf_internal.h" + +#ifndef NDEBUG + +/* These global debugging variables and arrays do not exist if debugging */ +/* is disabled at compile time (which is the default). */ +GLOBAL Int UMF_debug = -999 ; +GLOBAL Int UMF_allocfail = FALSE ; +GLOBAL double UMF_gprob = -1.0 ; + +/* static debugging arrays used only in UMF_dump_rowcol */ +PRIVATE Int UMF_DBflag = 0 ; +PRIVATE Int UMF_DBpacked [UMF_DBMAX+1] ; +PRIVATE Int UMF_DBscatter [UMF_DBMAX+1] ; + +/* ========================================================================== */ +/* === UMF_DBinit =========================================================== */ +/* ========================================================================== */ + +/* clear the debugging arrays */ + +PRIVATE void UMF_DBinit +( + void +) +{ + Int i ; + + /* Int_MAX is defined in umfpack.h */ + if (UMF_DBflag < 1 || UMF_DBflag == Int_MAX) + { + /* clear the debugging arrays */ + UMF_DBflag = 0 ; + for (i = 0 ; i <= UMF_DBMAX ; i++) + { + UMF_DBscatter [i] = 0 ; + UMF_DBpacked [i] = 0 ; + } + } + + UMF_DBflag++ ; + + /* UMF_DBflag > UMF_DBscatter [0...UMF_DBmax] is now true */ +} + +/* ========================================================================== */ +/* === UMF_dump_dense ======================================================= */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_dense +( + Entry *C, + Int dim, + Int m, + Int n +) +{ + + /* dump C [1..m,1..n], with column dimenstion dim */ + Int i, j; + + if (UMF_debug < 7) return ; + if (C == (Entry *) NULL) + { + DEBUG7 (("No dense matrix allocated\n")) ; + return ; + } + DEBUG8 ((" dimension= "ID" rows= "ID" cols= "ID"\n", dim, m, n)) ; + + for (i = 0 ; i < m ; i++) + { + DEBUG9 ((ID": ", i)) ; + for (j = 0 ; j < n ; j++) + { + EDEBUG9 (C [i+j*dim]) ; + if (j % 6 == 5) DEBUG9 (("\n ")) ; + } + DEBUG9 (("\n")) ; + } + + for (i = 0 ; i < m ; i++) + { + for (j = 0 ; j < n ; j++) + { + if (IS_ZERO (C [i+j*dim])) + { + DEBUG8 ((".")) ; + } + else + { + DEBUG8 (("X")) ; + } + } + DEBUG8 (("\n")) ; + } +} + +/* ========================================================================== */ +/* === UMF_dump_element ===================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_element +( + NumericType *Numeric, + WorkType *Work, + Int e, + Int clean +) +{ + + Int i, j, k, *Rows, *Cols, nrows, ncols, *E, row, col, + *Row_degree, *Col_degree ; + Entry *C ; + Element *ep ; + Unit *p ; + + if (UMF_debug < 7) return ; + + if (e == 0) + { + UMF_dump_current_front (Numeric, Work, FALSE) ; + return ; + } + + DEBUG7 (("\n====================ELEMENT: "ID" ", e)) ; + if (!Numeric || !Work || !Numeric->Memory) + { + DEBUG7 ((" No Numeric, Work\n")) ; + return ; + } + DEBUG7 ((" nel: "ID" of "ID, e, Work->nel)) ; + E = Work->E ; + if (!E) + { + DEBUG7 ((" No elements\n")) ; + return ; + } + if (e < 0 || e > Work->nel) + { + DEBUG7 (("e out of range!\n")) ; + return ; + } + if (!E [e]) + { + DEBUG7 ((" deallocated\n")) ; + return ; + } + DEBUG7 (("\n")) ; + Col_degree = Numeric->Cperm ; + Row_degree = Numeric->Rperm ; + + p = Numeric->Memory + E [e] ; + DEBUG7 (("ep "ID"\n", (Int) (p-Numeric->Memory))) ; + GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ; + DEBUG7 (("nrows "ID" nrowsleft "ID"\n", nrows, ep->nrowsleft)) ; + DEBUG7 (("ncols "ID" ncolsleft "ID"\n", ncols, ep->ncolsleft)) ; + DEBUG7 (("cdeg-cdeg0 "ID" rdeg-rdeg0 "ID" next "ID"\n", + ep->cdeg - Work->cdeg0, ep->rdeg - Work->rdeg0, ep->next)) ; + + DEBUG8 (("rows: ")) ; + k = 0 ; + for (i = 0 ; i < ep->nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + DEBUG8 ((" "ID, row)) ; + ASSERT (row < Work->n_row) ; + if ((k++ % 10) == 9) DEBUG8 (("\n")) ; + ASSERT (IMPLIES (clean, NON_PIVOTAL_ROW (row))) ; + } + } + + DEBUG8 (("\ncols: ")) ; + k = 0 ; + for (j = 0 ; j < ep->ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { + DEBUG8 ((" "ID, col)) ; + ASSERT (col < Work->n_col) ; + if ((k++ % 10) == 9) DEBUG8 (("\n")) ; + ASSERT (IMPLIES (clean, NON_PIVOTAL_COL (col))) ; + } + } + + DEBUG8 (("\nvalues:\n")) ; + if (UMF_debug >= 9) + { + for (i = 0 ; i < ep->nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + DEBUG9 ((ID": ", row)) ; + k = 0 ; + for (j = 0 ; j < ep->ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { + EDEBUG9 (C [i+j*ep->nrows]) ; + if (k++ % 6 == 5) DEBUG9 (("\n ")) ; + } + } + DEBUG9 (("\n")) ; + } + } + } + + DEBUG7 (("====================\n")) ; +} + + +/* ========================================================================== */ +/* === UMF_dump_rowcol ====================================================== */ +/* ========================================================================== */ + +/* dump a row or a column, from one or more memory spaces */ +/* return exact degree */ + +GLOBAL void UMF_dump_rowcol +( + Int dumpwhich, /* 0 for row, 1 for column */ + NumericType *Numeric, + WorkType *Work, + Int dumpindex, /* row or column index to dump */ + Int check_degree /* true if degree is to be checked */ +) +{ + Int f, nrows, j, jj, len, e, deg, index, n_row, n_col, *Cols, *Rows, nn, + dumpdeg, ncols, preve, *E, tpi, *Pattern, approx_deg, not_in_use ; + Tuple *tp, *tend ; + Element *ep ; + Int *Row_tuples, *Row_degree, *Row_tlen ; + Int *Col_tuples, *Col_degree, *Col_tlen ; + Entry value, *C ; + Unit *p ; + Int is_there ; + + /* clear the debugging arrays */ + UMF_DBinit () ; + + if (dumpwhich == 0) + { + DEBUG7 (("\n====================ROW: "ID, dumpindex)) ; + } + else + { + DEBUG7 (("\n====================COL: "ID, dumpindex)) ; + } + + if (dumpindex == EMPTY) + { + DEBUG7 ((" (EMPTY)\n")) ; + return ; + } + + deg = 0 ; + approx_deg = 0 ; + + if (!Numeric || !Work) + { + DEBUG7 ((" No Numeric, Work\n")) ; + return ; + } + + n_row = Work->n_row ; + n_col = Work->n_col ; + nn = MAX (n_row, n_col) ; + E = Work->E ; + + Col_degree = Numeric->Cperm ; + Row_degree = Numeric->Rperm ; + + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + + if (!E + || !Row_tuples || !Row_degree || !Row_tlen + || !Col_tuples || !Col_degree || !Col_tlen) + { + DEBUG7 ((" No E, Rows, Cols\n")) ; + return ; + } + + if (dumpwhich == 0) + { + /* dump a row */ + ASSERT (dumpindex >= 0 && dumpindex < n_row) ; + if (!NON_PIVOTAL_ROW (dumpindex)) + { + DEBUG7 ((" Pivotal\n")) ; + return ; + } + len = Row_tlen [dumpindex] ; + dumpdeg = Row_degree [dumpindex] ; + tpi = Row_tuples [dumpindex] ; + } + else + { + /* dump a column */ + ASSERT (dumpindex >= 0 && dumpindex < n_col) ; + if (!NON_PIVOTAL_COL (dumpindex)) + { + DEBUG7 ((" Pivotal\n")) ; + return ; + } + len = Col_tlen [dumpindex] ; + dumpdeg = Col_degree [dumpindex] ; + tpi = Col_tuples [dumpindex] ; + } + + p = Numeric->Memory + tpi ; + tp = (Tuple *) p ; + if (!tpi) + { + DEBUG7 ((" Nonpivotal, No tuple list tuples "ID" tlen "ID"\n", + tpi, len)) ; + return ; + } + ASSERT (p >= Numeric->Memory + Numeric->itail) ; + ASSERT (p < Numeric->Memory + Numeric->size) ; + + DEBUG7 ((" degree: "ID" len: "ID"\n", dumpdeg, len)) ; + not_in_use = (p-1)->header.size - UNITS (Tuple, len) ; + DEBUG7 ((" Tuple list: p+1: "ID" size: "ID" units, "ID" not in use\n", + (Int) (p-Numeric->Memory), (p-1)->header.size, not_in_use)) ; + ASSERT (not_in_use >= 0) ; + tend = tp + len ; + preve = 0 ; + for ( ; tp < tend ; tp++) + { + /* row/col of element e, offset is f: */ + /* DEBUG8 ((" (tp="ID")\n", tp)) ; */ + e = tp->e ; + f = tp->f ; + DEBUG8 ((" (e="ID", f="ID")\n", e, f)) ; + ASSERT (e > 0 && e <= Work->nel) ; + /* dump the pattern and values */ + if (E [e]) + { + p = Numeric->Memory + E [e] ; + GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ; + if (dumpwhich == 0) + { + Pattern = Cols ; + jj = ep->ncols ; + is_there = Rows [f] >= 0 ; + if (is_there) approx_deg += ep->ncolsleft ; + } + else + { + Pattern = Rows ; + jj = ep->nrows ; + is_there = Cols [f] >= 0 ; + if (is_there) approx_deg += ep->nrowsleft ; + } + if (!is_there) + { + DEBUG8 (("\t\tnot present\n")) ; + } + else + { + for (j = 0 ; j < jj ; j++) + { + index = Pattern [j] ; + value = + C [ (dumpwhich == 0) ? (f+nrows*j) : (j+nrows*f) ] ; + if (index >= 0) + { + DEBUG8 (("\t\t"ID":", index)) ; + EDEBUG8 (value) ; + DEBUG8 (("\n")) ; + if (dumpwhich == 0) + { + /* col must be in the range 0..n_col-1 */ + ASSERT (index < n_col) ; + } + else + { + /* row must be in the range 0..n_row-1 */ + ASSERT (index < n_row) ; + } + + if (nn <= UMF_DBMAX) + { + if (UMF_DBscatter [index] != UMF_DBflag) + { + UMF_DBpacked [deg++] = index ; + UMF_DBscatter [index] = UMF_DBflag ; + } + } + } + } + } + /* the (e,f) tuples should be in order of their creation */ + /* this means that garbage collection will not jumble them */ + ASSERT (preve < e) ; + preve = e ; + } + else + { + DEBUG8 (("\t\tdeallocated\n")) ; + } + } + + if (nn <= UMF_DBMAX) + { + if (deg > 0) + { + DEBUG7 ((" Assembled, actual deg: "ID" : ", deg)) ; + for (j = 0 ; j < deg ; j++) + { + index = UMF_DBpacked [j] ; + DEBUG8 ((ID" ", index)) ; + if (j % 20 == 19) DEBUG8 (("\n ")) ; + ASSERT (UMF_DBscatter [index] == UMF_DBflag) ; + } + DEBUG7 (("\n")) ; + } + } + + /* Col_degree is not maintained when fixQ is true */ + if (check_degree) + { + DEBUG8 ((" approx_deg "ID" dumpdeg "ID"\n", approx_deg, dumpdeg)) ; + ASSERT (approx_deg == dumpdeg) ; + } + + DEBUG7 (("====================\n")) ; + + /* deg is now the exact degree */ + /* if nn <= UMF_DBMAX, then UMF_DBscatter [i] == UMF_DBflag for every i */ + /* in the row or col, and != UMF_DBflag if not */ + + return ; +} + + +/* ========================================================================== */ +/* === UMF_dump_matrix ====================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_matrix +( + NumericType *Numeric, + WorkType *Work, + Int check_degree +) +{ + + Int e, row, col, intfrag, frag, n_row, n_col, *E, fullsize, actualsize ; + Element *ep ; + Unit *p ; + + DEBUG6 (("=================================================== MATRIX:\n")) ; + if (!Numeric || !Work) + { + DEBUG6 (("No Numeric or Work allocated\n")) ; + return ; + } + if (!Numeric->Memory) + { + DEBUG6 (("No Numeric->Memory\n")) ; + return ; + } + + n_row = Work->n_row ; + n_col = Work->n_col ; + DEBUG6 (("n_row "ID" n_col "ID" nz "ID"\n", n_row, n_col, Work->nz)) ; + DEBUG6 (("============================ ELEMENTS: "ID" \n", Work->nel)) ; + intfrag = 0 ; + E = Work->E ; + if (!E) + { + DEBUG6 (("No elements allocated\n")) ; + } + else + { + for (e = 0 ; e <= Work->nel ; e++) + { + UMF_dump_element (Numeric, Work, e, FALSE) ; + if (e > 0 && E [e]) + { + p = Numeric->Memory + E [e] ; + ep = (Element *) p ; + ASSERT (ep->nrowsleft > 0 || ep->ncolsleft > 0) ; + fullsize = GET_BLOCK_SIZE (p) ; + actualsize = GET_ELEMENT_SIZE (ep->nrowsleft,ep->ncolsleft); + frag = fullsize - actualsize ; + intfrag += frag ; + DEBUG7 (("dump el: "ID", full "ID" actual "ID" frag: "ID + " intfrag: "ID"\n", e, fullsize, actualsize, frag, + intfrag)) ; + } + } + } + + DEBUG6 (("CURRENT INTERNAL FRAG in elements: "ID" \n", intfrag)) ; + + + + DEBUG6 (("======================================== ROWS: "ID"\n", n_row)) ; + UMF_debug -= 2 ; + for (row = 0 ; row < n_row ; row++) + { + UMF_dump_rowcol (0, Numeric, Work, row, check_degree) ; + } + UMF_debug += 2 ; + DEBUG6 (("======================================== COLS: "ID"\n", n_col)) ; + UMF_debug -= 2 ; + for (col = 0 ; col < n_col ; col++) + { + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; + } + UMF_debug += 2 ; + DEBUG6 (("============================================= END OF MATRIX:\n")); +} + + +/* ========================================================================== */ +/* === UMF_dump_current_front =============================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_current_front +( + NumericType *Numeric, + WorkType *Work, + Int check +) +{ + + Entry *Flublock, *Flblock, *Fublock, *Fcblock ; + Int fnrows_max, fncols_max, fnrows, fncols, fnpiv, *Frows, *Fcols, + i, j, *Fcpos, *Frpos, fnr_curr, fnc_curr, *E ; + if (!Work) return ; + DEBUG7 (("\n\n========CURRENT FRONTAL MATRIX:\n")) ; + + Flublock = Work->Flublock ; + Flblock = Work->Flblock ; + Fublock = Work->Fublock ; + Fcblock = Work->Fcblock ; + + Frows = Work->Frows ; + Fcols = Work->Fcols ; + Frpos = Work->Frpos ; + Fcpos = Work->Fcpos ; + fnrows_max = Work->fnrows_max ; + fncols_max = Work->fncols_max ; + fnr_curr = Work->fnr_curr ; + fnc_curr = Work->fnc_curr ; + fnrows = Work->fnrows ; + fncols = Work->fncols ; + fnpiv = Work->fnpiv ; + E = Work->E ; + + DEBUG6 (("=== fnpiv= "ID"\n", fnpiv)) ; + DEBUG6 (("fnrows_max fncols_max "ID" "ID"\n",fnrows_max, fncols_max)) ; + DEBUG6 (("fnr_curr fnc_curr "ID" "ID"\n",fnr_curr, fnc_curr)) ; + DEBUG6 (("fnrows fncols "ID" "ID"\n",fnrows, fncols)) ; + ASSERT ((fnr_curr % 2 == 1) || fnr_curr == 0) ; + DEBUG6 (("Pivot row pattern:\n")) ; + for (j = 0 ; j < fncols ; j++) + { + DEBUG7 ((ID" "ID" "ID" %d\n", j, Fcols [j], Fcpos [Fcols [j]], + j < fncols)) ; + if (check) + { + ASSERT (Fcols [j] >= 0 && Fcols [j] < Work->n_col) ; + ASSERT (Fcpos [Fcols [j]] == j * fnr_curr) ; + } + } + DEBUG6 (("Pivot col pattern:\n")) ; + for (i = 0 ; i < fnrows ; i++) + { + DEBUG7 ((ID" "ID" "ID" %d\n", i, Frows [i], Frpos [Frows [i]], + i < fnrows)) ; + if (check) + { + ASSERT (Frows [i] >= 0 && Frows [i] < Work->n_row) ; + ASSERT (Frpos [Frows [i]] == i) ; + } + } + if (UMF_debug < 7) return ; + + if (!E [0]) + { + DEBUG6 (("current front not allocated\n")) ; + ASSERT (!Work->Flublock) ; + return ; + } + + ASSERT (Work->Flublock == (Entry *) (Numeric->Memory + E [0])) ; + DEBUG7 (("C block: ")) ; + UMF_dump_dense (Fcblock, fnr_curr, fnrows, fncols) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (Flblock, fnr_curr, fnrows, fnpiv) ; + DEBUG7 (("U' block: ")) ; + UMF_dump_dense (Fublock, fnc_curr, fncols, fnpiv) ; + DEBUG7 (("LU block: ")) ; + UMF_dump_dense (Flublock, Work->nb, fnpiv, fnpiv) ; + if (fnpiv > 0) + { + DEBUG7 (("Pivot entry: ")) ; + EDEBUG7 (Flublock [(fnpiv-1)+(fnpiv-1)*Work->nb]) ; + DEBUG7 (("\n")) ; + } +} + +/* ========================================================================== */ +/* === UMF_dump_lu ========================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_lu +( + NumericType *Numeric +) +{ + Int i, n_row, n_col, *Cperm, *Rperm ; + + DEBUG6 (("=============================================== LU factors:\n")) ; + if (!Numeric) + { + DEBUG6 (("No LU factors allocated\n")) ; + return ; + } + n_row = Numeric->n_row ; + n_col = Numeric->n_col ; + DEBUG6 (("n_row: "ID" n_col: "ID"\n", n_row, n_col)) ; + DEBUG6 (("nLentries: "ID" nUentries: "ID"\n", + Numeric->nLentries, Numeric->nUentries)) ; + + if (Numeric->Cperm) + { + Cperm = Numeric->Cperm ; + DEBUG7 (("Column permutations: (new: old)\n")) ; + for (i = 0 ; i < n_col ; i++) + { + if (Cperm [i] != EMPTY) + { + DEBUG7 ((ID": "ID"\n", i, Cperm [i])) ; + } + } + } + else + { + DEBUG7 (("No Numeric->Cperm allocatated\n")) ; + } + + if (Numeric->Rperm) + { + Rperm = Numeric->Rperm ; + DEBUG7 (("row permutations: (new: old)\n")) ; + for (i = 0 ; i < n_row ; i++) + { + if (Rperm [i] != EMPTY) + { + DEBUG7 ((ID": "ID"\n", i, Rperm [i])) ; + } + } + } + else + { + DEBUG7 (("No Numeric->Rperm allocatated\n")) ; + } + + DEBUG6 (("========================================= END OF LU factors:\n")); +} + + +/* ========================================================================== */ +/* === UMF_dump_memory ====================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_memory +( + NumericType *Numeric +) +{ + + Unit *p ; + Int prevsize, s ; + Int found ; + + if (!Numeric) + { + DEBUG6 (("No memory space S allocated\n")) ; + return ; + } + + DEBUG6 (("\n ============================================== MEMORY:\n")) ; + if (!Numeric || !Numeric->Memory) + { + DEBUG6 (("No memory space Numeric allocated\n")) ; + return ; + } + + DEBUG6 (("S: "ID"\n", (Int) Numeric)) ; + DEBUG6 (("S->ihead : "ID"\n", Numeric->ihead)) ; + DEBUG6 (("S->itail : "ID"\n", Numeric->itail)) ; + DEBUG6 (("S->size : "ID"\n", Numeric->size)) ; + DEBUG6 (("S->ngarbage : "ID"\n", Numeric->ngarbage)) ; + DEBUG6 (("S->nrealloc : "ID"\n", Numeric->nrealloc)) ; + DEBUG6 ((" in use at head : "ID"\n", Numeric->ihead)) ; + DEBUG6 ((" free space : "ID"\n", + Numeric->itail - Numeric->ihead)) ; + DEBUG6 ((" blocks in use at tail : "ID"\n", + Numeric->size - Numeric->itail)) ; + DEBUG6 ((" total in use : "ID"\n", + Numeric->size - (Numeric->itail - Numeric->ihead))) ; + + prevsize = 0 ; + found = FALSE ; + + ASSERT (0 <= Numeric->ihead) ; + ASSERT (Numeric->ihead <= Numeric->itail) ; + ASSERT (Numeric->itail <= Numeric->size) ; + + p = Numeric->Memory + Numeric->itail ; + + while (p < Numeric->Memory + Numeric->size) + { + DEBUG8 (("p: "ID" p+1: "ID" prevsize: "ID" size: "ID, + (Int) (p-Numeric->Memory), (Int) (p+1-Numeric->Memory), + p->header.prevsize, p->header.size)) ; + if (p->header.size < 0) + { + DEBUG8 ((" free")) ; + } + + if (p == Numeric->Memory + Numeric->itail) + { + ASSERT (p->header.prevsize == 0) ; + } + else + { + ASSERT (p->header.prevsize > 0) ; + } + + ASSERT (p->header.size != 0) ; + s = prevsize >= 0 ? prevsize : -prevsize ; + ASSERT (p->header.prevsize == s) ; + /* no adjacent free blocks */ + ASSERT (p->header.size > 0 || prevsize > 0) ; + if (Numeric->ibig != EMPTY) + { + if (p == Numeric->Memory + Numeric->ibig) + { + ASSERT (p->header.size < 0) ; + DEBUG8 ((" <===== Numeric->ibig")) ; + found = TRUE ; + } + } + s = p->header.size ; + prevsize = s ; + s = s >= 0 ? s : -s ; + p = p + 1 + s ; + DEBUG8 (("\n")) ; + } + + ASSERT (p == Numeric->Memory + Numeric->size) ; + ASSERT (IMPLIES (Numeric->ibig != EMPTY, found)) ; + DEBUG6 (("============================================= END OF MEMORY:\n")); + +} + + +/* ========================================================================== */ +/* === UMF_dump_packed_memory =============================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_packed_memory +( + NumericType *Numeric, + WorkType *Work +) +{ + Unit *p, *p3 ; + Int prevsize, col, row, *Rows, *Cols, ncols, nrows, k, esize, + *Row_tuples, *Row_degree, *Col_tuples, *Col_degree ; + Entry *C ; + Element *ep ; + + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ + Row_tuples = Numeric->Uip ; + Col_tuples = Numeric->Lip ; + + DEBUG6 (("============================================ PACKED MEMORY:\n")) ; + if (!Numeric || !Numeric->Memory) + { + DEBUG6 (("No memory space S allocated\n")) ; + return ; + } + DEBUG6 (("S: "ID"\n", (Int) Numeric)) ; + DEBUG6 (("S->ihead : "ID"\n", Numeric->ihead)) ; + DEBUG6 (("S->itail : "ID"\n", Numeric->itail)) ; + DEBUG6 (("S->size : "ID"\n", Numeric->size)) ; + DEBUG6 (("S->ngarbage : "ID"\n", Numeric->ngarbage)) ; + DEBUG6 (("S->nrealloc : "ID"\n", Numeric->nrealloc)) ; + DEBUG6 ((" in use at head : "ID"\n", Numeric->ihead)) ; + DEBUG6 ((" free space : "ID"\n", + Numeric->itail - Numeric->ihead)) ; + DEBUG6 ((" blocks in use at tail : "ID"\n", + Numeric->size - Numeric->itail)) ; + DEBUG6 ((" total in use : "ID"\n", + Numeric->size - (Numeric->itail - Numeric->ihead))) ; + + ASSERT (0 <= Numeric->ihead) ; + ASSERT (Numeric->ihead <= Numeric->itail) ; + ASSERT (Numeric->itail <= Numeric->size) ; + + for (row = 0 ; row < Work->n_row ; row++) + { + ASSERT (IMPLIES (NON_PIVOTAL_ROW (row), !Row_tuples [row])) ; + } + for (col = 0 ; col < Work->n_col ; col++) + { + ASSERT (IMPLIES (NON_PIVOTAL_COL (col), !Col_tuples [col])) ; + } + + prevsize = 0 ; + p = Numeric->Memory + Numeric->itail ; + while (p < Numeric->Memory + Numeric->size) + { + DEBUG9 (("====================\n")) ; + DEBUG7 (("p: "ID" p+1: "ID" prevsize: "ID" size: "ID"\n", + (Int) (p-Numeric->Memory), (Int) (p+1-Numeric->Memory), + p->header.prevsize, p->header.size)) ; + ASSERT (p->header.size > 0) ; + + if (p == Numeric->Memory + Numeric->itail) + { + ASSERT (p->header.prevsize == 0) ; + } + else + { + ASSERT (p->header.prevsize > 0) ; + } + + ASSERT (p->header.prevsize == prevsize) ; + prevsize = p->header.size ; + + if (p != Numeric->Memory + Numeric->size - 2) + { + + p3 = p + 1 ; + if (p3 == Numeric->Memory + Work->E [0]) + { + /* this is the current frontal matrix */ + UMF_dump_current_front (Numeric, Work, FALSE) ; + } + else + { + + /* this is a packed element */ + GET_ELEMENT (ep, p3, Cols, Rows, ncols, nrows, C) ; + DEBUG9 (("ep "ID"\n nrows "ID" ncols "ID"\n", + (Int) ((p+1)-Numeric->Memory), ep->nrows, ep->ncols)) ; + DEBUG9 (("rows:")) ; + for (k = 0 ; k < ep->nrows; k++) + { + row = Rows [k] ; + DEBUG9 ((" "ID, row)) ; + ASSERT (row >= 0 && row <= Work->n_row) ; + if ((k % 10) == 9) DEBUG9 (("\n")) ; + } + DEBUG9 (("\ncols:")) ; + for (k = 0 ; k < ep->ncols; k++) + { + col = Cols [k] ; + DEBUG9 ((" "ID, col)) ; + ASSERT (col >= 0 && col <= Work->n_col) ; + if ((k % 10) == 9) DEBUG9 (("\n")) ; + } + DEBUG9 (("\nvalues: ")) ; + if (UMF_debug >= 9) + { + UMF_dump_dense (C, ep->nrows, ep->nrows, ep->ncols) ; + } + esize = GET_ELEMENT_SIZE (ep->nrows, ep->ncols) ; + DEBUG9 (("esize: "ID"\n", esize)) ; + ASSERT (esize <= p->header.size) ; + } + + } + else + { + /* this is the final marker block */ + ASSERT (p->header.size == 1) ; + } + p = p + 1 + p->header.size ; + } + + ASSERT (Numeric->ibig == EMPTY) ; + ASSERT (p == Numeric->Memory + Numeric->size) ; + DEBUG6 (("======================================END OF PACKED MEMORY:\n")) ; + +} + +/* ========================================================================== */ +/* === UMF_dump_col_matrix ================================================== */ +/* ========================================================================== */ + +/* This code is the same for real or complex matrices. */ + +GLOBAL void UMF_dump_col_matrix +( + const double Ax [ ], /* Ax [0..nz-1]: real values, in column order */ +#ifdef COMPLEX + const double Az [ ], /* Az [0..nz-1]: imag values, in column order */ +#endif + const Int Ai [ ], /* Ai [0..nz-1]: row indices, in column order */ + const Int Ap [ ], /* Ap [0..n_col]: column pointers */ + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Int nz /* number of entries */ +) +{ + Int col, p, p1, p2, row ; + if (!Ai || !Ap) return ; + DEBUG6 (("============================================ COLUMN FORM:\n")) ; + + + ASSERT (n_col >= 0) ; + nz = Ap [n_col] ; + DEBUG2 (("UMF_dump_col: nz "ID"\n", nz)) ; + DEBUG2 (("n_row "ID" \n", n_row)) ; + DEBUG2 (("n_col "ID" \n", n_col)) ; + + DEBUG6 ((" n_row = "ID", n_col ="ID" nz = "ID" Ap [0] "ID", Ap [n] "ID"\n", + n_row, n_col, nz, Ap [0], Ap [n_col])) ; + ASSERT (Ap [0] == 0) ; + ASSERT (Ap [n_col] == nz) ; + for (col = 0 ; col < n_col ; col++) + { + p1 = Ap [col] ; + p2 = Ap [col+1] ; + DEBUG6 (("col: "ID", length "ID"\n", col, p2 - p1)) ; + ASSERT (p2 >= p1) ; + for (p = p1 ; p < p2 ; p++) + { + row = Ai [p] ; + ASSERT (row >= 0 && row < n_row) ; + DEBUG6 (("\t"ID" ", row)) ; + if (Ax != (double *) NULL) + { +#ifdef COMPLEX + if (Az != (double *) NULL) + { + DEBUG6 ((" (%e+%ei) ", Ax [p], Az [p])) ; + } + else + { + DEBUG6 ((" %e", Ax [p])) ; + } +#else + DEBUG6 ((" %e", Ax [p])) ; +#endif + } + DEBUG6 (("\n")) ; + } + } + DEBUG6 (("========================================== COLUMN FORM done\n")) ; +} + + +/* ========================================================================== */ +/* === UMF_dump_chain ======================================================= */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_chain +( + Int frontid, + Int Front_parent [ ], + Int Front_npivcol [ ], + Int Front_nrows [ ], + Int Front_ncols [ ], + Int nfr +) +{ + Int i, len = 0 ; + + /* print a list of contiguous parents */ + i = frontid ; + ASSERT (Front_parent [i] == EMPTY || + (Front_parent [i] > i && Front_parent [i] < nfr)) ; + + len++ ; + DEBUG3 (("Chain:\n "ID" ["ID","ID"]("ID"-by-"ID")\n", i, + Front_npivcol [i], + MIN (Front_npivcol [i], Front_nrows [i]), + Front_nrows [i], + Front_ncols [i])) ; + + for (i = frontid ; i < nfr ; i++) + { + ASSERT (Front_parent [i] == EMPTY || + (Front_parent [i] > i && Front_parent [i] < nfr)) ; + if (Front_parent [i] == i+1) + { + len++ ; + DEBUG3 (("\t"ID" ["ID","ID"]("ID"-by-"ID")\n", i+1, + Front_npivcol [i+1], + MIN (Front_npivcol [i+1], Front_nrows [i+1]), + Front_nrows [i+1], + Front_ncols [i+1])) ; + } + else + { + DEBUG2 (("Length of chain: "ID"\n", len)) ; + return ; + } + } +} + + +/* ========================================================================== */ +/* === UMF_dump_start ======================================================= */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_start +( + void +) +{ + FILE *ff ; + + /* get the debug print level from the "debug.umf" file, if it exists */ + UMF_debug = -999 ; + ff = fopen ("debug.umf", "r") ; + if (ff) + { + (void) fscanf (ff, ID, &UMF_debug) ; + (void) fclose (ff) ; + } + + DEBUG0 (("umfpack: debug version (SLOW!) ")) ; + + DEBUG0 ((" BLAS: ")) ; + +#if defined (USE_NO_BLAS) + DEBUG0 (("none.")) ; +#elif defined (USE_C_BLAS) + DEBUG0 (("C-BLAS.")) ; +#elif defined (USE_MATLAB_BLAS) + DEBUG0 (("built-in MATLAB BLAS.")) ; +#elif defined (USE_SUNPERF_BLAS) + DEBUG0 (("Sun Performance Library BLAS.")) ; +#elif defined (USE_SCSL_BLAS) + DEBUG0 (("SGI SCSL BLAS.")) ; +#elif defined (USE_FORTRAN_BLAS) + DEBUG0 (("Fortran BLAS.")) ; +#endif + + DEBUG0 ((" MATLAB: ")) ; +#ifdef MATLAB_MEX_FILE + DEBUG0 (("mexFunction.\n")) ; +#else +#ifdef MATHWORKS + DEBUG0 (("yes (uses MathWorks internal ut* routines).\n")) ; +#else + DEBUG0 (("no.\n")) ; +#endif +#endif + + UMF_gprob = -1.0 ; + ff = fopen ("gprob.umf", "r") ; + if (ff) + { + (void) fscanf (ff, "%lg", &UMF_gprob) ; + (void) fclose (ff) ; + srand (1) ; /* restart the random number generator */ + } + + if (UMF_gprob > 1.0) UMF_gprob = 1.0 ; + DEBUG1 (("factor: UMF_gprob: %e UMF_debug "ID"\n", UMF_gprob, UMF_debug)) ; + + DEBUG2 (("sizeof: (bytes / int / Units) \n")) ; + DEBUG2 (("sizeof (Int) %u %u %u\n", + sizeof (Int), sizeof (Int) / sizeof (int), UNITS (Int, 1) )) ; + DEBUG2 (("sizeof (int) %u %u %u\n", + sizeof (int), sizeof (int) / sizeof (int), UNITS (int, 1) )) ; + DEBUG2 (("sizeof (size_t) %u %u %u\n", + sizeof (size_t), sizeof (size_t) / sizeof (size_t), UNITS (size_t, 1) )) ; + DEBUG2 (("sizeof (long) %u %u %u\n", + sizeof (long), sizeof (long) / sizeof (long), UNITS (long, 1) )) ; + DEBUG2 (("sizeof (double) %u %u %u\n", + sizeof (double), sizeof (double) / sizeof (int), UNITS (double, 1) )) ; + DEBUG2 (("sizeof (Unit) %u %u %u\n", + sizeof (Unit), sizeof (Unit) / sizeof (int), UNITS (Unit, 1) )) ; + DEBUG2 (("sizeof (Entry) %u %u %u\n", + sizeof (Entry), sizeof (Entry) / sizeof (int), UNITS (Entry, 1) )) ; + DEBUG2 (("sizeof (Tuple) %u %u %u\n", + sizeof (Tuple), sizeof (Tuple) / sizeof (int), UNITS (Tuple, 1) )) ; + DEBUG2 (("sizeof (Tuple *) %u %u %u\n", + sizeof (Tuple *), sizeof (Tuple *) / sizeof (int), UNITS (Tuple *, 1) )) ; + DEBUG2 (("sizeof (Element) %u %u %u\n", + sizeof (Element), sizeof (Element) / sizeof (int), UNITS (Element, 1) )) ; + DEBUG2 (("sizeof (Element *) %u %u %u\n", + sizeof (Element *), sizeof (Element *) / sizeof (int), + UNITS (Element *, 1) )) ; + DEBUG2 (("sizeof (WorkType) %u %u %u\n", + sizeof (WorkType), sizeof (WorkType) / sizeof (int), + UNITS (WorkType, 1) )) ; + DEBUG2 (("sizeof (NumericType) %u %u %u\n", + sizeof (NumericType), sizeof (NumericType) / sizeof (int), + UNITS (NumericType, 1) )) ; + DEBUG2 (("sizeof (SymbolicType) %u %u %u\n", + sizeof (SymbolicType), sizeof (SymbolicType) / sizeof (int), + UNITS (SymbolicType, 1) )) ; + +} + + +/* ========================================================================== */ +/* === UMF_dump_rowmerge ==================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_rowmerge +( + NumericType *Numeric, + SymbolicType *Symbolic, + WorkType *Work +) +{ + Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1, row2, + fleftmost, nfr, n_row, *Row_degree, i, frontid, row ; + + nfr = Symbolic->nfr ; + DEBUG3 (("\n================== Row merge sets: nfr "ID"\n", nfr)) ; + Front_leftmostdesc = Symbolic->Front_leftmostdesc ; + Front_1strow = Symbolic->Front_1strow ; + Front_new1strow = Work->Front_new1strow ; + n_row = Symbolic->n_row ; + Row_degree = Numeric->Rperm ; + frontid = Work->frontid ; + + for (i = frontid ; i <= nfr ; i++) + { + DEBUG3 (("----------------------\n")) ; + if (i == nfr) DEBUG3 (("Dummy: ")) ; + DEBUG3 (("Front "ID" 1strow "ID" new1strow "ID" leftmostdesc "ID, + i, Front_1strow [i], Front_new1strow [i], Front_leftmostdesc [i])) ; + DEBUG3 ((" parent "ID" pivcol "ID"\n", Symbolic->Front_parent [i], + Symbolic->Front_npivcol [i])) ; + + if (i == nfr) + { + fleftmost = -1 ; + row1 = Front_new1strow [i] ; + row2 = n_row-1 ; + } + else + { + fleftmost = Front_leftmostdesc [i] ; + row1 = Front_new1strow [fleftmost] ; + row2 = Front_1strow [i+1] - 1 ; + } + DEBUG3 (("Leftmost: "ID" Rows ["ID" to "ID"], search ["ID" to "ID"]\n", + fleftmost, Front_1strow [i], row2, row1, row2)) ; + + for (row = row1 ; row <= row2 ; row++) + { + ASSERT (row >= 0 && row < n_row) ; + DEBUG3 ((" Row "ID" live: %d\n", row, NON_PIVOTAL_ROW (row))) ; + } + } +} + +/* ========================================================================== */ +/* === UMF_dump_diagonal_map ================================================ */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_diagonal_map +( + Int Diagonal_map [ ], + Int Diagonal_imap [ ], + Int n1, + Int nn, + Int nempty +) +{ + Int row, col ; + if (Diagonal_map != (Int *) NULL) + { + DEBUG2 (("\nDump the Diagonal_map: n1 "ID" nn "ID" nempty "ID"\n", + n1, nn, nempty)) ; + for (col = n1 ; col < nn - nempty ; col++) + { + row = Diagonal_map [col] ; + DEBUG2 ((" Diagonal_map [col = "ID"] gives "ID": ", + col, row)) ; + row = UNFLIP (row) ; + DEBUG2 ((" row "ID"\n", row)) ; + ASSERT (Diagonal_imap [row] == col) ; + } + } +} + +#endif /* NDEBUG */ diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_dump.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_dump.h new file mode 100644 index 0000000000..de220f37a1 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_dump.h @@ -0,0 +1,187 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* umf_dump.h: debugging definitions. */ + +#ifndef NDEBUG + +GLOBAL void UMF_dump_dense +( + Entry *C, + Int dim, + Int m, + Int n +) ; + +GLOBAL void UMF_dump_element +( + NumericType *Numeric, + WorkType *Work, + Int e, + Int clean +) ; + +GLOBAL void UMF_dump_rowcol +( + Int dump_which, + NumericType *Numeric, + WorkType *Work, + Int dump_index, + Int check_degree +) ; + +GLOBAL void UMF_dump_matrix +( + NumericType *Numeric, + WorkType *Work, + Int check_degree +) ; + +GLOBAL void UMF_dump_current_front +( + NumericType *Numeric, + WorkType *Work, + Int check +) ; + +GLOBAL void UMF_dump_lu +( + NumericType *Numeric +) ; + +GLOBAL void UMF_dump_memory +( + NumericType *Numeric +) ; + +GLOBAL void UMF_dump_packed_memory +( + NumericType *Numeric, + WorkType *Work +) ; + +GLOBAL void UMF_dump_col_matrix +( + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + const Int Ai [ ], + const Int Ap [ ], + Int n_row, + Int n_col, + Int nz +) ; + +GLOBAL void UMF_dump_chain +( + Int frontid, + Int Front_parent [ ], + Int Front_npivcol [ ], + Int Front_nrows [ ], + Int Front_ncols [ ], + Int nfr +) ; + +GLOBAL void UMF_dump_rowmerge +( + NumericType *Numeric, + SymbolicType *Symbolic, + WorkType *Work +) ; + +GLOBAL void UMF_dump_start +( + void +) ; + + +GLOBAL void UMF_dump_diagonal_map +( + Int Diagonal_map [ ], + Int Diagonal_imap [ ], + Int n1, + Int nn, + Int nempty +) ; + +#define UMF_DBMAX 50000 +GLOBAL extern Int UMF_debug ; +GLOBAL extern Int UMF_allocfail ; +GLOBAL extern double UMF_gprob ; + +#define DEBUGk(k,params) { if (UMF_debug >= (k)) { PRINTF (params) ; } } + +#define DEBUGm4(params) DEBUGk (-4, params) +#define DEBUGm3(params) DEBUGk (-3, params) +#define DEBUGm2(params) DEBUGk (-2, params) +#define DEBUGm1(params) DEBUGk (-1, params) +#define DEBUG0(params) DEBUGk (0, params) +#define DEBUG1(params) DEBUGk (1, params) +#define DEBUG2(params) DEBUGk (2, params) +#define DEBUG3(params) DEBUGk (3, params) +#define DEBUG4(params) DEBUGk (4, params) +#define DEBUG5(params) DEBUGk (5, params) +#define DEBUG6(params) DEBUGk (6, params) +#define DEBUG7(params) DEBUGk (7, params) +#define DEBUG8(params) DEBUGk (8, params) +#define DEBUG9(params) DEBUGk (9, params) + +#define EDEBUGk(k,a) { if (UMF_debug >= (k)) { PRINT_ENTRY (a) ; } } + +#define EDEBUG0(a) EDEBUGk (0, a) +#define EDEBUG1(a) EDEBUGk (1, a) +#define EDEBUG2(a) EDEBUGk (2, a) +#define EDEBUG3(a) EDEBUGk (3, a) +#define EDEBUG4(a) EDEBUGk (4, a) +#define EDEBUG5(a) EDEBUGk (5, a) +#define EDEBUG6(a) EDEBUGk (6, a) +#define EDEBUG7(a) EDEBUGk (7, a) +#define EDEBUG8(a) EDEBUGk (8, a) +#define EDEBUG9(a) EDEBUGk (9, a) + +/* ASSERT defined in amd_dump.h */ + +#else + +/* ========================================================================== */ +/* === No debugging ========================================================= */ +/* ========================================================================== */ + +/* turn off all debugging macros */ + +#define DEBUGk(k,params) + +#define DEBUGm4(params) +#define DEBUGm3(params) +#define DEBUGm2(params) +#define DEBUGm1(params) +#define DEBUG0(params) +#define DEBUG1(params) +#define DEBUG2(params) +#define DEBUG3(params) +#define DEBUG4(params) +#define DEBUG5(params) +#define DEBUG6(params) +#define DEBUG7(params) +#define DEBUG8(params) +#define DEBUG9(params) + +#define EDEBUGk(k,a) + +#define EDEBUG0(a) +#define EDEBUG1(a) +#define EDEBUG2(a) +#define EDEBUG3(a) +#define EDEBUG4(a) +#define EDEBUG5(a) +#define EDEBUG6(a) +#define EDEBUG7(a) +#define EDEBUG8(a) +#define EDEBUG9(a) + +#endif /* NDEBUG */ diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_extend_front.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_extend_front.c new file mode 100644 index 0000000000..241ed8aa58 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_extend_front.c @@ -0,0 +1,393 @@ +/* ========================================================================== */ +/* === UMF_extend_front ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Called by kernel. */ + +#include "umf_internal.h" +#include "umf_grow_front.h" + +/* ========================================================================== */ +/* === zero_front =========================================================== */ +/* ========================================================================== */ + +PRIVATE void zero_front ( + Entry *Flblock, Entry *Fublock, Entry *Fcblock, + Int fnrows, Int fncols, Int fnr_curr, Int fnc_curr, + Int fnpiv, Int fnrows_extended, Int fncols_extended) +{ + Int j, i ; + Entry *F, *Fj, *Fi ; + + Fj = Fcblock + fnrows ; + for (j = 0 ; j < fncols ; j++) + { + /* zero the new rows in the contribution block: */ + F = Fj ; + Fj += fnr_curr ; +#pragma ivdep + for (i = fnrows ; i < fnrows_extended ; i++) + { + /* CLEAR (Fcblock [i + j*fnr_curr]) ; */ + CLEAR_AND_INCREMENT (F) ; + } + } + + Fj -= fnrows ; + for (j = fncols ; j < fncols_extended ; j++) + { + /* zero the new columns in the contribution block: */ + F = Fj ; + Fj += fnr_curr ; +#pragma ivdep + for (i = 0 ; i < fnrows_extended ; i++) + { + /* CLEAR (Fcblock [i + j*fnr_curr]) ; */ + CLEAR_AND_INCREMENT (F) ; + } + } + + Fj = Flblock + fnrows ; + for (j = 0 ; j < fnpiv ; j++) + { + /* zero the new rows in L block: */ + F = Fj ; + Fj += fnr_curr ; +#pragma ivdep + for (i = fnrows ; i < fnrows_extended ; i++) + { + /* CLEAR (Flblock [i + j*fnr_curr]) ; */ + CLEAR_AND_INCREMENT (F) ; + } + } + + Fi = Fublock + fncols ; + for (i = 0 ; i < fnpiv ; i++) + { + /* zero the new columns in U block: */ + F = Fi ; + Fi += fnc_curr ; +#pragma ivdep + for (j = fncols ; j < fncols_extended ; j++) + { + /* CLEAR (Fublock [i*fnc_curr + j]) ; */ + CLEAR_AND_INCREMENT (F) ; + } + } + +} + +/* ========================================================================== */ +/* === UMF_extend_front ===================================================== */ +/* ========================================================================== */ + +GLOBAL Int UMF_extend_front +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int j, i, *Frows, row, col, *Wrow, fnr2, fnc2, *Frpos, *Fcpos, *Fcols, + fnrows_extended, rrdeg, ccdeg, fncols_extended, fnr_curr, fnc_curr, + fnrows, fncols, pos, fnpiv, *Wm ; + Entry *Wx, *Wy, *Fu, *Fl ; + + /* ---------------------------------------------------------------------- */ + /* get current frontal matrix and check for frontal growth */ + /* ---------------------------------------------------------------------- */ + + fnpiv = Work->fnpiv ; + +#ifndef NDEBUG + DEBUG2 (("EXTEND FRONT\n")) ; + DEBUG2 (("Work->fnpiv "ID"\n", fnpiv)) ; + ASSERT (Work->Flblock == Work->Flublock + Work->nb*Work->nb) ; + ASSERT (Work->Fublock == Work->Flblock + Work->fnr_curr*Work->nb) ; + ASSERT (Work->Fcblock == Work->Fublock + Work->nb*Work->fnc_curr) ; + DEBUG7 (("C block: ")) ; + UMF_dump_dense (Work->Fcblock, Work->fnr_curr, Work->fnrows, Work->fncols) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (Work->Flblock, Work->fnr_curr, Work->fnrows, fnpiv); + DEBUG7 (("U' block: ")) ; + UMF_dump_dense (Work->Fublock, Work->fnc_curr, Work->fncols, fnpiv) ; + DEBUG7 (("LU block: ")) ; + UMF_dump_dense (Work->Flublock, Work->nb, fnpiv, fnpiv) ; +#endif + + if (Work->do_grow) + { + fnr2 = UMF_FRONTAL_GROWTH * Work->fnrows_new + 2 ; + fnc2 = UMF_FRONTAL_GROWTH * Work->fncols_new + 2 ; + if (!UMF_grow_front (Numeric, fnr2, fnc2, Work, 1)) + { + DEBUGm4 (("out of memory: extend front\n")) ; + return (FALSE) ; + } + } + + fnr_curr = Work->fnr_curr ; + fnc_curr = Work->fnc_curr ; + ASSERT (Work->fnrows_new + 1 <= fnr_curr) ; + ASSERT (Work->fncols_new + 1 <= fnc_curr) ; + ASSERT (fnr_curr >= 0 && fnr_curr % 2 == 1) ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + Frows = Work->Frows ; + Frpos = Work->Frpos ; + Fcols = Work->Fcols ; + Fcpos = Work->Fcpos ; + fnrows = Work->fnrows ; + fncols = Work->fncols ; + rrdeg = Work->rrdeg ; + ccdeg = Work->ccdeg ; + + /* scan starts at the first new column in Fcols */ + /* also scan the pivot column if it was not in the front */ + Work->fscan_col = fncols ; + Work->NewCols = Fcols ; + + /* scan1 starts at the first new row in Frows */ + /* also scan the pivot row if it was not in the front */ + Work->fscan_row = fnrows ; + Work->NewRows = Frows ; + + /* ---------------------------------------------------------------------- */ + /* extend row pattern of the front with the new pivot column */ + /* ---------------------------------------------------------------------- */ + + fnrows_extended = fnrows ; + fncols_extended = fncols ; + +#ifndef NDEBUG + DEBUG2 (("Pivot col, before extension: "ID"\n", fnrows)) ; + for (i = 0 ; i < fnrows ; i++) + { + DEBUG2 ((" "ID": row "ID"\n", i, Frows [i])) ; + ASSERT (Frpos [Frows [i]] == i) ; + } + DEBUG2 (("Extending pivot column: pivcol_in_front: "ID"\n", + Work->pivcol_in_front)) ; +#endif + + Fl = Work->Flblock + fnpiv * fnr_curr ; + + if (Work->pivcol_in_front) + { + /* extended pattern and position already in Frows, Frpos. Values above + * the diagonal are already in LU block. Values on and below the + * diagonal are in Wy [0 .. fnrows_extended-1]. Copy into the L + * block. */ + fnrows_extended += ccdeg ; + Wy = Work->Wy ; + + for (i = 0 ; i < fnrows_extended ; i++) + { + Fl [i] = Wy [i] ; +#ifndef NDEBUG + row = Frows [i] ; + DEBUG2 ((" "ID": row "ID" ", i, row)) ; + EDEBUG2 (Fl [i]) ; + if (row == Work->pivrow) DEBUG2 ((" <- pivrow")) ; + DEBUG2 (("\n")) ; + if (i == fnrows - 1) DEBUG2 ((" :::::::\n")) ; + ASSERT (row >= 0 && row < Work->n_row) ; + ASSERT (Frpos [row] == i) ; +#endif + } + + } + else + { + /* extended pattern,values is in (Wm,Wx), not yet in the front */ + Entry *F ; + Fu = Work->Flublock + fnpiv * Work->nb ; + Wm = Work->Wm ; + Wx = Work->Wx ; + F = Fu ; + for (i = 0 ; i < fnpiv ; i++) + { + CLEAR_AND_INCREMENT (F) ; + } + F = Fl ; + for (i = 0 ; i < fnrows ; i++) + { + CLEAR_AND_INCREMENT (F) ; + } + for (i = 0 ; i < ccdeg ; i++) + { + row = Wm [i] ; +#ifndef NDEBUG + DEBUG2 ((" "ID": row "ID" (ext) ", fnrows_extended, row)) ; + EDEBUG2 (Wx [i]) ; + if (row == Work->pivrow) DEBUG2 ((" <- pivrow")) ; + DEBUG2 (("\n")) ; + ASSERT (row >= 0 && row < Work->n_row) ; +#endif + pos = Frpos [row] ; + if (pos < 0) + { + pos = fnrows_extended++ ; + Frows [pos] = row ; + Frpos [row] = pos ; + } + Fl [pos] = Wx [i] ; + } + } + + ASSERT (fnrows_extended <= fnr_curr) ; + + /* ---------------------------------------------------------------------- */ + /* extend the column pattern of the front with the new pivot row */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG6 (("Pivot row, before extension: "ID"\n", fncols)) ; + for (j = 0 ; j < fncols ; j++) + { + DEBUG7 ((" "ID": col "ID"\n", j, Fcols [j])) ; + ASSERT (Fcpos [Fcols [j]] == j * fnr_curr) ; + } + DEBUG6 (("Extending pivot row:\n")) ; +#endif + + if (Work->pivrow_in_front) + { + if (Work->pivcol_in_front) + { + ASSERT (Fcols == Work->Wrow) ; + for (j = fncols ; j < rrdeg ; j++) + { +#ifndef NDEBUG + col = Fcols [j] ; + DEBUG2 ((" "ID": col "ID" (ext)\n", j, col)) ; + ASSERT (col != Work->pivcol) ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (Fcpos [col] < 0) ; +#endif + Fcpos [Fcols [j]] = j * fnr_curr ; + } + } + else + { + /* OUT-IN option: pivcol not in front, but pivrow is in front */ + Wrow = Work->Wrow ; + ASSERT (IMPLIES (Work->pivcol_in_front, Wrow == Fcols)) ; + if (Wrow == Fcols) + { + /* Wrow and Fcols are equivalenced */ + for (j = fncols ; j < rrdeg ; j++) + { + col = Wrow [j] ; + DEBUG2 ((" "ID": col "ID" (ext)\n", j, col)) ; + ASSERT (Fcpos [col] < 0) ; + /* Fcols [j] = col ; not needed */ + Fcpos [col] = j * fnr_curr ; + } + } + else + { + for (j = fncols ; j < rrdeg ; j++) + { + col = Wrow [j] ; + DEBUG2 ((" "ID": col "ID" (ext)\n", j, col)) ; + ASSERT (Fcpos [col] < 0) ; + Fcols [j] = col ; + Fcpos [col] = j * fnr_curr ; + } + } + } + fncols_extended = rrdeg ; + } + else + { + ASSERT (Fcols != Work->Wrow) ; + Wrow = Work->Wrow ; + for (j = 0 ; j < rrdeg ; j++) + { + col = Wrow [j] ; + ASSERT (col >= 0 && col < Work->n_col) ; + if (Fcpos [col] < 0) + { + DEBUG2 ((" col:: "ID" (ext)\n", col)) ; + Fcols [fncols_extended] = col ; + Fcpos [col] = fncols_extended * fnr_curr ; + fncols_extended++ ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* pivot row and column have been extended */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + ASSERT (fncols_extended <= fnc_curr) ; + ASSERT (fnrows_extended <= fnr_curr) ; + + DEBUG6 (("Pivot col, after ext: "ID" "ID"\n", fnrows,fnrows_extended)) ; + for (i = 0 ; i < fnrows_extended ; i++) + { + row = Frows [i] ; + DEBUG7 ((" "ID": row "ID" pos "ID" old: %d", i, row, Frpos [row], + i < fnrows)) ; + if (row == Work->pivrow ) DEBUG7 ((" <-- pivrow")) ; + DEBUG7 (("\n")) ; + ASSERT (Frpos [Frows [i]] == i) ; + } + + DEBUG6 (("Pivot row position: "ID"\n", Frpos [Work->pivrow])) ; + ASSERT (Frpos [Work->pivrow] >= 0) ; + ASSERT (Frpos [Work->pivrow] < fnrows_extended) ; + + DEBUG6 (("Pivot row, after ext: "ID" "ID"\n", fncols,fncols_extended)) ; + for (j = 0 ; j < fncols_extended ; j++) + { + col = Fcols [j] ; + DEBUG7 ((" "ID": col "ID" pos "ID" old: %d", j, col, Fcpos [col], + j < fncols)) ; + if (col == Work->pivcol ) DEBUG7 ((" <-- pivcol")) ; + DEBUG7 (("\n")) ; + ASSERT (Fcpos [Fcols [j]] == j * fnr_curr) ; + } + + DEBUG6 (("Pivot col position: "ID"\n", Fcpos [Work->pivcol])) ; + ASSERT (Fcpos [Work->pivcol] >= 0) ; + ASSERT (Fcpos [Work->pivcol] < fncols_extended * fnr_curr) ; + +#endif + + /* ---------------------------------------------------------------------- */ + /* Zero the newly extended frontal matrix */ + /* ---------------------------------------------------------------------- */ + + zero_front (Work->Flblock, Work->Fublock, Work->Fcblock, + fnrows, fncols, fnr_curr, fnc_curr, + fnpiv, fnrows_extended, fncols_extended) ; + + /* ---------------------------------------------------------------------- */ + /* finalize extended row and column pattern of the frontal matrix */ + /* ---------------------------------------------------------------------- */ + + Work->fnrows = fnrows_extended ; + Work->fncols = fncols_extended ; + + ASSERT (fnrows_extended == Work->fnrows_new + 1) ; + ASSERT (fncols_extended == Work->fncols_new + 1) ; + + return (TRUE) ; + +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_extend_front.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_extend_front.h new file mode 100644 index 0000000000..45e3d47500 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_extend_front.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_extend_front +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_free.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_free.c new file mode 100644 index 0000000000..5f74ae97ba --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_free.c @@ -0,0 +1,46 @@ +/* ========================================================================== */ +/* === UMF_free ============================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Free a block previously allocated by UMF_malloc and return NULL. + Usage is p = UMF_free (p), to ensure that we don't free it twice. + Also maintains the UMFPACK malloc count. +*/ + +#include "umf_internal.h" + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) +#include "umf_malloc.h" +#endif + +GLOBAL void *UMF_free +( + void *p +) +{ + DEBUG0 (("UMF_free: "ID"\n", (Int) p)) ; + if (p) + { + + /* see umf_config.h for the memory allocator selection */ + FREE (p) ; + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) + /* One more object has been free'd. Keep track of the count. */ + /* (purely for sanity checks). */ + UMF_malloc_count-- ; + DEBUG0 ((" new malloc count: "ID"\n", UMF_malloc_count)) ; +#endif + + } + + return ((void *) NULL) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_free.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_free.h new file mode 100644 index 0000000000..b2bf660d2c --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_free.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void *UMF_free +( + void *p +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_fsize.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_fsize.c new file mode 100644 index 0000000000..ceaf648b2b --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_fsize.c @@ -0,0 +1,70 @@ +/* ========================================================================== */ +/* === UMF_fsize ============================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Determine the largest frontal matrix size for each subtree. Called by + * UMF_colamd and UMF_analyze. Only required to sort the children of each + * node prior to AMD_postorder. */ + +#include "umf_internal.h" + +GLOBAL void UMF_fsize +( + Int nn, + Int Fsize [ ], + Int Fnrows [ ], + Int Fncols [ ], + Int Parent [ ], + Int Npiv [ ] +) +{ + Int j, parent, frsize, r, c ; + + for (j = 0 ; j < nn ; j++) + { + Fsize [j] = EMPTY ; + } + + /* ---------------------------------------------------------------------- */ + /* find max front size for tree rooted at node j, for each front j */ + /* ---------------------------------------------------------------------- */ + + DEBUG1 (("\n\n========================================FRONTS:\n")) ; + for (j = 0 ; j < nn ; j++) + { + if (Npiv [j] > 0) + { + /* this is a frontal matrix */ + parent = Parent [j] ; + r = Fnrows [j] ; + c = Fncols [j] ; + frsize = r * c ; + /* avoid integer overflow */ + if (INT_OVERFLOW (((double) r) * ((double) c))) + { + /* :: frsize int overflow :: */ + frsize = Int_MAX ; + } + DEBUG1 ((""ID" : npiv "ID" size "ID" parent "ID" ", + j, Npiv [j], frsize, parent)) ; + Fsize [j] = MAX (Fsize [j], frsize) ; + DEBUG1 (("Fsize [j = "ID"] = "ID"\n", j, Fsize [j])) ; + if (parent != EMPTY) + { + /* find the maximum frontsize of self and children */ + ASSERT (Npiv [parent] > 0) ; + ASSERT (parent > j) ; + Fsize [parent] = MAX (Fsize [parent], Fsize [j]) ; + DEBUG1 (("Fsize [parent = "ID"] = "ID"\n", + parent, Fsize [parent])); + } + } + } +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_fsize.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_fsize.h new file mode 100644 index 0000000000..cef2ad9878 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_fsize.h @@ -0,0 +1,16 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_fsize +( + Int nn, + Int MaxFsize [ ], + Int Fnrows [ ], + Int Fncols [ ], + Int Parent [ ], + Int Npiv [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_garbage_collection.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_garbage_collection.c new file mode 100644 index 0000000000..abaf180f5d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_garbage_collection.c @@ -0,0 +1,696 @@ +/* ========================================================================== */ +/* === UMF_garbage_collection =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Compress the elements at the tail of Numeric->Memory, and delete the tuples. + Elements are renumbered. The new numbering space is compressed, and + in the order of element creation (original elements of A first, followed + by the new elements in the order that they were formed). + + Only called by UMF_get_memory. + + There are 5 ways in which garbage collection can be performed: + + Allocate a new working array for the current frontal matrix. In this + case, there are never any pivot rows/columns in the current frontal + matrix (fnpiv = 0), and the old working array for the current frontal + matrix can always be fully compacted, to fnrows-by-fncols. + + UMF_kernel : UMF_extend : UMF_grow_front : UMF_get_memory + UMF_kernel : UMF_init_front : UMF_grow_front : UMF_get_memory + UMF_kernel : UMF_start_front : UMF_grow_front : UMF_get_memory + + Allocate a new element. In this case, UMF_grow_front may or may not + be subsequently called, depending on Work->do_grow. There are never + any pivot rows/columns in the current frontal matrix (fnpiv=0), but one + may be added if UMF_init_front is to be called just after + UMF_create_element. If do_grow is true, then the current front can be + fully compacted, to fnrows-by-fncols. Otherwise, it can only be + partially compacted, to MAX (fnrows, fnrows_new + 1) -by- + MAX (fncols, fncols_new + 1). + + UMF_kernel : UMF_create_element : UMF_get_memory + + Allocate rows of L and columns of U. In this case, the current + frontal matrix is only partially compacted, to (fnrows_new + 1)-by- + (fncols_new + 1). There are pivots in the frontal matrix (fnpiv > 0). + + UMF_kernel : UMF_store_lu : UMF_get_memory +*/ + +#include "umf_internal.h" + +GLOBAL void UMF_garbage_collection +( + NumericType *Numeric, + WorkType *Work, + Int drnew, /* compact current front to drnew-by-dcnew */ + Int dcnew, + Int do_Fcpos +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int size, e, n_row, n_col, nrows, ncols, nrowsleft, ncolsleft, prevsize, + csize, size2, i2, j2, i, j, cdeg, rdeg, *E, row, col, + *Rows, *Cols, *Rows2, *Cols2, nel, e2, *Row_tuples, *Col_tuples, + *Row_degree, *Col_degree ; + Entry *C, *C1, *C3, *C2 ; + Unit *psrc, *pdest, *p, *pnext ; + Element *epsrc, *epdest ; + +#ifndef NDEBUG + Int nmark ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ + Row_tuples = Numeric->Uip ; + Col_tuples = Numeric->Lip ; + E = Work->E ; + n_row = Work->n_row ; + n_col = Work->n_col ; + + /* note that the tuple lengths (Col_tlen and Row_tlen) are updated, but */ + /* the tuple lists themselves are stale and are about to be destroyed */ + /* and recreated. Do not attempt to scan them until they are recreated. */ + +#ifndef NDEBUG + DEBUGm1 (("::::GARBAGE COLLECTION::::\n")) ; + UMF_dump_memory (Numeric) ; +#endif + + Numeric->ngarbage++ ; + + /* ---------------------------------------------------------------------- */ + /* delete the tuple lists by marking the blocks as free */ + /* ---------------------------------------------------------------------- */ + + /* do not modify Row_tlen and Col_tlen */ + /* those are needed for UMF_build_tuples */ + + for (row = 0 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row) && Row_tuples [row]) + { + DEBUG2 (("row "ID" tuples "ID"\n", row, Row_tuples [row])) ; + p = Numeric->Memory + Row_tuples [row] - 1 ; + DEBUG2 (("Freeing tuple list row "ID", p-S "ID", size "ID"\n", + row, (Int) (p-Numeric->Memory), p->header.size)) ; + ASSERT (p->header.size > 0) ; + ASSERT (p >= Numeric->Memory + Numeric->itail) ; + ASSERT (p < Numeric->Memory + Numeric->size) ; + p->header.size = -p->header.size ; + Row_tuples [row] = 0 ; + } + } + + for (col = 0 ; col < n_col ; col++) + { + if (NON_PIVOTAL_COL (col) && Col_tuples [col]) + { + DEBUG2 (("col "ID" tuples "ID"\n", col, Col_tuples [col])) ; + p = Numeric->Memory + Col_tuples [col] - 1 ; + DEBUG2 (("Freeing tuple list col "ID", p-S "ID", size "ID"\n", + col, (Int) (p-Numeric->Memory), p->header.size)) ; + ASSERT (p->header.size > 0) ; + ASSERT (p >= Numeric->Memory + Numeric->itail) ; + ASSERT (p < Numeric->Memory + Numeric->size) ; + p->header.size = -p->header.size ; + Col_tuples [col] = 0 ; + } + } + + /* ---------------------------------------------------------------------- */ + /* mark the elements, and compress the name space */ + /* ---------------------------------------------------------------------- */ + + nel = Work->nel ; + ASSERT (nel < Work->elen) ; + +#ifndef NDEBUG + nmark = 0 ; + UMF_dump_current_front (Numeric, Work, FALSE) ; + DEBUGm1 (("E [0] "ID" \n", E [0])) ; + ASSERT (IMPLIES (E [0], + Work->Flublock == (Entry *) (Numeric->Memory + E [0]))) ; + ASSERT (IMPLIES (Work->Flublock, + Work->Flublock == (Entry *) (Numeric->Memory + E [0]))) ; + ASSERT ((E [0] != 0) == (Work->Flublock != (Entry *) NULL)) ; +#endif + + e2 = 0 ; + + for (e = 0 ; e <= nel ; e++) /* for all elements in order of creation */ + { + if (E [e]) + { + psrc = Numeric->Memory + E [e] ; + psrc-- ; /* get the header of this block */ + if (e > 0) + { + e2++ ; /* do not renumber element zero */ + } + ASSERT (psrc->header.size > 0) ; + psrc->header.size = e2 ; /* store the new name in the header */ +#ifndef NDEBUG + nmark++ ; +#endif + DEBUG7 ((ID":: Mark e "ID" at psrc-S "ID", new e "ID"\n", + nmark, e, (Int) (psrc-Numeric->Memory), e2)) ; + E [e] = 0 ; + if (e == Work->prior_element) + { + Work->prior_element = e2 ; + } + } + } + + /* all 1..e2 are now in use (element zero may or may not be in use) */ + Work->nel = e2 ; + nel = Work->nel ; + +#ifndef NDEBUG + for (e = 0 ; e < Work->elen ; e++) + { + ASSERT (!E [e]) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* compress the elements */ + /* ---------------------------------------------------------------------- */ + + /* point to tail marker block of size 1 + header */ + psrc = Numeric->Memory + Numeric->size - 2 ; + pdest = psrc ; + prevsize = psrc->header.prevsize ; + DEBUG7 (("Starting the compression:\n")) ; + + while (prevsize > 0) + { + + /* ------------------------------------------------------------------ */ + /* move up to the next element above the current header, and */ + /* get the element name and size */ + /* (if it is an element, the name will be positive) */ + /* ------------------------------------------------------------------ */ + + size = prevsize ; + psrc -= (size + 1) ; + e = psrc->header.size ; + prevsize = psrc->header.prevsize ; + /* top block at tail has prevsize of 0 */ + + /* a free block will have a negative size, so skip it */ + /* otherwise, if size >= 0, it holds the element name, not the size */ + + DEBUG8 (("psrc-S: "ID" prevsize: "ID" size: "ID, + (Int) (psrc-Numeric->Memory), prevsize, size)) ; + + if (e == 0) + { + /* -------------------------------------------------------------- */ + /* this is the current frontal matrix */ + /* -------------------------------------------------------------- */ + + Entry *F1, *F2, *Fsrc, *Fdst ; + Int c, r, k, dr, dc, gap, gap1, gap2, nb ; + + /* shift the frontal matrix down */ + F1 = (Entry *) (psrc + 1) ; + + /* get the size of the current front. r and c could be zero */ + k = Work->fnpiv ; + dr = Work->fnr_curr ; + dc = Work->fnc_curr ; + r = Work->fnrows ; + c = Work->fncols ; + nb = Work->nb ; + + ASSERT ((dr >= 0 && (dr % 2) == 1) || dr == 0) ; + ASSERT (drnew >= 0) ; + if (drnew % 2 == 0) + { + /* make sure leading frontal matrix dimension is always odd */ + drnew++ ; + } + drnew = MIN (dr, drnew) ; + ASSERT ((drnew >= 0 && (drnew % 2) == 1) || drnew == 0) ; + + pnext = pdest ; + +#ifndef NDEBUG + DEBUGm2 (("move front: dr "ID" dc "ID" r "ID" drnew "ID" c "ID + " dcnew " ID" k "ID"\n", dr, dc, r, drnew, c, dcnew, k)) ; + DEBUG7 (("\n")) ; + DEBUG7 ((ID":: Move current frontal matrix from: psrc-S: "ID" \n", + nmark, (Int) (psrc-Numeric->Memory))) ; + nmark-- ; + ASSERT (E [e] == 0) ; + ASSERT (Work->Flublock == F1) ; + ASSERT (Work->Flblock == Work->Flublock + nb*nb) ; + ASSERT (Work->Fublock == Work->Flblock + dr*nb) ; + ASSERT (Work->Fcblock == Work->Fublock + nb*dc) ; + DEBUG7 (("C block: ")) ; + UMF_dump_dense (Work->Fcblock, dr, r, c) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (Work->Flblock, dr, r, k); + DEBUG7 (("U' block: ")) ; + UMF_dump_dense (Work->Fublock, dc, c, k) ; + DEBUG7 (("LU block: ")) ; + UMF_dump_dense (Work->Flublock, nb, k, k) ; + ASSERT (r <= drnew && c <= dcnew && drnew <= dr && dcnew <= dc) ; +#endif + + /* compact frontal matrix to drnew-by-dcnew before moving it */ + + /* do not compact the LU block (nb-by-nb) */ + + /* compact the columns of L (from dr-by-nb to drnew-by-nb) */ + Fsrc = Work->Flblock ; + Fdst = Work->Flblock ; + ASSERT (Fdst == F1 + nb*nb) ; + gap1 = dr - r ; + gap2 = drnew - r ; + ASSERT (gap1 >= 0) ; + for (j = 0 ; j < k ; j++) + { + for (i = 0 ; i < r ; i++) + { + *Fdst++ = *Fsrc++ ; + } + Fsrc += gap1 ; + Fdst += gap2 ; + } + ASSERT (Fdst == F1 + nb*nb + drnew*k) ; + Fdst += drnew * (nb - k) ; + + /* compact the rows of U (U' from dc-by-nb to dcnew-by-nb) */ + Fsrc = Work->Fublock ; + ASSERT (Fdst == F1 + nb*nb + drnew*nb) ; + gap1 = dc - c ; + gap2 = dcnew - c ; + for (i = 0 ; i < k ; i++) + { + for (j = 0 ; j < c ; j++) + { + *Fdst++ = *Fsrc++ ; + } + Fsrc += gap1 ; + Fdst += gap2 ; + } + ASSERT (Fdst == F1 + nb*nb + drnew*nb + dcnew*k) ; + Fdst += dcnew * (nb - k) ; + + /* compact the columns of C (from dr-by-dc to drnew-by-dcnew) */ + Fsrc = Work->Fcblock ; + ASSERT (Fdst == F1 + nb*nb + drnew*nb + nb*dcnew) ; + gap1 = dr - r ; + gap2 = drnew - r ; + for (j = 0 ; j < c ; j++) + { + for (i = 0 ; i < r ; i++) + { + *Fdst++ = *Fsrc++ ; + } + Fsrc += gap1 ; + Fdst += gap2 ; + } + ASSERT (Fdst == F1 + nb*nb + drnew*nb + nb*dcnew + drnew*c) ; + + /* recompute Fcpos, if necessary */ + if (do_Fcpos) + { + Int *Fcols, *Fcpos ; + Fcols = Work->Fcols ; + Fcpos = Work->Fcpos ; + for (j = 0 ; j < c ; j++) + { + col = Fcols [j] ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (Fcpos [col] == j * dr) ; + Fcpos [col] = j * drnew ; + } +#ifndef NDEBUG + { + Int cnt = 0 ; + for (j = 0 ; j < Work->n_col ; j++) + { + if (Fcpos [j] != EMPTY) cnt++ ; + } + DEBUGm2 (("Recompute Fcpos cnt "ID" c "ID"\n", cnt, c)) ; + ASSERT (cnt == c) ; + } +#endif + } + +#ifndef NDEBUG + DEBUGm2 (("Compacted front, drnew "ID" dcnew "ID"\n", drnew, dcnew)) ; + DEBUG7 (("C block: ")) ; + UMF_dump_dense (F1 + nb*nb + drnew*nb + nb*dcnew, drnew, r, c) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (F1 + nb*nb, drnew, r, k) ; + DEBUG7 (("U block: ")) ; + UMF_dump_dense (F1 + nb*nb + drnew*nb, nb, k, c) ; + DEBUG7 (("LU block: ")) ; + UMF_dump_dense (F1, nb, k, k) ; +#endif + + /* Compacted dimensions of the new frontal matrix. */ + Work->fnr_curr = drnew ; + Work->fnc_curr = dcnew ; + Work->fcurr_size = (drnew + nb) * (dcnew + nb) ; + size = UNITS (Entry, Work->fcurr_size) ; + + /* make sure the object doesn't evaporate. The front can have + * zero size (Work->fcurr_size = 0), but the size of the memory + * block containing it cannot have zero size. */ + size = MAX (1, size) ; + + /* get the destination of frontal matrix */ + pnext->header.prevsize = size ; + pdest -= (size + 1) ; + F2 = (Entry *) (pdest + 1) ; + + ASSERT ((unsigned Int) psrc + 1 + size <= (unsigned Int) pnext) ; + ASSERT (psrc <= pdest) ; + ASSERT (F1 <= F2) ; + + /* move the C block first */ + Fsrc = F1 + nb*nb + drnew*nb + nb*dcnew + drnew*c ; + Fdst = F2 + nb*nb + drnew*nb + nb*dcnew + drnew*c ; + gap = drnew - r ; + for (j = c-1 ; j >= 0 ; j--) + { + Fsrc -= gap ; + Fdst -= gap ; + /* move column j of C */ + for (i = r-1 ; i >= 0 ; i--) + { + *--Fdst = *--Fsrc ; + } + } + ASSERT (Fsrc == F1 + nb*nb + drnew*nb + nb*dcnew) ; + ASSERT (Fdst == F2 + nb*nb + drnew*nb + nb*dcnew) ; + + /* move the U block */ + Fsrc -= dcnew * (nb - k) ; + Fdst -= dcnew * (nb - k) ; + ASSERT (Fsrc == F1 + nb*nb + drnew*nb + dcnew*k) ; + ASSERT (Fdst == F2 + nb*nb + drnew*nb + dcnew*k) ; + gap = dcnew - c ; + for (i = k-1 ; i >= 0 ; i--) + { + Fsrc -= gap ; + Fdst -= gap ; + for (j = c-1 ; j >= 0 ; j--) + { + *--Fdst = *--Fsrc ; + } + } + ASSERT (Fsrc == F1 + nb*nb + drnew*nb) ; + ASSERT (Fdst == F2 + nb*nb + drnew*nb) ; + + /* move the L block */ + Fsrc -= drnew * (nb - k) ; + Fdst -= drnew * (nb - k) ; + ASSERT (Fsrc == F1 + nb*nb + drnew*k) ; + ASSERT (Fdst == F2 + nb*nb + drnew*k) ; + gap = drnew - r ; + for (j = k-1 ; j >= 0 ; j--) + { + Fsrc -= gap ; + Fdst -= gap ; + for (i = r-1 ; i >= 0 ; i--) + { + *--Fdst = *--Fsrc ; + } + } + ASSERT (Fsrc == F1 + nb*nb) ; + ASSERT (Fdst == F2 + nb*nb) ; + + /* move the LU block */ + Fsrc -= nb * (nb - k) ; + Fdst -= nb * (nb - k) ; + ASSERT (Fsrc == F1 + nb*k) ; + ASSERT (Fdst == F2 + nb*k) ; + gap = nb - k ; + for (j = k-1 ; j >= 0 ; j--) + { + Fsrc -= gap ; + Fdst -= gap ; + for (i = k-1 ; i >= 0 ; i--) + { + *--Fdst = *--Fsrc ; + } + } + ASSERT (Fsrc == F1) ; + ASSERT (Fdst == F2) ; + + E [0] = (pdest + 1) - Numeric->Memory ; + + Work->Flublock = (Entry *) (Numeric->Memory + E [0]) ; + ASSERT (Work->Flublock == F2) ; + Work->Flblock = Work->Flublock + nb * nb ; + Work->Fublock = Work->Flblock + drnew * nb ; + Work->Fcblock = Work->Fublock + nb * dcnew ; + + pdest->header.prevsize = 0 ; + pdest->header.size = size ; + +#ifndef NDEBUG + DEBUG7 (("After moving compressed current frontal matrix:\n")) ; + DEBUG7 (("C block: ")) ; + UMF_dump_dense (Work->Fcblock, drnew, r, c) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (Work->Flblock, drnew, r, k); + DEBUG7 (("U' block: ")) ; + UMF_dump_dense (Work->Fublock, dcnew, c, k) ; + DEBUG7 (("LU block: ")) ; + UMF_dump_dense (Work->Flublock, nb, k, k) ; +#endif + + } + else if (e > 0) + { + + /* -------------------------------------------------------------- */ + /* this is an element, compress and move from psrc down to pdest */ + /* -------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG7 (("\n")) ; + DEBUG7 ((ID":: Move element "ID": from: "ID" \n", + nmark, e, (Int) (psrc-Numeric->Memory))) ; + nmark-- ; + ASSERT (e <= nel) ; + ASSERT (E [e] == 0) ; +#endif + + /* -------------------------------------------------------------- */ + /* get the element scalars, and pointers to C, Rows, and Cols: */ + /* -------------------------------------------------------------- */ + + p = psrc + 1 ; + GET_ELEMENT (epsrc, p, Cols, Rows, ncols, nrows, C) ; + nrowsleft = epsrc->nrowsleft ; + ncolsleft = epsrc->ncolsleft ; + cdeg = epsrc->cdeg ; + rdeg = epsrc->rdeg ; + +#ifndef NDEBUG + DEBUG7 ((" nrows "ID" nrowsleft "ID"\n", nrows, nrowsleft)) ; + DEBUG7 ((" ncols "ID" ncolsleft "ID"\n", ncols, ncolsleft)) ; + DEBUG8 ((" Rows:")) ; + for (i = 0 ; i < nrows ; i++) DEBUG8 ((" "ID, Rows [i])) ; + DEBUG8 (("\n Cols:")) ; + for (j = 0 ; j < ncols ; j++) DEBUG8 ((" "ID, Cols [j])) ; + DEBUG8 (("\n")) ; +#endif + + /* -------------------------------------------------------------- */ + /* determine the layout of the new element */ + /* -------------------------------------------------------------- */ + + csize = nrowsleft * ncolsleft ; + size2 = UNITS (Element, 1) + + UNITS (Int, nrowsleft + ncolsleft) + + UNITS (Entry, csize) ; + + DEBUG7 (("Old size "ID" New size "ID"\n", size, size2)) ; + + pnext = pdest ; + pnext->header.prevsize = size2 ; + pdest -= (size2 + 1) ; + + ASSERT (size2 <= size) ; + ASSERT ((unsigned Int) psrc + 1 + size <= (unsigned Int) pnext) ; + ASSERT (psrc <= pdest) ; + + p = pdest + 1 ; + epdest = (Element *) p ; + p += UNITS (Element, 1) ; + Cols2 = (Int *) p ; + Rows2 = Cols2 + ncolsleft ; + p += UNITS (Int, nrowsleft + ncolsleft) ; + C2 = (Entry *) p ; + + ASSERT (epdest >= epsrc) ; + ASSERT (Rows2 >= Rows) ; + ASSERT (Cols2 >= Cols) ; + ASSERT (C2 >= C) ; + ASSERT (p + UNITS (Entry, csize) == pnext) ; + + /* -------------------------------------------------------------- */ + /* move the contribution block */ + /* -------------------------------------------------------------- */ + + /* overlap = psrc + size + 1 > pdest ; */ + + if (nrowsleft < nrows || ncolsleft < ncols) + { + + /* ---------------------------------------------------------- */ + /* compress contribution block in place prior to moving it */ + /* ---------------------------------------------------------- */ + + DEBUG7 (("Compress C in place prior to move:\n")); +#ifndef NDEBUG + UMF_dump_dense (C, nrows, nrows, ncols) ; +#endif + C1 = C ; + C3 = C ; + for (j = 0 ; j < ncols ; j++) + { + if (Cols [j] >= 0) + { + for (i = 0 ; i < nrows ; i++) + { + if (Rows [i] >= 0) + { + *C3++ = C1 [i] ; + } + } + } + C1 += nrows ; + } + ASSERT (C3-C == csize) ; + DEBUG8 (("Newly compressed contrib. block (all in use):\n")) ; +#ifndef NDEBUG + UMF_dump_dense (C, nrowsleft, nrowsleft, ncolsleft) ; +#endif + } + + /* shift the contribution block down */ + C += csize ; + C2 += csize ; + for (i = 0 ; i < csize ; i++) + { + *--C2 = *--C ; + } + + /* -------------------------------------------------------------- */ + /* move the row indices */ + /* -------------------------------------------------------------- */ + + i2 = nrowsleft ; + for (i = nrows - 1 ; i >= 0 ; i--) + { + ASSERT (Rows2+i2 >= Rows+i) ; + if (Rows [i] >= 0) + { + Rows2 [--i2] = Rows [i] ; + } + } + ASSERT (i2 == 0) ; + + j2 = ncolsleft ; + for (j = ncols - 1 ; j >= 0 ; j--) + { + ASSERT (Cols2+j2 >= Cols+j) ; + if (Cols [j] >= 0) + { + Cols2 [--j2] = Cols [j] ; + } + } + ASSERT (j2 == 0) ; + + /* -------------------------------------------------------------- */ + /* construct the new header */ + /* -------------------------------------------------------------- */ + + /* E [0...e] is now valid */ + E [e] = (pdest + 1) - Numeric->Memory ; + epdest = (Element *) (pdest + 1) ; + + epdest->next = EMPTY ; /* destroys the son list */ + epdest->ncols = ncolsleft ; + epdest->nrows = nrowsleft ; + epdest->ncolsleft = ncolsleft ; + epdest->nrowsleft = nrowsleft ; + epdest->rdeg = rdeg ; + epdest->cdeg = cdeg ; + + ASSERT (size2 <= size) ; + pdest->header.prevsize = 0 ; + pdest->header.size = size2 ; + + DEBUG7 (("After moving it:\n")) ; +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + } + +#ifndef NDEBUG + else + { + DEBUG8 ((" free\n")) ; + } +#endif + DEBUG7 (("psrc "ID" tail "ID"\n", + (Int) (psrc-Numeric->Memory), Numeric->itail)) ; + } + + ASSERT (psrc == Numeric->Memory + Numeric->itail) ; + ASSERT (nmark == 0) ; + + /* ---------------------------------------------------------------------- */ + /* final tail pointer */ + /* ---------------------------------------------------------------------- */ + + ASSERT (pdest >= Numeric->Memory + Numeric->itail) ; + Numeric->itail = pdest - Numeric->Memory ; + pdest->header.prevsize = 0 ; + Numeric->ibig = EMPTY ; + Numeric->tail_usage = Numeric->size - Numeric->itail ; + + /* ---------------------------------------------------------------------- */ + /* clear the unused E [nel+1 .. Work->elen - 1] */ + /* ---------------------------------------------------------------------- */ + + for (e = nel+1 ; e < Work->elen ; e++) + { + E [e] = 0 ; + } + +#ifndef NDEBUG + UMF_dump_packed_memory (Numeric, Work) ; +#endif + + DEBUG8 (("::::GARBAGE COLLECTION DONE::::\n")) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_garbage_collection.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_garbage_collection.h new file mode 100644 index 0000000000..29d9186cf7 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_garbage_collection.h @@ -0,0 +1,15 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_garbage_collection +( + NumericType *Numeric, + WorkType *Work, + Int drnew, + Int dcnew, + Int do_Fcpos +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_get_memory.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_get_memory.c new file mode 100644 index 0000000000..480288b2df --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_get_memory.c @@ -0,0 +1,223 @@ +/* ========================================================================== */ +/* === UMF_get_memory ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Reallocate the workspace (Numeric->Memory) and shift elements downwards. + needunits: increase in size so that the free space is at least this many + Units (to which the tuple lengths is added). + + Return TRUE if successful, FALSE if out of memory. +*/ + +#include "umf_internal.h" +#include "umf_garbage_collection.h" +#include "umf_tuple_lengths.h" +#include "umf_build_tuples.h" +#include "umf_mem_free_tail_block.h" +#include "umf_realloc.h" + +GLOBAL Int UMF_get_memory +( + NumericType *Numeric, + WorkType *Work, + Int needunits, + Int r2, /* compact current front to r2-by-c2 */ + Int c2, + Int do_Fcpos +) +{ + Int i, minsize, newsize, newmem, costly, row, col, *Row_tlen, *Col_tlen, + n_row, n_col, *Row_degree, *Col_degree ; + Unit *mnew, *p ; + double nsize, bsize, tsize ; + + /* ---------------------------------------------------------------------- */ + /* get and check parameters */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG1 (("::::GET MEMORY::::\n")) ; + UMF_dump_memory (Numeric) ; +#endif + + n_row = Work->n_row ; + n_col = Work->n_col ; + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_tlen = Numeric->Uilen ; + Col_tlen = Numeric->Lilen ; + + /* ---------------------------------------------------------------------- */ + /* initialize the tuple list lengths */ + /* ---------------------------------------------------------------------- */ + + for (row = 0 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row)) + { + Row_tlen [row] = 0 ; + } + } + for (col = 0 ; col < n_col ; col++) + { + if (NON_PIVOTAL_COL (col)) + { + Col_tlen [col] = 0 ; + } + } + + /* ---------------------------------------------------------------------- */ + /* determine how much memory is needed for the tuples */ + /* ---------------------------------------------------------------------- */ + + nsize = (double) needunits + 2 ; + needunits += UMF_tuple_lengths (Numeric, Work, &tsize) ; + nsize += tsize ; + needunits += 2 ; /* add 2, so that newmem >= 2 is true if realloc'd */ + + /* note: Col_tlen and Row_tlen are updated, but the tuple lists */ + /* themselves are not. Do not attempt to scan the tuple lists. */ + /* They are now stale, and are about to be destroyed and recreated. */ + + /* ---------------------------------------------------------------------- */ + /* determine the desired new size of memory */ + /* ---------------------------------------------------------------------- */ + + DEBUG0 (("UMF_get_memory: needunits: "ID"\n", needunits)) ; + + minsize = Numeric->size + needunits ; + nsize += (double) Numeric->size ; + + bsize = ((double) Int_MAX) / sizeof (Unit) - 1 ; + + newsize = (Int) (UMF_REALLOC_INCREASE * ((double) minsize)) ; + nsize *= UMF_REALLOC_INCREASE ; + nsize += 1 ; + + if (newsize < 0 || nsize > bsize) + { + /* :: realloc Numeric->Memory int overflow :: */ + DEBUGm3 (("Realloc hit integer limit\n")) ; + newsize = (Int) bsize ; /* we cannot increase the size beyond bsize */ + } + else + { + ASSERT (newsize <= nsize) ; + newsize = MAX (newsize, minsize) ; + } + newsize = MAX (newsize, Numeric->size) ; + + DEBUG0 (( + "REALLOC MEMORY: needunits "ID" old size: "ID" new size: "ID" Units \n", + needunits, Numeric->size, newsize)) ; + + /* Forget where the biggest free block is (we no longer need it) */ + /* since garbage collection will occur shortly. */ + Numeric->ibig = EMPTY ; + + DEBUG0 (("Before realloc E [0] "ID"\n", Work->E [0])) ; + + /* ---------------------------------------------------------------------- */ + /* reallocate the memory, if possible, and make it bigger */ + /* ---------------------------------------------------------------------- */ + + mnew = (Unit *) NULL ; + while (!mnew) + { + mnew = (Unit *) UMF_realloc (Numeric->Memory, newsize, sizeof (Unit)) ; + if (!mnew) + { + if (newsize == minsize) /* last realloc attempt failed */ + { + /* We failed to get the minimum. Just stick with the */ + /* current allocation and hope that garbage collection */ + /* can recover enough space. */ + mnew = Numeric->Memory ; /* no new memory available */ + newsize = Numeric->size ; + } + else + { + /* otherwise, reduce the request and keep trying */ + newsize = (Int) (UMF_REALLOC_REDUCTION * ((double) newsize)) ; + newsize = MAX (minsize, newsize) ; + } + } + } + ASSERT (mnew != (Unit *) NULL) ; + + /* see if realloc had to copy, rather than just extend memory */ + costly = (mnew != Numeric->Memory) ; + + /* ---------------------------------------------------------------------- */ + /* extend the tail portion of memory downwards */ + /* ---------------------------------------------------------------------- */ + + Numeric->Memory = mnew ; + if (Work->E [0]) + { + Int nb, dr, dc ; + nb = Work->nb ; + dr = Work->fnr_curr ; + dc = Work->fnc_curr ; + Work->Flublock = (Entry *) (Numeric->Memory + Work->E [0]) ; + Work->Flblock = Work->Flublock + nb * nb ; + Work->Fublock = Work->Flblock + dr * nb ; + Work->Fcblock = Work->Fublock + nb * dc ; + DEBUG0 (("after realloc E [0] "ID"\n", Work->E [0])) ; + } + ASSERT (IMPLIES (!(Work->E [0]), Work->Flublock == (Entry *) NULL)) ; + + newmem = newsize - Numeric->size ; + ASSERT (newmem == 0 || newmem >= 2) ; + + if (newmem >= 2) + { + /* reallocation succeeded */ + + /* point to the old tail marker block of size 1 + header */ + p = Numeric->Memory + Numeric->size - 2 ; + + /* create a new block out of the newly extended memory */ + p->header.size = newmem - 1 ; + i = Numeric->size - 1 ; + p += newmem ; + + /* create a new tail marker block */ + p->header.prevsize = newmem - 1 ; + p->header.size = 1 ; + + Numeric->size = newsize ; + + /* free the new block */ + UMF_mem_free_tail_block (Numeric, i) ; + + Numeric->nrealloc++ ; + + if (costly) + { + Numeric->ncostly++ ; + } + + } + DEBUG1 (("Done with realloc memory\n")) ; + + /* ---------------------------------------------------------------------- */ + /* garbage collection on the tail of Numeric->memory (destroys tuples) */ + /* ---------------------------------------------------------------------- */ + + UMF_garbage_collection (Numeric, Work, r2, c2, do_Fcpos) ; + + /* ---------------------------------------------------------------------- */ + /* rebuild the tuples */ + /* ---------------------------------------------------------------------- */ + + return (UMF_build_tuples (Numeric, Work)) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_get_memory.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_get_memory.h new file mode 100644 index 0000000000..754f1faced --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_get_memory.h @@ -0,0 +1,16 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_get_memory +( + NumericType *Numeric, + WorkType *Work, + Int needunits, + Int r2, + Int c2, + Int do_Fcpos +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_grow_front.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_grow_front.c new file mode 100644 index 0000000000..9ecf67388a --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_grow_front.c @@ -0,0 +1,295 @@ +/* ========================================================================== */ +/* === UMF_grow_front ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Current frontal matrix is too small. Make it bigger. */ + +#include "umf_internal.h" +#include "umf_realloc.h" +#include "umf_mem_free_tail_block.h" +#include "umf_mem_alloc_tail_block.h" +#include "umf_get_memory.h" + +GLOBAL Int UMF_grow_front +( + NumericType *Numeric, + Int fnr2, /* desired size is fnr2-by-fnc2 */ + Int fnc2, + WorkType *Work, + Int do_what /* -1: UMF_start_front + * 0: UMF_init_front, do not recompute Fcpos + * 1: UMF_extend_front + * 2: UMF_init_front, recompute Fcpos */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Entry *Fcold, *Fcnew ; + Int j, i, col, *Fcpos, *Fcols, fnrows_max, fncols_max, fnr_curr, nb, + fnrows_new, fncols_new, fnr_min, fnc_min, minsize, + newsize, fnrows, fncols, *E, eloc ; + double s ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + if (do_what != -1) UMF_debug++ ; + DEBUG0 (("\n\n====================GROW FRONT: do_what: "ID"\n", do_what)) ; + if (do_what != -1) UMF_debug-- ; + ASSERT (Work->do_grow) ; + ASSERT (Work->fnpiv == 0) ; +#endif + + Fcols = Work->Fcols ; + Fcpos = Work->Fcpos ; + E = Work->E ; + + /* ---------------------------------------------------------------------- */ + /* The current front is too small, find the new size */ + /* ---------------------------------------------------------------------- */ + + /* maximum size of frontal matrix for this chain */ + nb = Work->nb ; + fnrows_max = Work->fnrows_max + nb ; + fncols_max = Work->fncols_max + nb ; + ASSERT (fnrows_max >= 0 && (fnrows_max % 2) == 1) ; + DEBUG0 (("Max size: "ID"-by-"ID" (incl. "ID" pivot block\n", + fnrows_max, fncols_max, nb)) ; + + /* current dimensions of frontal matrix: fnr-by-fnc */ + DEBUG0 (("Current : "ID"-by-"ID" (excl "ID" pivot blocks)\n", + Work->fnr_curr, Work->fnc_curr, nb)) ; + ASSERT (Work->fnr_curr >= 0) ; + ASSERT ((Work->fnr_curr % 2 == 1) || Work->fnr_curr == 0) ; + + /* required dimensions of frontal matrix: fnr_min-by-fnc_min */ + fnrows_new = Work->fnrows_new + 1 ; + fncols_new = Work->fncols_new + 1 ; + ASSERT (fnrows_new >= 0) ; + if (fnrows_new % 2 == 0) fnrows_new++ ; + fnrows_new += nb ; + fncols_new += nb ; + fnr_min = MIN (fnrows_new, fnrows_max) ; + fnc_min = MIN (fncols_new, fncols_max) ; + minsize = fnr_min * fnc_min ; + if (INT_OVERFLOW ((double) fnr_min * (double) fnc_min * sizeof (Entry))) + { + /* :: the minimum front size is bigger than the integer maximum :: */ + return (FALSE) ; + } + ASSERT (fnr_min >= 0) ; + ASSERT (fnr_min % 2 == 1) ; + + DEBUG0 (("Min : "ID"-by-"ID"\n", fnr_min, fnc_min)) ; + + /* grow the front to fnr2-by-fnc2, but no bigger than the maximum, + * and no smaller than the minumum. */ + DEBUG0 (("Desired : ("ID"+"ID")-by-("ID"+"ID")\n", fnr2, nb, fnc2, nb)) ; + fnr2 += nb ; + fnc2 += nb ; + ASSERT (fnr2 >= 0) ; + if (fnr2 % 2 == 0) fnr2++ ; + fnr2 = MAX (fnr2, fnr_min) ; + fnc2 = MAX (fnc2, fnc_min) ; + fnr2 = MIN (fnr2, fnrows_max) ; + fnc2 = MIN (fnc2, fncols_max) ; + DEBUG0 (("Try : "ID"-by-"ID"\n", fnr2, fnc2)) ; + ASSERT (fnr2 >= 0) ; + ASSERT (fnr2 % 2 == 1) ; + + s = ((double) fnr2) * ((double) fnc2) ; + if (INT_OVERFLOW (s * sizeof (Entry))) + { + /* :: frontal matrix size int overflow :: */ + /* the desired front size is bigger than the integer maximum */ + /* compute a such that a*a*s < Int_MAX / sizeof (Entry) */ + double a = 0.9 * sqrt ((Int_MAX / sizeof (Entry)) / s) ; + fnr2 = MAX (fnr_min, a * fnr2) ; + fnc2 = MAX (fnc_min, a * fnc2) ; + /* the new frontal size is a*r*a*c = a*a*s */ + newsize = fnr2 * fnc2 ; + ASSERT (fnr2 >= 0) ; + if (fnr2 % 2 == 0) fnr2++ ; + fnc2 = newsize / fnr2 ; + } + + fnr2 = MAX (fnr2, fnr_min) ; + fnc2 = MAX (fnc2, fnc_min) ; + newsize = fnr2 * fnc2 ; + + ASSERT (fnr2 >= 0) ; + ASSERT (fnr2 % 2 == 1) ; + ASSERT (fnr2 >= fnr_min) ; + ASSERT (fnc2 >= fnc_min) ; + ASSERT (newsize >= minsize) ; + + /* ---------------------------------------------------------------------- */ + /* free the current front if it is empty of any numerical values */ + /* ---------------------------------------------------------------------- */ + + if (E [0] && do_what != 1) + { + /* free the current front, if it exists and has nothing in it */ + DEBUG0 (("Freeing empty front\n")) ; + UMF_mem_free_tail_block (Numeric, E [0]) ; + E [0] = 0 ; + Work->Flublock = (Entry *) NULL ; + Work->Flblock = (Entry *) NULL ; + Work->Fublock = (Entry *) NULL ; + Work->Fcblock = (Entry *) NULL ; + } + + /* ---------------------------------------------------------------------- */ + /* allocate the new front, doing garbage collection if necessary */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) /* a double relop, but ignore NaN case */ + { + double rrr = ((double) (rand ( ))) / (((double) RAND_MAX) + 1) ; + DEBUG1 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; + if (UMF_allocfail) DEBUGm2 (("Random garbage collection (grow)\n")) ; + } +#endif + + DEBUG0 (("Attempt size: "ID"-by-"ID"\n", fnr2, fnc2)) ; + eloc = UMF_mem_alloc_tail_block (Numeric, UNITS (Entry, newsize)) ; + + if (!eloc) + { + /* Do garbage collection, realloc, and try again. Compact the current + * contribution block in the front to fnrows-by-fncols. Note that + * there are no pivot rows/columns in current front. Do not recompute + * Fcpos in UMF_garbage_collection. */ + DEBUGm3 (("get_memory from umf_grow_front\n")) ; + if (!UMF_get_memory (Numeric, Work, 1 + UNITS (Entry, newsize), + Work->fnrows, Work->fncols, FALSE)) + { + /* :: out of memory in umf_grow_front :: */ + return (FALSE) ; /* out of memory */ + } + DEBUG0 (("Attempt size: "ID"-by-"ID" again\n", fnr2, fnc2)) ; + eloc = UMF_mem_alloc_tail_block (Numeric, UNITS (Entry, newsize)) ; + } + + /* try again with something smaller */ + while ((fnr2 != fnr_min || fnc2 != fnc_min) && !eloc) + { + fnr2 = MIN (fnr2 - 2, fnr2 * UMF_REALLOC_REDUCTION) ; + fnc2 = MIN (fnc2 - 2, fnc2 * UMF_REALLOC_REDUCTION) ; + ASSERT (fnr_min >= 0) ; + ASSERT (fnr_min % 2 == 1) ; + fnr2 = MAX (fnr_min, fnr2) ; + fnc2 = MAX (fnc_min, fnc2) ; + ASSERT (fnr2 >= 0) ; + if (fnr2 % 2 == 0) fnr2++ ; + newsize = fnr2 * fnc2 ; + DEBUGm3 (("Attempt smaller size: "ID"-by-"ID" minsize "ID"-by-"ID"\n", + fnr2, fnc2, fnr_min, fnc_min)) ; + eloc = UMF_mem_alloc_tail_block (Numeric, UNITS (Entry, newsize)) ; + } + + /* try again with the smallest possible size */ + if (!eloc) + { + fnr2 = fnr_min ; + fnc2 = fnc_min ; + newsize = minsize ; + DEBUG0 (("Attempt minsize: "ID"-by-"ID"\n", fnr2, fnc2)) ; + eloc = UMF_mem_alloc_tail_block (Numeric, UNITS (Entry, newsize)) ; + } + + if (!eloc) + { + /* out of memory */ + return (FALSE) ; + } + + ASSERT (fnr2 >= 0) ; + ASSERT (fnr2 % 2 == 1) ; + ASSERT (fnr2 >= fnr_min && fnc2 >= fnc_min) ; + + /* ---------------------------------------------------------------------- */ + /* copy the old frontal matrix into the new one */ + /* ---------------------------------------------------------------------- */ + + /* old contribution block (if any) */ + fnr_curr = Work->fnr_curr ; /* garbage collection can change fn*_curr */ + ASSERT (fnr_curr >= 0) ; + ASSERT ((fnr_curr % 2 == 1) || fnr_curr == 0) ; + fnrows = Work->fnrows ; + fncols = Work->fncols ; + Fcold = Work->Fcblock ; + + /* remove nb from the sizes */ + fnr2 -= nb ; + fnc2 -= nb ; + + /* new frontal matrix */ + Work->Flublock = (Entry *) (Numeric->Memory + eloc) ; + Work->Flblock = Work->Flublock + nb * nb ; + Work->Fublock = Work->Flblock + nb * fnr2 ; + Work->Fcblock = Work->Fublock + nb * fnc2 ; + Fcnew = Work->Fcblock ; + + if (E [0]) + { + /* copy the old contribution block into the new one */ + for (j = 0 ; j < fncols ; j++) + { + col = Fcols [j] ; + DEBUG1 (("copy col "ID" \n",col)) ; + ASSERT (col >= 0 && col < Work->n_col) ; + for (i = 0 ; i < fnrows ; i++) + { + Fcnew [i] = Fcold [i] ; + } + Fcnew += fnr2 ; + Fcold += fnr_curr ; + DEBUG1 (("new offset col "ID" "ID"\n",col, j * fnr2)) ; + Fcpos [col] = j * fnr2 ; + } + } + else if (do_what == 2) + { + /* just find the new column offsets */ + for (j = 0 ; j < fncols ; j++) + { + col = Fcols [j] ; + DEBUG1 (("new offset col "ID" "ID"\n",col, j * fnr2)) ; + Fcpos [col] = j * fnr2 ; + } + } + + /* free the old frontal matrix */ + UMF_mem_free_tail_block (Numeric, E [0]) ; + + /* ---------------------------------------------------------------------- */ + /* new frontal matrix size */ + /* ---------------------------------------------------------------------- */ + + E [0] = eloc ; + Work->fnr_curr = fnr2 ; /* C block is fnr2-by-fnc2 */ + Work->fnc_curr = fnc2 ; + Work->fcurr_size = newsize ; /* including LU, L, U, and C blocks */ + Work->do_grow = FALSE ; /* the front has just been grown */ + + ASSERT (Work->fnr_curr >= 0) ; + ASSERT (Work->fnr_curr % 2 == 1) ; + DEBUG0 (("Newly grown front: "ID"+"ID" by "ID"+"ID"\n", Work->fnr_curr, + nb, Work->fnc_curr, nb)) ; + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_grow_front.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_grow_front.h new file mode 100644 index 0000000000..2f27fe52d3 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_grow_front.h @@ -0,0 +1,15 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_grow_front +( + NumericType *Numeric, + Int fnr2, + Int fnc2, + WorkType *Work, + Int do_what +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_init_front.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_init_front.c new file mode 100644 index 0000000000..cce2dd1979 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_init_front.c @@ -0,0 +1,268 @@ +/* ========================================================================== */ +/* === UMF_init_front ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" +#include "umf_grow_front.h" + +/* ========================================================================== */ +/* === zero_init_front ====================================================== */ +/* ========================================================================== */ + +/* Set the initial frontal matrix to zero. */ + +PRIVATE void zero_init_front (Int m, Int n, Entry *Fcblock, Int d) +{ + Int i, j ; + Entry *F, *Fj = Fcblock ; + for (j = 0 ; j < m ; j++) + { + F = Fj ; + Fj += d ; + for (i = 0 ; i < n ; i++) + { + /* CLEAR (Fcblock [i + j*d]) ; */ + CLEAR (*F) ; + F++ ; + } + } +} + +/* ========================================================================== */ +/* === UMF_init_front ======================================================= */ +/* ========================================================================== */ + +GLOBAL Int UMF_init_front +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int i, j, fnr_curr, row, col, *Frows, *Fcols, + *Fcpos, *Frpos, fncols, fnrows, *Wrow, fnr2, fnc2, rrdeg, ccdeg, *Wm, + fnrows_extended ; + Entry *Fcblock, *Fl, *Wy, *Wx ; + + /* ---------------------------------------------------------------------- */ + /* get current frontal matrix and check for frontal growth */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG0 (("INIT FRONT\n")) ; + DEBUG1 (("CURR before init:\n")) ; + UMF_dump_current_front (Numeric, Work, FALSE) ; +#endif + if (Work->do_grow) + { + fnr2 = UMF_FRONTAL_GROWTH * Work->fnrows_new + 2 ; + fnc2 = UMF_FRONTAL_GROWTH * Work->fncols_new + 2 ; + if (!UMF_grow_front (Numeric, fnr2, fnc2, Work, + Work->pivrow_in_front ? 2 : 0)) + { + /* :: out of memory in umf_init_front :: */ + DEBUGm4 (("out of memory: init front\n")) ; + return (FALSE) ; + } + } +#ifndef NDEBUG + DEBUG1 (("CURR after grow:\n")) ; + UMF_dump_current_front (Numeric, Work, FALSE) ; + DEBUG1 (("fnrows new "ID" fncols new "ID"\n", + Work->fnrows_new, Work->fncols_new)) ; +#endif + ASSERT (Work->fnpiv == 0) ; + fnr_curr = Work->fnr_curr ; + ASSERT (Work->fnrows_new + 1 <= fnr_curr) ; + ASSERT (Work->fncols_new + 1 <= Work->fnc_curr) ; + ASSERT (fnr_curr >= 0) ; + ASSERT (fnr_curr % 2 == 1) ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + /* current front is defined by pivot row and column */ + + Frows = Work->Frows ; + Fcols = Work->Fcols ; + Frpos = Work->Frpos ; + Fcpos = Work->Fcpos ; + + Work->fnzeros = 0 ; + + ccdeg = Work->ccdeg ; + rrdeg = Work->rrdeg ; + + fnrows = Work->fnrows ; + fncols = Work->fncols ; + + /* if both pivrow and pivcol are in front, then we extend the old one */ + /* in UMF_extend_front, rather than starting a new one here. */ + ASSERT (! (Work->pivrow_in_front && Work->pivcol_in_front)) ; + + /* ---------------------------------------------------------------------- */ + /* place pivot column pattern in frontal matrix */ + /* ---------------------------------------------------------------------- */ + + Fl = Work->Flblock ; + + if (Work->pivcol_in_front) + { + /* Append the pivot column extension. + * Note that all we need to do is increment the size, since the + * candidate pivot column pattern is already in place in + * Frows [0 ... fnrows-1] (the old pattern), and + * Frows [fnrows ... fnrows + Work->ccdeg - 1] (the new + * pattern). Frpos is also properly defined. */ + /* make a list of the new rows to scan */ + Work->fscan_row = fnrows ; /* only scan the new rows */ + Work->NewRows = Work->Wrp ; + Wy = Work->Wy ; + for (i = 0 ; i < fnrows ; i++) + { + Fl [i] = Wy [i] ; + } + fnrows_extended = fnrows + ccdeg ; + for (i = fnrows ; i < fnrows_extended ; i++) + { + Fl [i] = Wy [i] ; + /* flip the row index, since Wrp must be < 0 */ + row = Frows [i] ; + Work->NewRows [i] = FLIP (row) ; + } + fnrows = fnrows_extended ; + } + else + { + /* this is a completely new column */ + Work->fscan_row = 0 ; /* scan all the rows */ + Work->NewRows = Frows ; + Wm = Work->Wm ; + Wx = Work->Wx ; + for (i = 0 ; i < ccdeg ; i++) + { + Fl [i] = Wx [i] ; + row = Wm [i] ; + Frows [i] = row ; + Frpos [row] = i ; + } + fnrows = ccdeg ; + } + + Work->fnrows = fnrows ; + +#ifndef NDEBUG + DEBUG3 (("New Pivot col "ID" now in front, length "ID"\n", + Work->pivcol, fnrows)) ; + for (i = 0 ; i < fnrows ; i++) + { + DEBUG4 ((" "ID": row "ID"\n", i, Frows [i])) ; + ASSERT (Frpos [Frows [i]] == i) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* place pivot row pattern in frontal matrix */ + /* ---------------------------------------------------------------------- */ + + Wrow = Work->Wrow ; + if (Work->pivrow_in_front) + { + /* append the pivot row extension */ + Work->fscan_col = fncols ; /* only scan the new columns */ + Work->NewCols = Work->Wp ; +#ifndef NDEBUG + for (j = 0 ; j < fncols ; j++) + { + col = Fcols [j] ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (Fcpos [col] == j * fnr_curr) ; + } +#endif + /* Wrow == Fcol for the IN_IN case, and for the OUT_IN case when + * the pivrow [IN][IN] happens to be the same as pivrow [OUT][IN]. + * See UMF_local_search for more details. */ + ASSERT (IMPLIES (Work->pivcol_in_front, Wrow == Fcols)) ; + if (Wrow == Fcols) + { + for (j = fncols ; j < rrdeg ; j++) + { + col = Wrow [j] ; + /* Fcols [j] = col ; not needed */ + /* flip the col index, since Wp must be < 0 */ + Work->NewCols [j] = FLIP (col) ; + Fcpos [col] = j * fnr_curr ; + } + } + else + { + for (j = fncols ; j < rrdeg ; j++) + { + col = Wrow [j] ; + Fcols [j] = col ; + /* flip the col index, since Wp must be < 0 */ + Work->NewCols [j] = FLIP (col) ; + Fcpos [col] = j * fnr_curr ; + } + } + } + else + { + /* this is a completely new row */ + Work->fscan_col = 0 ; /* scan all the columns */ + Work->NewCols = Fcols ; + for (j = 0 ; j < rrdeg ; j++) + { + col = Wrow [j] ; + Fcols [j] = col ; + Fcpos [col] = j * fnr_curr ; + } + } + + DEBUGm1 (("rrdeg "ID" fncols "ID"\n", rrdeg, fncols)) ; + fncols = rrdeg ; + Work->fncols = fncols ; + + /* ---------------------------------------------------------------------- */ + /* clear the frontal matrix */ + /* ---------------------------------------------------------------------- */ + + ASSERT (fnrows == Work->fnrows_new + 1) ; + ASSERT (fncols == Work->fncols_new + 1) ; + + Fcblock = Work->Fcblock ; + ASSERT (Fcblock != (Entry *) NULL) ; + + zero_init_front (fncols, fnrows, Fcblock, fnr_curr) ; + +#ifndef NDEBUG + DEBUG3 (("New Pivot row "ID" now in front, length "ID" fnr_curr "ID"\n", + Work->pivrow, fncols, fnr_curr)) ; + for (j = 0 ; j < fncols ; j++) + { + DEBUG4 (("col "ID" position "ID"\n", j, Fcols [j])) ; + ASSERT (Fcpos [Fcols [j]] == j * fnr_curr) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* current workspace usage: */ + /* ---------------------------------------------------------------------- */ + + /* Fcblock [0..fnr_curr-1, 0..fnc_curr-1]: space for the new frontal + * matrix. Fcblock (i,j) is located at Fcblock [i+j*fnr_curr] */ + + return (TRUE) ; + +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_init_front.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_init_front.h new file mode 100644 index 0000000000..96a77ab66d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_init_front.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_init_front +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_internal.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_internal.h new file mode 100644 index 0000000000..f4e26c2f80 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_internal.h @@ -0,0 +1,756 @@ +/* ========================================================================== */ +/* === umf_internal.h ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + This file is for internal use in UMFPACK itself, and should not be included + in user code. Use umfpack.h instead. User-accessible file names and + routine names all start with the letters "umfpack_". Non-user-accessible + file names and routine names all start with "umf_". +*/ + +/* -------------------------------------------------------------------------- */ +/* ANSI standard include files */ +/* -------------------------------------------------------------------------- */ + +/* from float.h: DBL_EPSILON */ +#include + +/* from string.h: strcmp */ +#include + +/* when debugging, assert.h and the assert macro are used (see umf_dump.h) */ + +/* -------------------------------------------------------------------------- */ +/* Architecture */ +/* -------------------------------------------------------------------------- */ + +#if defined (__sun) || defined (MSOL2) || defined (ARCH_SOL2) +#define UMF_SOL2 +#define UMFPACK_ARCHITECTURE "Sun Solaris" + +#elif defined (__sgi) || defined (MSGI) || defined (ARCH_SGI) +#define UMF_SGI +#define UMFPACK_ARCHITECTURE "SGI Irix" + +#elif defined (__linux) || defined (MGLNX86) || defined (ARCH_GLNX86) +#define UMF_LINUX +#define UMFPACK_ARCHITECTURE "Linux" + +#elif defined (_AIX) || defined (MIBM_RS) || defined (ARCH_IBM_RS) +#define UMF_AIX +#define UMFPACK_ARCHITECTURE "IBM AIX" + +#elif defined (__alpha) || defined (MALPHA) || defined (ARCH_ALPHA) +#define UMF_ALPHA +#define UMFPACK_ARCHITECTURE "Compaq Alpha" + +#elif defined (__WIN32) || defined (_WIN32) || defined (_win32) || defined (__win32) || defined (WIN32) +#define UMF_WINDOWS +#define UMFPACK_ARCHITECTURE "Microsoft Windows" + +#elif defined (__hppa) || defined (__hpux) || defined (MHPUX) || defined (ARCH_HPUX) +#define UMF_HP +#define UMFPACK_ARCHITECTURE "HP Unix" + +#elif defined (__hp700) || defined (MHP700) || defined (ARCH_HP700) +#define UMF_HP +#define UMFPACK_ARCHITECTURE "HP 700 Unix" + +#else +/* If the architecture is unknown, and you call the BLAS, you may need to */ +/* define BLAS_BY_VALUE, BLAS_NO_UNDERSCORE, and/or BLAS_CHAR_ARG yourself. */ +#define UMFPACK_ARCHITECTURE "unknown" +#endif + + +/* -------------------------------------------------------------------------- */ +/* basic definitions (see also amd_internal.h) */ +/* -------------------------------------------------------------------------- */ + +#define ONES_COMPLEMENT(r) (-(r)-1) + +/* -------------------------------------------------------------------------- */ +/* AMD include file */ +/* -------------------------------------------------------------------------- */ + +/* stdio.h, stdlib.h, limits.h, and math.h, NDEBUG definition, + * assert.h, and MATLAB include files */ +#include "amd_internal.h" + +/* -------------------------------------------------------------------------- */ +/* Real/complex and int/long definitions, double relops */ +/* -------------------------------------------------------------------------- */ + +#include "umf_version.h" + +/* -------------------------------------------------------------------------- */ +/* Compile-time configurations */ +/* -------------------------------------------------------------------------- */ + +#include "umf_config.h" + +/* -------------------------------------------------------------------------- */ +/* umfpack include file */ +/* -------------------------------------------------------------------------- */ + +#include "umfpack.h" + +/* -------------------------------------------------------------------------- */ +/* for contents of Info. This must correlate with umfpack.h */ +/* -------------------------------------------------------------------------- */ + +#define ESTIMATE (UMFPACK_NUMERIC_SIZE_ESTIMATE - UMFPACK_NUMERIC_SIZE) +#define ACTUAL 0 + +/* -------------------------------------------------------------------------- */ +/* get a parameter from the Control array */ +/* -------------------------------------------------------------------------- */ + +#define GET_CONTROL(i,default) \ + ((Control != (double *) NULL) ? \ + (SCALAR_IS_NAN (Control [i]) ? default : Control [i]) \ + : default) + +/* -------------------------------------------------------------------------- */ +/* for clearing the external degree counters */ +/* -------------------------------------------------------------------------- */ + +#define MAX_MARK(n) Int_MAX - (2*(n)+1) + +/* -------------------------------------------------------------------------- */ +/* convert number of Units to MBytes */ +/* -------------------------------------------------------------------------- */ + +#define MBYTES(units) (((units) * sizeof (Unit)) / 1048576.0) + +/* -------------------------------------------------------------------------- */ +/* dense row/column macro */ +/* -------------------------------------------------------------------------- */ + +/* In order for a row or column to be treated as "dense", it must have more */ +/* entries than the value returned by this macro. n is the dimension of the */ +/* matrix, and alpha is the dense row/column control parameter. */ + +/* Note: this is not defined if alpha is NaN or Inf: */ +#define UMFPACK_DENSE_DEGREE_THRESHOLD(alpha,n) \ + ((Int) MAX (16.0, (alpha) * 16.0 * sqrt ((double) (n)))) + +/* -------------------------------------------------------------------------- */ +/* PRINTF */ +/* -------------------------------------------------------------------------- */ + +#define PRINTFk(k,params) { if (prl >= (k)) { PRINTF (params) ; } } +#define PRINTF1(params) PRINTFk (1, params) +#define PRINTF2(params) PRINTFk (2, params) +#define PRINTF3(params) PRINTFk (3, params) +#define PRINTF4(params) PRINTFk (4, params) +#define PRINTF5(params) PRINTFk (5, params) +#define PRINTF6(params) PRINTFk (6, params) + +/* -------------------------------------------------------------------------- */ +/* Fixed control parameters */ +/* -------------------------------------------------------------------------- */ + +/* maximum number of columns to consider at one time, in a single front */ +#define MAX_CANDIDATES 128 + +/* reduce Numeric->Memory request by this ratio, if allocation fails */ +#define UMF_REALLOC_REDUCTION (0.95) + +/* increase Numeric->Memory request by this ratio, if we need more */ +#define UMF_REALLOC_INCREASE (1.2) + +/* increase the dimensions of the current frontal matrix by this factor + * when it needs to grow. */ +#define UMF_FRONTAL_GROWTH (1.2) + +/* largest BLAS block size permitted */ +#define MAXNB 64 + +/* if abs (y) < RECIPROCAL_TOLERANCE, then compute x/y. Otherwise x*(1/y). + * Ignored if NRECIPROCAL is defined */ +#define RECIPROCAL_TOLERANCE 1e-12 + +/* -------------------------------------------------------------------------- */ +/* Memory allocator */ +/* -------------------------------------------------------------------------- */ + +/* The MATLAB mexFunction uses MATLAB's memory manager, while the C-callable + * AMD library uses the ANSI C malloc, free, and realloc routines. To use + * the mx* memory allocation routines, use -DNUTIL when compiling. + */ + +#undef ALLOCATE +#undef FREE +#undef REALLOC + +#ifdef MATLAB_MEX_FILE + +#ifdef NUTIL + +/* These functions simply terminate the mexFunction if they fail to allocate + * memory. That's too restrictive for UMFPACK. */ +#define ALLOCATE mxMalloc +#define FREE mxFree +#define REALLOCATE mxRealloc + +#else + +/* Use internal MATLAB memory allocation routines, used by built-in MATLAB + * functions. These are not documented, but are available for use. Their + * prototypes are in util.h, but that file is not provided to the MATLAB user. + * The advantage of using these routines is that they return NULL if out of + * memory, instead of terminating the mexFunction. UMFPACK attempts to allocate + * extra space for "elbow room", and then reduces its request if the memory is + * not available. That strategy doesn't work with the mx* routines. + */ +void *utMalloc (size_t size) ; +void utFree (void *p) ; +void *utRealloc (void *p, size_t size) ; +#define ALLOCATE utMalloc +#define FREE utFree +#define REALLOCATE utRealloc + +#endif +#else +#ifdef MATHWORKS + +/* Compiling as a built-in routine. Since out-of-memory conditions are checked + * after every allocation, we can use ut* routines here. */ +#define ALLOCATE utMalloc +#define FREE utFree +#define REALLOCATE utRealloc + +#else + +/* use the ANSI C memory allocation routines */ +#define ALLOCATE malloc +#define FREE free +#define REALLOCATE realloc + +#endif +#endif + +/* -------------------------------------------------------------------------- */ +/* Memory space definitions */ +/* -------------------------------------------------------------------------- */ + +/* for memory alignment - assume double has worst case alignment */ +typedef double Align ; + +/* get number of bytes required to hold n items of a type: */ +/* note that this will not overflow, because sizeof (type) is always */ +/* greater than or equal to sizeof (Int) >= 2 */ +#define BYTES(type,n) (sizeof (type) * (n)) + +/* ceiling of (b/u). Assumes b >= 0 and u > 0 */ +#define CEILING(b,u) (((b) + (u) - 1) / (u)) + +/* get number of Units required to hold n items of a type: */ +#define UNITS(type,n) (CEILING (BYTES (type, n), sizeof (Unit))) + +/* same as DUNITS, but use double instead of int to avoid overflow */ +#define DUNITS(type,n) (ceil (BYTES (type, (double) n) / sizeof (Unit))) + +union Unit_union +{ /* memory is allocated in multiples of Unit */ + struct + { + Int + size, /* size, in Units, of the block, excl. header block */ + /* size >= 0: block is in use */ + /* size < 0: block is free, of |size| Units */ + prevsize ; /* size, in Units, of preceding block in S->Memory */ + /* during garbage_collection, prevsize is set to -e-1 */ + /* for element e, or positive (and thus a free block) */ + /* otherwise */ + } header ; /* block header */ + Align xxxxxx ; /* force alignment of blocks (xxxxxx is never used) */ +} ; + +typedef union Unit_union Unit ; + +/* get the size of an allocated block */ +#define GET_BLOCK_SIZE(p) (((p)-1)->header.size) + +/* -------------------------------------------------------------------------- */ +/* Numeric */ +/* -------------------------------------------------------------------------- */ + +/* + NUMERIC_VALID and SYMBOLIC_VALID: + The different values of SYBOLIC_VALID and NUMERIC_VALID are chosen as a + first defense against corrupted *Symbolic or *Numeric pointers passed to an + UMFPACK routine. They also ensure that the objects are used only by the + same version that created them (umfpack_di_*, umfpack_dl_*, umfpack_zi_*, + or umfpack_zl_*). The values have also been changed since prior releases of + the code to ensure that all routines that operate on the objects are of the + same release. The values themselves are purely arbitrary. The are less + than the ANSI C required minimums of INT_MAX and LONG_MAX, respectively. +*/ + +#ifdef DINT +#define NUMERIC_VALID 15977 +#define SYMBOLIC_VALID 41937 +#endif +#ifdef DLONG +#define NUMERIC_VALID 399789720 +#define SYMBOLIC_VALID 399192713 +#endif +#ifdef ZINT +#define NUMERIC_VALID 17957 +#define SYMBOLIC_VALID 40927 +#endif +#ifdef ZLONG +#define NUMERIC_VALID 129987754 +#define SYMBOLIC_VALID 110291734 +#endif + +typedef struct /* NumericType */ +{ + double + flops, /* "true" flop count */ + relpt, /* relative pivot tolerance used */ + relpt2, /* relative pivot tolerance used for sym. */ + droptol, + alloc_init, /* initial allocation of Numeric->memory */ + front_alloc_init, /* frontal matrix allocation parameter */ + rsmin, /* smallest row sum */ + rsmax, /* largest row sum */ + min_udiag, /* smallest abs value on diagonal of D */ + max_udiag, /* smallest abs value on diagonal of D */ + rcond ; /* min (D) / max (D) */ + + Int + scale ; + + Int valid ; /* set to NUMERIC_VALID, for validity check */ + + /* Memory space for A and LU factors */ + Unit + *Memory ; /* working memory for A and LU factors */ + Int + ihead, /* pointer to tail of LU factors, in Numeric->Memory */ + itail, /* pointer to top of elements & tuples, */ + /* in Numeric->Memory */ + ibig, /* pointer to largest free block seen in tail */ + size ; /* size of Memory, in Units */ + + Int + *Rperm, /* pointer to row perm array, size: n+1 */ + /* after UMF_kernel: Rperm [new] = old */ + /* during UMF_kernel: Rperm [old] = new */ + *Cperm, /* pointer to col perm array, size: n+1 */ + /* after UMF_kernel: Cperm [new] = old */ + /* during UMF_kernel: Cperm [old] = new */ + + *Upos, /* see UMFPACK_get_numeric for a description */ + *Lpos, + *Lip, + *Lilen, + *Uip, + *Uilen, + *Upattern ; /* pattern of last row of U (if singular) */ + + Int + ulen, /* length of Upattern */ + npiv, /* number of structural pivots found (sprank approx) */ + nnzpiv ; /* number of numerical (nonzero) pivots found */ + + Entry + *D ; /* D [i] is the diagonal entry of U */ + + Int do_recip ; + double *Rs ; /* scale factors for the rows of A and b */ + /* do_recip FALSE: Divide row i by Rs [i] */ + /* do_recip TRUE: Multiply row i by Rs [i] */ + + Int + n_row, n_col, /* A is n_row-by-n_row */ + n1 ; /* number of singletons */ + + /* for information only: */ + Int + tail_usage, /* amount of memory allocated in tail */ + /* head_usage is Numeric->ihead */ + init_usage, /* memory usage just after UMF_kernel_init */ + max_usage, /* peak memory usage (excludes internal and external */ + /* fragmentation in the tail) */ + ngarbage, /* number of garbage collections performed */ + nrealloc, /* number of reallocations performed */ + ncostly, /* number of costly reallocations performed */ + isize, /* size of integer pattern of L and U */ + nLentries, /* number of entries in L, excluding diagonal */ + nUentries, /* number of entries in U, including diagonal */ + /* Some entries may be numerically zero. */ + lnz, /* number of nonzero entries in L, excl. diagonal */ + all_lnz, /* lnz plus entries dropped from L */ + unz, /* number of nonzero entries in U, excl. diagonal */ + all_unz, /* unz plus entries dropped form U */ + maxfrsize ; /* largest actual front size */ + + Int maxnrows, maxncols ; /* not the same as Symbolic->maxnrows/cols* */ + +} NumericType ; + + + +/* -------------------------------------------------------------------------- */ +/* Element tuples for connecting elements together in a matrix */ +/* -------------------------------------------------------------------------- */ + +typedef struct /* Tuple */ +{ + /* The (e,f) tuples for the element lists */ + Int + e, /* element */ + f ; /* contribution to the row/col appears at this offset */ + +} Tuple ; + +#define TUPLES(t) MAX (4, (t) + 1) + +/* Col_degree is aliased with Cperm, and Row_degree with Rperm */ +#define NON_PIVOTAL_COL(col) (Col_degree [col] >= 0) +#define NON_PIVOTAL_ROW(row) (Row_degree [row] >= 0) + +/* -------------------------------------------------------------------------- */ +/* An element */ +/* -------------------------------------------------------------------------- */ + +typedef struct /* Element */ +{ + Int + + cdeg, /* external column degree + cdeg0 offset */ + rdeg, /* external row degree + rdeg0 offset */ + nrowsleft, /* number of rows remaining */ + ncolsleft, /* number of columns remaining */ + nrows, /* number of rows */ + ncols, /* number of columns */ + next ; /* for list link of sons, used during assembly only */ + + /* followed in memory by: + Int + col [0..ncols-1], column indices of this element + row [0..nrows-1] ; row indices of this element + Entry (suitably aligned, see macro below) + C [0...nrows-1, 0...ncols-1] ; + size of C is nrows*ncols Entry's + */ + +} Element ; + +/* macros for computing pointers to row/col indices, and contribution block: */ + +#define GET_ELEMENT_SIZE(nr,nc) \ +(UNITS (Element, 1) + UNITS (Int, (nc) + (nr)) + UNITS (Entry, (nc) * (nr))) + +#define DGET_ELEMENT_SIZE(nr,nc) \ +(DUNITS (Element, 1) + DUNITS (Int, (nc) + (nr)) + DUNITS (Entry, (nc) * (nr))) + +#define GET_ELEMENT_COLS(ep,p,Cols) { \ + ASSERT (p != (Unit *) NULL) ; \ + ASSERT (p >= Numeric->Memory + Numeric->itail) ; \ + ASSERT (p <= Numeric->Memory + Numeric->size) ; \ + ep = (Element *) p ; \ + p += UNITS (Element, 1) ; \ + Cols = (Int *) p ; \ +} + +#define GET_ELEMENT_PATTERN(ep,p,Cols,Rows,ncm) { \ + GET_ELEMENT_COLS (ep, p, Cols) ; \ + ncm = ep->ncols ; \ + Rows = Cols + ncm ; \ +} + +#define GET_ELEMENT(ep,p,Cols,Rows,ncm,nrm,C) { \ + GET_ELEMENT_PATTERN (ep, p, Cols, Rows, ncm) ; \ + nrm = ep->nrows ; \ + p += UNITS (Int, ncm + nrm) ; \ + C = (Entry *) p ; \ +} + +/* -------------------------------------------------------------------------- */ +/* Work data structure */ +/* -------------------------------------------------------------------------- */ + +/* + This data structure holds items needed only during factorization. + All of this is freed when UMFPACK_numeric completes. Note that some of + it is stored in the tail end of Numeric->S (namely, the Tuples and the + Elements). +*/ + +typedef struct /* WorkType */ +{ + + /* ---------------------------------------------------------------------- */ + /* information about each row and col of A */ + /* ---------------------------------------------------------------------- */ + + /* + Row_tuples: pointer to tuple list (alias with Numeric->Uip) + Row_tlen: number of tuples (alias with Numeric->Uilen) + Col_tuples: pointer to tuple list (alias with Numeric->Lip) + Col_tlen: number of tuples (alias with Numeric->Lilen) + Row_degree: degree of the row or column (alias Numeric->Rperm) + Col_degree: degree of the row or column (alias Numeric->Cperm) + + The Row_degree and Col_degree are MATLAB-style colmmd approximations, + are equal to the sum of the sizes of the elements (contribution blocks) + in each row and column. They are maintained when elements are created + and assembled. They are used only during the pivot row and column + search. They are not needed to represent the pattern of the remaining + matrix. + */ + + /* ---------------------------------------------------------------------- */ + /* information about each element */ + /* ---------------------------------------------------------------------- */ + + Int *E ; /* E [0 .. Work->elen-1] element "pointers" */ + /* (offsets in Numeric->Memory) */ + + /* ---------------------------------------------------------------------- */ + /* generic workspace */ + /* ---------------------------------------------------------------------- */ + + Entry *Wx, *Wy ; /* each of size maxnrows+1 */ + + Int /* Sizes: nn = MAX (n_row, n_col) */ + *Wp, /* nn+1 */ + *Wrp, /* n_col+1 */ + *Wm, /* maxnrows+1 */ + *Wio, /* maxncols+1 */ + *Woi, /* maxncols+1 */ + *Woo, /* MAX (maxnrows,maxncols)+1 */ + *Wrow, /* pointer to Fcols, Wio, or Woi */ + *NewRows, /* list of rows to scan */ + *NewCols ; /* list of cols to scan */ + + /* ---------------------------------------------------------------------- */ + + Int + *Lpattern, /* pattern of column of L, for one Lchain */ + *Upattern, /* pattern of row of U, for one Uchain */ + ulen, llen ; /* length of Upattern and Lpattern */ + + Int + *Diagonal_map, /* used for symmetric pivoting, of size nn+1 */ + *Diagonal_imap ;/* used for symmetric pivoting, of size nn+1 */ + + /* ---------------------------------------------------------------------- */ + + Int + n_row, n_col, /* matrix is n_row-by-n_col */ + nz, /* nonzeros in the elements for this matrix */ + n1, /* number of row and col singletons */ + elen, /* max possible number of elements */ + npiv, /* number of pivot rows and columns so far */ + ndiscard, /* number of discarded pivot columns */ + Wrpflag, + nel, /* elements in use are in the range 1..nel */ + noff_diagonal, + prior_element, + rdeg0, cdeg0, + rrdeg, ccdeg, + Candidates [MAX_CANDIDATES], /* current candidate pivot columns */ + nCandidates, /* number of candidates in Candidate set */ + ksuper, + firstsuper, + jsuper, + ncand, /* number of candidates (some not in Candidates[ ]) */ + nextcand, /* next candidate to place in Candidate search set */ + lo, + hi, + pivrow, /* current pivot row */ + pivcol, /* current pivot column */ + do_extend, /* true if the next pivot extends the current front */ + do_update, /* true if update should be applied */ + nforced, /* number of forced updates because of frontal growth */ + any_skip, + do_scan2row, + do_scan2col, + do_grow, + pivot_case, + frontid, /* id of current frontal matrix */ + nfr ; /* number of frontal matrices */ + + /* ---------------------------------------------------------------------- */ + /* For row-merge tree */ + /* ---------------------------------------------------------------------- */ + + Int + *Front_new1strow ; + + /* ---------------------------------------------------------------------- */ + /* current frontal matrix, F */ + /* ---------------------------------------------------------------------- */ + + Int Pivrow [MAXNB], + Pivcol [MAXNB] ; + + Entry + *Flublock, /* LU block, nb-by-nb */ + *Flblock, /* L block, fnr_curr-by-nb */ + *Fublock, /* U block, nb-by-fnc_curr, or U' fnc_curr-by-nb */ + *Fcblock ; /* C block, fnr_curr-by-fnc_curr */ + + Int + *Frows, /* Frows [0.. ]: row indices of F */ + + *Fcols, /* Fcols [0.. ]: column indices of F */ + + *Frpos, /* position of row indices in F, or -1 if not present */ + /* if Frows[i] == row, then Frpos[row] == i */ + + *Fcpos, /* position of col indices in F, or -1 if not present */ + /* if Fcols[j] == col, then */ + /* Fcpos[col] == j*Work->fnr_curr */ + + fnrows, /* number of rows in contribution block in F */ + fncols, /* number of columns in contribution block in F */ + fnr_curr, /* maximum # of rows in F (leading dimension) */ + fnc_curr, /* maximum # of columns in F */ + fcurr_size, /* current size of F */ + fnrows_max, /* max possible column-dimension (max # of rows) of F */ + fncols_max, /* max possible row-dimension (max # of columns) of F */ + nb, + fnpiv, /* number of pivots in F */ + fnzeros, /* number of explicit zero entries in LU block */ + fscan_row, /* where to start scanning rows of F in UMF_assemble */ + fscan_col, /* where to start scanning cols of F in UMF_assemble */ + fnrows_new, /* number of new row indices in F after pivot added */ + fncols_new, /* number of new col indices in F after pivot added */ + pivrow_in_front, /* true if current pivot row in Frows */ + pivcol_in_front ; /* true if current pivot column in Fcols */ + + /* ---------------------------------------------------------------------- + * Current frontal matrix + * ---------------------------------------------------------------------- + * The current frontal matrix is held as a single block of memory allocated + * from the "tail" end of Numeric->Memory. It is subdivided into four + * parts: an LU block, an L block, a U block, and a C block. + * + * Let k = fnpiv, r = fnrows, and c = fncols for the following discussion. + * Let dr = fnr_curr and dc = fnc_curr. Note that r <= dr and c <= dc. + * + * The LU block is of dimension nb-by-nb. The first k-by-k part holds the + * "diagonal" part of the LU factors for these k pivot rows and columns. + * The k pivot row and column indices in this part are Pivrow [0..k-1] and + * Pivcol [0..k-1], respectively. + * + * The L block is of dimension dr-by-nb. It holds the k pivot columns, + * except for the leading k-by-k part in the LU block. Only the leading + * r-by-k part is in use. + * + * The U block is of dimension dc-by-nb. It holds the k pivot rows, + * except for the leading k-by-k part in the LU block. It is stored in + * row-oriented form. Only the leading c-by-k part is in use. + * + * The C block is of dimension dr-by-dc. It holds the current contribution + * block. Only the leading r-by-c part is in use. The column indices in + * the C block are Fcols [0..c-1], and the row indices are Frows [0..r-1]. + * + * dr is always odd, to avoid bad cache behavior. + */ + +} WorkType ; + + +/* -------------------------------------------------------------------------- */ +/* Symbolic */ +/* -------------------------------------------------------------------------- */ + +/* + This is is constructed by UMFPACK_symbolic, and is needed by UMFPACK_numeric + to factor the matrix. +*/ + +typedef struct /* SymbolicType */ +{ + + double + num_mem_usage_est, /* estimated max Numeric->Memory size */ + num_mem_size_est, /* estimated final Numeric->Memory size */ + peak_sym_usage, /* peak Symbolic and SymbolicWork usage */ + sym, /* symmetry of pattern */ + dnum_mem_init_usage, /* min Numeric->Memory for UMF_kernel_init */ + amd_lunz, /* nz in LU for AMD, with symmetric pivoting */ + lunz_bound ; /* max nx in LU, for arbitrary row pivoting */ + + Int valid, /* set to SYMBOLIC_VALID, for validity check */ + max_nchains, + nchains, + *Chain_start, + *Chain_maxrows, + *Chain_maxcols, + maxnrows, /* largest number of rows in any front */ + maxncols, /* largest number of columns in any front */ + *Front_npivcol, /* Front_npivcol [j] = size of jth supercolumn*/ + *Front_1strow, /* first row index in front j */ + *Front_leftmostdesc, /* leftmost desc of front j */ + *Front_parent, /* super-column elimination tree */ + *Cperm_init, /* initial column ordering */ + *Rperm_init, /* initial row ordering */ + *Cdeg, *Rdeg, + *Esize, + dense_row_threshold, + n1, /* number of singletons */ + nempty, /* MIN (nempty_row, nempty_col) */ + *Diagonal_map, /* initial "diagonal" (after 2by2) */ + esize, /* size of Esize array */ + nfr, + n_row, n_col, /* matrix A is n_row-by-n_col */ + nz, /* nz of original matrix */ + nb, /* block size for BLAS 3 */ + num_mem_init_usage, /* min Numeric->Memory for UMF_kernel_init */ + nempty_row, nempty_col, + + strategy, + ordering, + fixQ, + prefer_diagonal, + nzaat, + nzdiag, + amd_dmax ; + +} SymbolicType ; + + +/* -------------------------------------------------------------------------- */ +/* for debugging only: */ +/* -------------------------------------------------------------------------- */ + +#include "umf_dump.h" + +/* -------------------------------------------------------------------------- */ +/* for statement coverage testing only: */ +/* -------------------------------------------------------------------------- */ + +#ifdef TESTING + +/* for testing integer overflow: */ +#ifdef TEST_FOR_INTEGER_OVERFLOW +#undef MAX_MARK +#define MAX_MARK(n) (3*(n)) +#endif + +/* for testing out-of-memory conditions: */ +#define UMF_TCOV_TEST +GLOBAL extern Int umf_fail, umf_fail_lo, umf_fail_hi ; +GLOBAL extern Int umf_realloc_fail, umf_realloc_lo, umf_realloc_hi ; + +/* for testing malloc count: */ +#define UMF_MALLOC_COUNT + +#endif diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_is_permutation.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_is_permutation.c new file mode 100644 index 0000000000..845ece2ac0 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_is_permutation.c @@ -0,0 +1,56 @@ +/* ========================================================================== */ +/* === UMF_is_permutation =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Return TRUE if P is a r-permutation vector, FALSE otherwise */ +/* P [0..r-1] must be an r-permutation of 0..n-1 */ + +#include "umf_internal.h" + +GLOBAL Int UMF_is_permutation +( + const Int P [ ], /* permutation of size r */ + Int W [ ], /* workspace of size n */ + Int n, + Int r +) +{ + Int i, k ; + + if (!P) + { + /* if P is (Int *) NULL, this is the identity permutation */ + return (TRUE) ; + } + + ASSERT (W != (Int *) NULL) ; + + for (i = 0 ; i < n ; i++) + { + W [i] = FALSE ; + } + for (k = 0 ; k < r ; k++) + { + i = P [k] ; + DEBUG5 (("k "ID" i "ID"\n", k, i)) ; + if (i < 0 || i >= n) + { + DEBUG0 (("i out of range "ID" "ID"\n", i, n)) ; + return (FALSE) ; + } + if (W [i]) + { + DEBUG0 (("i duplicate "ID"\n", i)) ; + return (FALSE) ; + } + W [i] = TRUE ; + } + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_is_permutation.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_is_permutation.h new file mode 100644 index 0000000000..386604b2f5 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_is_permutation.h @@ -0,0 +1,14 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_is_permutation +( + const Int P [ ], + Int W [ ], + Int n, + Int r +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel.c new file mode 100644 index 0000000000..7e433f1653 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel.c @@ -0,0 +1,300 @@ +/* ========================================================================== */ +/* === UMF_kernel =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Primary factorization routine. Called by UMFPACK_numeric. + Returns: + UMFPACK_OK if successful, + UMFPACK_ERROR_out_of_memory if out of memory, or + UMFPACK_ERROR_different_pattern if pattern of matrix (Ap and/or Ai) + has changed since the call to UMFPACK_*symbolic. +*/ + +#include "umf_internal.h" +#include "umf_kernel_init.h" +#include "umf_init_front.h" +#include "umf_start_front.h" +#include "umf_assemble.h" +#include "umf_scale_column.h" +#include "umf_local_search.h" +#include "umf_create_element.h" +#include "umf_extend_front.h" +#include "umf_blas3_update.h" +#include "umf_store_lu.h" +#include "umf_kernel_wrapup.h" + +/* perform an action, and return if out of memory */ +#define DO(action) { if (! (action)) { return (UMFPACK_ERROR_out_of_memory) ; }} + +GLOBAL Int UMF_kernel +( + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int j, f1, f2, chain, nchains, *Chain_start, status, fixQ, evaporate, + *Front_npivcol, jmax, nb, drop ; + + /* ---------------------------------------------------------------------- */ + /* initialize memory space and load the matrix. Optionally scale. */ + /* ---------------------------------------------------------------------- */ + + if (!UMF_kernel_init (Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + Numeric, Work, Symbolic)) + { + /* UMF_kernel_init is guaranteed to succeed, since UMFPACK_numeric */ + /* either allocates enough space or if not, UMF_kernel does not get */ + /* called. So running out of memory here is a fatal error, and means */ + /* that the user changed Ap and/or Ai since the call to */ + /* UMFPACK_*symbolic. */ + DEBUGm4 (("kernel init failed\n")) ; + return (UMFPACK_ERROR_different_pattern) ; + } + + /* ---------------------------------------------------------------------- */ + /* get the symbolic factorization */ + /* ---------------------------------------------------------------------- */ + + nchains = Symbolic->nchains ; + Chain_start = Symbolic->Chain_start ; + Front_npivcol = Symbolic->Front_npivcol ; + nb = Symbolic->nb ; + fixQ = Symbolic->fixQ ; + drop = Numeric->droptol > 0.0 ; + +#ifndef NDEBUG + for (chain = 0 ; chain < nchains ; chain++) + { + Int i ; + f1 = Chain_start [chain] ; + f2 = Chain_start [chain+1] - 1 ; + DEBUG1 (("\nCHain: "ID" start "ID" end "ID"\n", chain, f1, f2)) ; + for (i = f1 ; i <= f2 ; i++) + { + DEBUG1 (("Front "ID", npivcol "ID"\n", i, Front_npivcol [i])) ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* factorize each chain of frontal matrices */ + /* ---------------------------------------------------------------------- */ + + for (chain = 0 ; chain < nchains ; chain++) + { + f1 = Chain_start [chain] ; + f2 = Chain_start [chain+1] - 1 ; + + /* ------------------------------------------------------------------ */ + /* get the initial frontal matrix size for this chain */ + /* ------------------------------------------------------------------ */ + + DO (UMF_start_front (chain, Numeric, Work, Symbolic)) ; + + /* ------------------------------------------------------------------ */ + /* factorize each front in the chain */ + /* ------------------------------------------------------------------ */ + + for (Work->frontid = f1 ; Work->frontid <= f2 ; Work->frontid++) + { + + /* -------------------------------------------------------------- */ + /* Initialize the pivot column candidate set */ + /* -------------------------------------------------------------- */ + + Work->ncand = Front_npivcol [Work->frontid] ; + Work->lo = Work->nextcand ; + Work->hi = Work->nextcand + Work->ncand - 1 ; + jmax = MIN (MAX_CANDIDATES, Work->ncand) ; + DEBUGm1 ((">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Starting front " + ID", npivcol: "ID"\n", Work->frontid, Work->ncand)) ; + if (fixQ) + { + /* do not modify the column order */ + jmax = 1 ; + } + DEBUGm1 (("Initial candidates: ")) ; + for (j = 0 ; j < jmax ; j++) + { + DEBUGm1 ((" "ID, Work->nextcand)) ; + ASSERT (Work->nextcand <= Work->hi) ; + Work->Candidates [j] = Work->nextcand++ ; + } + Work->nCandidates = jmax ; + DEBUGm1 (("\n")) ; + + /* -------------------------------------------------------------- */ + /* Assemble and factorize the current frontal matrix */ + /* -------------------------------------------------------------- */ + + while (Work->ncand > 0) + { + + /* ---------------------------------------------------------- */ + /* get the pivot row and column */ + /* ---------------------------------------------------------- */ + + status = UMF_local_search (Numeric, Work, Symbolic) ; + if (status == UMFPACK_ERROR_different_pattern) + { + /* :: pattern change detected in umf_local_search :: */ + /* input matrix has changed since umfpack_*symbolic */ + DEBUGm4 (("local search failed\n")) ; + return (UMFPACK_ERROR_different_pattern) ; + } + if (status == UMFPACK_WARNING_singular_matrix) + { + /* no pivot found, discard and try again */ + continue ; + } + + /* ---------------------------------------------------------- */ + /* update if front not extended or too many zeros in L,U */ + /* ---------------------------------------------------------- */ + + if (Work->do_update) + { + UMF_blas3_update (Work) ; + if (drop) + { + DO (UMF_store_lu_drop (Numeric, Work)) ; + } + else + { + DO (UMF_store_lu (Numeric, Work)) ; + } + } + + /* ---------------------------------------------------------- */ + /* extend the frontal matrix, or start a new one */ + /* ---------------------------------------------------------- */ + + if (Work->do_extend) + { + /* extend the current front */ + DO (UMF_extend_front (Numeric, Work)) ; + } + else + { + /* finish the current front (if any) and start a new one */ + DO (UMF_create_element (Numeric, Work, Symbolic)) ; + DO (UMF_init_front (Numeric, Work)) ; + } + + /* ---------------------------------------------------------- */ + /* Numerical & symbolic assembly into current frontal matrix */ + /* ---------------------------------------------------------- */ + + if (fixQ) + { + UMF_assemble_fixq (Numeric, Work) ; + } + else + { + UMF_assemble (Numeric, Work) ; + } + + /* ---------------------------------------------------------- */ + /* scale the pivot column */ + /* ---------------------------------------------------------- */ + + UMF_scale_column (Numeric, Work) ; + + /* ---------------------------------------------------------- */ + /* Numerical update if enough pivots accumulated */ + /* ---------------------------------------------------------- */ + + evaporate = Work->fnrows == 0 || Work->fncols == 0 ; + if (Work->fnpiv >= nb || evaporate) + { + UMF_blas3_update (Work) ; + if (drop) + { + DO (UMF_store_lu_drop (Numeric, Work)) ; + } + else + { + DO (UMF_store_lu (Numeric, Work)) ; + } + + } + + Work->pivrow_in_front = FALSE ; + Work->pivcol_in_front = FALSE ; + + /* ---------------------------------------------------------- */ + /* If front is empty, evaporate it */ + /* ---------------------------------------------------------- */ + + if (evaporate) + { + /* This does not create an element, just evaporates it. + * It ensures that a front is not 0-by-c or r-by-0. No + * memory is allocated, so it is guaranteed to succeed. */ + (void) UMF_create_element (Numeric, Work, Symbolic) ; + Work->fnrows = 0 ; + Work->fncols = 0 ; + } + } + } + + /* ------------------------------------------------------------------ + * Wrapup the current frontal matrix. This is the last in a chain + * in the column elimination tree. The next frontal matrix + * cannot overlap with the current one, which will be its sibling + * in the column etree. + * ------------------------------------------------------------------ */ + + UMF_blas3_update (Work) ; + if (drop) + { + DO (UMF_store_lu_drop (Numeric, Work)) ; + } + else + { + DO (UMF_store_lu (Numeric, Work)) ; + } + Work->fnrows_new = Work->fnrows ; + Work->fncols_new = Work->fncols ; + DO (UMF_create_element (Numeric, Work, Symbolic)) ; + + /* ------------------------------------------------------------------ */ + /* current front is now empty */ + /* ------------------------------------------------------------------ */ + + Work->fnrows = 0 ; + Work->fncols = 0 ; + } + + /* ---------------------------------------------------------------------- */ + /* end the last Lchain and Uchain and finalize the LU factors */ + /* ---------------------------------------------------------------------- */ + + UMF_kernel_wrapup (Numeric, Symbolic, Work) ; + + /* note that the matrix may be singular (this is OK) */ + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel.h new file mode 100644 index 0000000000..3f64436204 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel.h @@ -0,0 +1,19 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_kernel +( + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_init.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_init.c new file mode 100644 index 0000000000..a1f0b97068 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_init.c @@ -0,0 +1,1053 @@ +/* ========================================================================== */ +/* === UMF_kernel_init ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Initialize the kernel: scale the matrix, load the initial elements, and + build the tuple lists. + + Returns TRUE if successful, FALSE if out of memory or if the pattern has + changed since UMFPACK_*symbolic. UMFPACK_numeric allocates at least enough + space for UMF_kernel_init to succeed; otherwise it does not call + UMF_kernel_init. So an out-of-memory condition means that the pattern must + have gotten larger. +*/ + +#include "umf_internal.h" +#include "umf_tuple_lengths.h" +#include "umf_build_tuples.h" +#include "umf_mem_init_memoryspace.h" +#include "umf_mem_alloc_element.h" +#include "umf_mem_alloc_head_block.h" +#include "umf_mem_alloc_tail_block.h" +#include "umf_mem_free_tail_block.h" +#include "umf_free.h" +#include "umf_scale.h" + +/* ========================================================================== */ +/* === packsp =============================================================== */ +/* ========================================================================== */ + +/* remove zero or small entries from a column of L or a row of U */ + +PRIVATE Int packsp /* returns new value of pnew */ +( + Int pnew, /* index into Memory of next free space */ + Int *p_p, /* ptr to index of old pattern in Memory on input, + new pattern on output */ + Int *p_len, /* ptr to length of old pattern on input, + new pattern on output */ + Int drop, /* TRUE if small nonzero entries are to be dropped */ + double droptol, /* the drop tolerance */ + Unit *Memory /* contains the sparse vector on input and output */ +) +{ + Entry x, *Bx, *Bx2 ; + Int p, i, len, len_new, *Bi, *Bi2 ; + double s ; + + /* get the pointers to the sparse vector, and its length */ + p = *p_p ; + len = *p_len ; + Bi = (Int *) (Memory + p) ; p += UNITS (Int, len) ; + Bx = (Entry *) (Memory + p) ; p += UNITS (Entry, len) ; + DEBUGm4 ((" p "ID" len "ID" pnew "ID"\n", p, len, pnew)) ; + + /* the vector resides in Bi [0..len-1] and Bx [0..len-1] */ + + /* first, compact the vector in place */ + len_new = 0 ; + for (p = 0 ; p < len ; p++) + { + i = Bi [p] ; + x = Bx [p] ; + DEBUGm4 ((" old vector: i "ID" value: ", i)) ; + EDEBUGk (-4, x) ; + DEBUGm4 (("\n")) ; + ASSERT (i >= 0) ; + /* skip if zero or below drop tolerance */ + if (IS_ZERO (x)) continue ; + if (drop) + { + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + } + /* store the value back into the vector */ + if (len_new != p) + { + Bi [len_new] = i ; + Bx [len_new] = x ; + } + len_new++ ; + } + ASSERT (len_new <= len) ; + + /* the vector is now in Bi [0..len_new-1] and Bx [0..len_new-1] */ + +#ifndef NDEBUG + for (p = 0 ; p < len_new ; p++) + { + DEBUGm4 ((" new vector: i "ID" value: ", Bi [p])) ; + EDEBUGk (-4, Bx [p]) ; + DEBUGm4 (("\n")) ; + ASSERT (Bi [p] >= 0) ; + } +#endif + + /* allocate new space for the compacted vector */ + *p_p = pnew ; + *p_len = len_new ; + Bi2 = (Int *) (Memory + pnew) ; pnew += UNITS (Int, len_new) ; + Bx2 = (Entry *) (Memory + pnew) ; pnew += UNITS (Entry, len_new) ; + DEBUGm4 ((" pnew "ID" len_new "ID"\n", pnew, len_new)) ; + + /* shift the vector upwards, into its new space */ + for (p = 0 ; p < len_new ; p++) + { + Bi2 [p] = Bi [p] ; + } + for (p = 0 ; p < len_new ; p++) + { + Bx2 [p] = Bx [p] ; + } + +#ifndef NDEBUG + for (p = 0 ; p < len_new ; p++) + { + DEBUGm4 ((" packed vec: i "ID" value: ", Bi2 [p])) ; + EDEBUGk (-4, Bx2 [p]) ; + DEBUGm4 (("\n")) ; + ASSERT (Bi2 [p] >= 0) ; + } +#endif + + /* return the pointer to the space just after the new vector */ + return (pnew) ; +} + + +/* ========================================================================== */ +/* === UMF_kernel_init ====================================================== */ +/* ========================================================================== */ + +GLOBAL Int UMF_kernel_init +( + const Int Ap [ ], /* user's input matrix (not modified) */ + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int row, k, oldcol, size, e, p1, p2, p, nz, *Rows, *Cols, *E, i, *Upos, + *Lpos, n_row, n_col, *Wp, *Cperm_init, *Frpos, *Fcpos, *Row_degree, nn, + *Row_tlen, *Col_degree, *Col_tlen, oldrow, newrow, ilast, *Wrp, + *Rperm_init, col, n_inner, prefer_diagonal, *Diagonal_map, nempty, + *Diagonal_imap, fixQ, rdeg, cdeg, nempty_col, *Esize, esize, pnew, + *Lip, *Uip, *Lilen, *Uilen, llen, pa, *Cdeg, *Rdeg, n1, clen, do_scale, + lnz, unz, lip, uip, k1, *Rperm, *Cperm, pivcol, *Li, lilen, drop, + **Rpi, nempty_row, dense_row_threshold, empty_elements, rpi, rpx ; + double unused = 0, *Rs, rsmin, rsmax, rs, droptol ; + Entry *D, *C, x, *Lval, pivot_value, **Rpx ; + Element *ep ; + Unit *Memory ; + +#ifndef NRECIPROCAL + Int do_recip = FALSE ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + DEBUG0 (("KERNEL INIT\n")) ; + + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + nempty_col = Symbolic->nempty_col ; + nempty_row = Symbolic->nempty_row ; + nempty = MIN (nempty_row, nempty_col) ; + ASSERT (n_row > 0 && n_col > 0) ; + Cperm_init = Symbolic->Cperm_init ; + Rperm_init = Symbolic->Rperm_init ; + Cdeg = Symbolic->Cdeg ; + Rdeg = Symbolic->Rdeg ; + n1 = Symbolic->n1 ; + dense_row_threshold = Symbolic->dense_row_threshold ; + DEBUG0 (("Singletons: "ID"\n", n1)) ; + Work->nforced = 0 ; + Work->ndiscard = 0 ; + Work->noff_diagonal = 0 ; + + nz = Ap [n_col] ; + if (nz < 0 || Ap [0] != 0 || nz != Symbolic->nz) + { + DEBUGm4 (("nz or Ap [0] bad\n")) ; + return (FALSE) ; /* pattern changed */ + } + + prefer_diagonal = Symbolic->prefer_diagonal ; + Diagonal_map = Work->Diagonal_map ; + Diagonal_imap = Work->Diagonal_imap ; + + /* ---------------------------------------------------------------------- */ + /* initialize the Numeric->Memory space for LU, elements, and tuples */ + /* ---------------------------------------------------------------------- */ + + UMF_mem_init_memoryspace (Numeric) ; + DEBUG1 (("Kernel init head usage, before allocs: "ID"\n", Numeric->ihead)) ; + + /* ---------------------------------------------------------------------- */ + /* initialize the Work and Numeric objects */ + /* ---------------------------------------------------------------------- */ + + /* current front is empty */ + Work->fnpiv = 0 ; + Work->fncols = 0 ; + Work->fnrows = 0 ; + Work->fncols_max = 0 ; + Work->fnrows_max = 0 ; + Work->fnzeros = 0 ; + Work->fcurr_size = 0 ; + Work->fnr_curr = 0 ; + Work->fnc_curr = 0 ; + + Work->nz = nz ; + Work->prior_element = EMPTY ; + Work->ulen = 0 ; + Work->llen = 0 ; + Work->npiv = n1 ; + Work->frontid = 0 ; + Work->nextcand = n1 ; + + Memory = Numeric->Memory ; + Rperm = Numeric->Rperm ; + Cperm = Numeric->Cperm ; + Row_degree = Numeric->Rperm ; + Col_degree = Numeric->Cperm ; + /* Row_tuples = Numeric->Uip ; not needed */ + Row_tlen = Numeric->Uilen ; + /* Col_tuples = Numeric->Lip ; not needed */ + Col_tlen = Numeric->Lilen ; + + Lip = Numeric->Lip ; + Uip = Numeric->Uip ; + Lilen = Numeric->Lilen ; + Uilen = Numeric->Uilen ; + + Frpos = Work->Frpos ; + Fcpos = Work->Fcpos ; + Wp = Work->Wp ; + Wrp = Work->Wrp ; + + D = Numeric->D ; + Upos = Numeric->Upos ; + Lpos = Numeric->Lpos ; + for (k = 0 ; k < n_inner ; k++) + { + CLEAR (D [k]) ; + } + + Rs = Numeric->Rs ; + + for (row = 0 ; row <= n_row ; row++) + { + Lpos [row] = EMPTY ; + /* Row_tuples [row] = 0 ; set in UMF_build_tuples */ + /* Row_degree [row] = 0 ; initialized below */ + Row_tlen [row] = 0 ; + /* Frpos [row] = EMPTY ; do this later */ + } + + for (col = 0 ; col <= n_col ; col++) + { + Upos [col] = EMPTY ; + /* Col_tuples [col] = 0 ; set in UMF_build_tuples */ + /* Col_degree [col] = 0 ; initialized below */ + Col_tlen [col] = 0 ; + Fcpos [col] = EMPTY ; + Wrp [col] = 0 ; + } + Work->Wrpflag = 1 ; + + /* When cleared, Wp [0..nn] is < 0 */ + for (i = 0 ; i <= nn ; i++) + { + Wp [i] = EMPTY ; + } + /* In col search, Wp [row] is set to a position, which is >= 0. */ + + /* When cleared, Wrp [0..n_col] is < Wrpflag */ + /* In row search, Wrp [col] is set to Wrpflag. */ + + /* no need to initialize Wm, Wio, Woi, and Woo */ + + /* clear the external degree counters */ + Work->cdeg0 = 1 ; + Work->rdeg0 = 1 ; + + fixQ = Symbolic->fixQ ; + + E = Work->E ; + + Numeric->n_row = n_row ; + Numeric->n_col = n_col ; + Numeric->npiv = 0 ; + Numeric->nnzpiv = 0 ; + Numeric->min_udiag = 0.0 ; + Numeric->max_udiag = 0.0 ; + Numeric->rcond = 0.0 ; + Numeric->isize = 0 ; + Numeric->nLentries = 0 ; + Numeric->nUentries = 0 ; + Numeric->lnz = 0 ; + Numeric->unz = 0 ; + Numeric->all_lnz = 0 ; + Numeric->all_unz = 0 ; + Numeric->maxfrsize = 0 ; + Numeric->maxnrows = 0 ; + Numeric->maxncols = 0 ; + Numeric->flops = 0. ; + Numeric->n1 = n1 ; + droptol = Numeric->droptol ; + drop = (droptol > 0) ; + + /* ---------------------------------------------------------------------- */ + /* compute the scale factors, if requested, and check the input matrix */ + /* ---------------------------------------------------------------------- */ + + /* UMFPACK_SCALE_SUM: Rs [i] = sum of the absolute values in row i. + * UMFPACK_SCALE_MAX: Rs [i] = max of the absolute values in row i. + * + * If A is complex, an approximate abs is used (|xreal| + |ximag|). + * + * If min (Rs [0..n_row]) >= RECIPROCAL_TOLERANCE, then the scale + * factors are inverted, and the rows of A are multiplied by the scale + * factors. Otherwise, the rows are divided by the scale factors. If + * NRECIPROCAL is defined, then the rows are always divided by the scale + * factors. + * + * For MATLAB (either built-in routine or mexFunction), or for gcc, + * the rows are always divided by the scale factors. + */ + + do_scale = (Numeric->scale != UMFPACK_SCALE_NONE) ; + + if (do_scale) + { + int do_max = Numeric->scale == UMFPACK_SCALE_MAX ; + for (row = 0 ; row < n_row ; row++) + { + Rs [row] = 0.0 ; + } + for (col = 0 ; col < n_col ; col++) + { + ilast = EMPTY ; + p1 = Ap [col] ; + p2 = Ap [col+1] ; + if (p1 > p2) + { + /* invalid matrix */ + DEBUGm4 (("invalid matrix (Ap)\n")) ; + return (FALSE) ; + } + for (p = p1 ; p < p2 ; p++) + { + double value ; + Entry aij ; + row = Ai [p] ; + if (row <= ilast || row >= n_row) + { + /* invalid matrix, columns must be sorted, no duplicates */ + DEBUGm4 (("invalid matrix (Ai)\n")) ; + return (FALSE) ; + } + ASSIGN (aij, Ax [p], Az [p]) ; + APPROX_ABS (value, aij) ; + rs = Rs [row] ; + if (!SCALAR_IS_NAN (rs)) + { + if (SCALAR_IS_NAN (value)) + { + /* if any entry in the row is NaN, then the scale factor + * is NaN too (for now) and then set to 1.0 below */ + Rs [row] = value ; + } + else if (do_max) + { + Rs [row] = MAX (rs, value) ; + } + else + { + Rs [row] += value ; + } + } + DEBUG4 (("i "ID" j "ID" value %g, Rs[i]: %g\n", + row, col, value, Rs[row])) ; + ilast = row ; + } + } + DEBUG2 (("Rs[0] = %30.20e\n", Rs [0])) ; + for (row = 0 ; row < n_row ; row++) + { + rs = Rs [row] ; + if (SCALAR_IS_ZERO (rs) || SCALAR_IS_NAN (rs)) + { + /* don't scale a completely zero row, or one with NaN's */ + Rs [row] = 1.0 ; + } + } + rsmin = Rs [0] ; + rsmax = Rs [0] ; + for (row = 0 ; row < n_row ; row++) + { + DEBUG2 (("sum %30.20e ", Rs [row])) ; + rsmin = MIN (rsmin, Rs [row]) ; + rsmax = MAX (rsmax, Rs [row]) ; + DEBUG2 (("Rs["ID"] = %30.20e\n", row, Rs [row])) ; + } +#ifndef NRECIPROCAL + /* multiply by the reciprocal if Rs is not too small */ + do_recip = (rsmin >= RECIPROCAL_TOLERANCE) ; + if (do_recip) + { + /* invert the scale factors */ + for (row = 0 ; row < n_row ; row++) + { + Rs [row] = 1.0 / Rs [row] ; + } + } +#endif + } + else + { + /* no scaling, rsmin and rsmax not computed */ + rsmin = -1 ; + rsmax = -1 ; +#ifndef NRECIPROCAL + do_recip = FALSE ; +#endif + /* check the input matrix */ + if (!AMD_valid (n_row, n_col, Ap, Ai)) + { + /* matrix is invalid */ + return (FALSE) ; + } + } + + Numeric->rsmin = rsmin ; + Numeric->rsmax = rsmax ; +#ifndef NRECIPROCAL + Numeric->do_recip = do_recip ; +#else + Numeric->do_recip = FALSE ; +#endif + + /* ---------------------------------------------------------------------- */ + /* construct the inverse row Rperm_init permutation (use Frpos as temp) */ + /* ---------------------------------------------------------------------- */ + + DEBUG3 (("\n\n===================LOAD_MATRIX:\n")) ; + + for (newrow = 0 ; newrow < n_row ; newrow++) + { + oldrow = Rperm_init [newrow] ; + ASSERT (oldrow >= 0 && oldrow < n_row) ; + Frpos [oldrow] = newrow ; + } + + /* ---------------------------------------------------------------------- */ + /* construct the diagonal imap if doing symmetric pivoting */ + /* ---------------------------------------------------------------------- */ + + if (prefer_diagonal) + { + ASSERT (n_row == n_col) ; + ASSERT (nempty_col == Symbolic->nempty_row) ; + ASSERT (nempty_col == nempty) ; + for (i = 0 ; i < nn ; i++) + { + Diagonal_map [i] = EMPTY ; + Diagonal_imap [i] = EMPTY ; + } + for (k = n1 ; k < nn - nempty ; k++) + { + newrow = Symbolic->Diagonal_map [k] ; + Diagonal_map [k] = newrow ; + Diagonal_imap [newrow] = k ; + } + } + + /* ---------------------------------------------------------------------- */ + /* allocate O (n_row) workspace at the tail end of Memory */ + /* ---------------------------------------------------------------------- */ + + rpi = UMF_mem_alloc_tail_block (Numeric, UNITS (Int *, n_row+1)) ; + rpx = UMF_mem_alloc_tail_block (Numeric, UNITS (Entry *, n_row+1)) ; + if (!rpi || !rpx) + { + /* :: pattern change (out of memory for Rpx, Rpx) :: */ + /* out of memory, which can only mean that the pattern has changed */ + return (FALSE) ; /* pattern changed */ + } + Rpi = (Int **) (Memory + rpx) ; + Rpx = (Entry **) (Memory + rpi) ; + + /* ---------------------------------------------------------------------- */ + /* allocate the LU factors for the columns of the singletons */ + /* ---------------------------------------------------------------------- */ + + DEBUG1 (("Allocating singletons:\n")) ; + for (k = 0 ; k < n1 ; k++) + { + lnz = Cdeg [k] - 1 ; + unz = Rdeg [k] - 1 ; + + DEBUG1 (("Singleton k "ID" pivrow "ID" pivcol "ID" cdeg "ID" rdeg " + ID"\n", k, Rperm_init [k], Cperm_init [k], Cdeg [k], Rdeg [k])) ; + ASSERT (unz >= 0 && lnz >= 0 && (lnz == 0 || unz == 0)) ; + DEBUG1 ((" lnz "ID" unz "ID"\n", lnz, unz)) ; + + size = UNITS (Int, lnz) + UNITS (Entry, lnz) + + UNITS (Int, unz) + UNITS (Entry, unz) ; + p = UMF_mem_alloc_head_block (Numeric, size) ; + DEBUG1 (("Kernel init head usage: "ID"\n", Numeric->ihead)) ; + if (!p) + { + /* :: pattern change (out of memory for singletons) :: */ + DEBUG0 (("Pattern has gotten larger - kernel init failed\n")) ; + return (FALSE) ; /* pattern changed */ + } + + Numeric->all_lnz += lnz ; + Numeric->all_unz += unz ; + + /* allocate the column of L */ + lip = p ; + p += UNITS (Int, lnz) ; + p += UNITS (Entry, lnz) ; + + /* allocate the row of U */ + uip = p ; + Rpi [k] = (Int *) (Memory + p) ; + p += UNITS (Int, unz) ; + Rpx [k] = (Entry *) (Memory + p) ; + /* p += UNITS (Entry, unz) ; (not needed) */ + + /* a single column of L (no Lchains) */ + Lip [k] = lip ; + Lilen [k] = lnz ; + + /* a single row of L (no Uchains) */ + Uip [k] = uip ; + Uilen [k] = unz ; + + Wp [k] = unz ; + + /* save row and column inverse permutation */ + k1 = ONES_COMPLEMENT (k) ; + Rperm [k] = k1 ; /* aliased with Row_degree */ + Cperm [k] = k1 ; /* aliased with Col_degree */ + } + + /* ---------------------------------------------------------------------- */ + /* current frontal matrix is empty */ + /* ---------------------------------------------------------------------- */ + + e = 0 ; + E [e] = 0 ; + Work->Flublock = (Entry *) NULL ; + Work->Flblock = (Entry *) NULL ; + Work->Fublock = (Entry *) NULL ; + Work->Fcblock = (Entry *) NULL ; + + /* ---------------------------------------------------------------------- */ + /* allocate the column elements */ + /* ---------------------------------------------------------------------- */ + + Esize = Symbolic->Esize ; + empty_elements = FALSE ; + for (k = n1 ; k < n_col - nempty_col ; k++) + { + e = k - n1 + 1 ; + ASSERT (e < Work->elen) ; + esize = Esize ? Esize [k-n1] : Cdeg [k] ; + if (esize > 0) + { + /* allocate an element for this column */ + E [e] = UMF_mem_alloc_element (Numeric, esize, 1, &Rows, &Cols, &C, + &size, &ep) ; + if (E [e] <= 0) + { + /* :: pattern change (out of memory for column elements) :: */ + return (FALSE) ; /* pattern has changed */ + } + Cols [0] = k ; + DEBUG0 (("Got column element e "ID" esize "ID"\n", e, esize)) ; + } + else + { + /* all rows in this column are dense, or empty */ + E [e] = 0 ; + empty_elements = TRUE ; + DEBUG0 (("column element e is empty "ID"\n", e)) ; + } + } + DEBUG0 (("e "ID" n_col "ID" nempty_col "ID" n1 "ID"\n", e, n_col, + nempty_col, n1)) ; + ASSERT (e == n_col - nempty_col - n1) ; + + /* ---------------------------------------------------------------------- */ + /* allocate the row elements for dense rows of A (if any) */ + /* ---------------------------------------------------------------------- */ + + if (Esize) + { + for (k = n1 ; k < n_row - nempty_row ; k++) + { + rdeg = Rdeg [k] ; + if (rdeg > dense_row_threshold) + { + /* allocate an element for this dense row */ + e++ ; + ASSERT (e < Work->elen) ; + E [e] = UMF_mem_alloc_element (Numeric, 1, rdeg, &Rows, &Cols, + &C, &size, &ep) ; + if (E [e] <= 0) + { + /* :: pattern change (out of memory for row elements) :: */ + return (FALSE) ; /* pattern has changed */ + } + Rows [0] = k ; + Rpi [k] = Cols ; + Rpx [k] = C ; + Wp [k] = rdeg ; + DEBUG0 (("Got row element e "ID" rdeg "ID"\n", e, rdeg)) ; + } + } + } + + /* elements are currently in the range 0 to e */ + Work->nel = e ; + + /* ---------------------------------------------------------------------- */ + /* create the first n1 columns of L and U */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < n1 ; k++) + { + pivcol = Cperm_init [k] ; + p2 = Ap [pivcol+1] ; + + /* get the kth column of L */ + p = Lip [k] ; + Li = (Int *) (Memory + p) ; + lilen = Lilen [k] ; + p += UNITS (Int, lilen) ; + Lval = (Entry *) (Memory + p) ; + + llen = 0 ; + + for (pa = Ap [pivcol] ; pa < p2 ; pa++) + { + oldrow = Ai [pa] ; + newrow = Frpos [oldrow] ; + ASSIGN (x, Ax [pa], Az [pa]) ; + + /* scale the value using the scale factors, Rs */ + if (do_scale) + { +#ifndef NRECIPROCAL + if (do_recip) + { + SCALE_RECIP (x, Rs [oldrow]) ; + } + else +#endif + { + SCALE_DIV (x, Rs [oldrow]) ; + } + } + + if (newrow == k) + { + /* this is the pivot entry itself */ + ASSERT (oldrow == Rperm_init [k]) ; + D [k] = x ; + } + else if (newrow < k) + { + /* this entry goes in a row of U */ + DEBUG1 (("Singleton row of U: k "ID" newrow "ID"\n", + k, newrow)) ; + if (--(Wp [newrow]) < 0) + { + /* :: pattern change (singleton row too long) :: */ + DEBUGm4 (("bad U singleton row (too long)\n")) ; + return (FALSE) ; /* pattern changed */ + } + *(Rpi [newrow]++) = k ; + *(Rpx [newrow]++) = x ; + } + else + { + /* this entry goes in a column of L */ + DEBUG1 (("Singleton col of L: k "ID" newrow "ID"\n", + k, newrow)) ; + if (llen >= lilen) + { + DEBUGm4 (("bad L singleton col (too long)\n")) ; + return (FALSE) ; /* pattern changed */ + } + Li [llen] = newrow ; + Lval [llen] = x ; + llen++ ; + } + } + + if (llen != lilen) + { + /* :: pattern change (singleton column too long) :: */ + DEBUGm4 (("bad L singleton col (too short)\n")) ; + return (FALSE) ; /* pattern changed */ + } + + /* scale the column of L */ + if (llen > 0) + { + pivot_value = D [k] ; + UMF_scale (llen, pivot_value, Lval) ; + } + + } + + /* ---------------------------------------------------------------------- */ + /* allocate the elements and copy the columns of A */ + /* ---------------------------------------------------------------------- */ + + /* also apply the row and column pre-ordering. */ + for (k = n1 ; k < n_col ; k++) + { + /* The newcol is k, which is what the name of the column is in the + * UMFPACK kernel. The user's name for the column is oldcol. */ + oldcol = Cperm_init [k] ; + + ASSERT (oldcol >= 0 && oldcol < n_col) ; + + p2 = Ap [oldcol+1] ; + + cdeg = Cdeg [k] ; + ASSERT (cdeg >= 0) ; + ASSERT (IMPLIES ( + (Symbolic->ordering != UMFPACK_ORDERING_GIVEN) && n1 > 0, + cdeg > 1 || cdeg == 0)) ; + + /* if fixQ: set Col_degree to 0 for the NON_PIVOTAL_COL macro */ + Col_degree [k] = fixQ ? 0 : cdeg ; + + /* get the element for this column (if any) */ + e = k - n1 + 1 ; + if (k < n_col - nempty_col) + { + esize = Esize ? Esize [k-n1] : cdeg ; + if (E [e]) + { + Int ncols, nrows ; + Unit *pp ; + pp = Memory + E [e] ; + GET_ELEMENT (ep, pp, Cols, Rows, ncols, nrows, C) ; + ASSERT (ncols == 1) ; + ASSERT (nrows == esize) ; + ASSERT (Cols [0] == k) ; + } + } + else + { + ASSERT (cdeg == 0) ; + esize = 0 ; + } + + clen = 0 ; + + for (pa = Ap [oldcol] ; pa < p2 ; pa++) + { + oldrow = Ai [pa] ; + newrow = Frpos [oldrow] ; + ASSIGN (x, Ax [pa], Az [pa]) ; + + /* scale the value using the scale factors, Rs */ + if (do_scale) + { +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the reciprocal */ + SCALE_RECIP (x, Rs [oldrow]) ; + } + else +#endif + { + /* divide instead */ + SCALE_DIV (x, Rs [oldrow]) ; + } + } + + rdeg = Rdeg [newrow] ; + if (newrow < n1 || rdeg > dense_row_threshold) + { + /* this entry goes in a row of U or into a dense row */ + DEBUG1 (("Singleton/dense row of U: k "ID" newrow "ID"\n", + k, newrow)) ; + if (--(Wp [newrow]) < 0) + { + DEBUGm4 (("bad row of U or A (too long)\n")) ; + return (FALSE) ; /* pattern changed */ + } + *(Rpi [newrow]++) = k ; + *(Rpx [newrow]++) = x ; + } + else + { + /* this entry goes in an initial element */ + DEBUG1 (("In element k "ID" e "ID" newrow "ID"\n", + k, e, newrow)) ; + if (clen >= esize) + { + DEBUGm4 (("bad A column (too long)\n")) ; + return (FALSE) ; /* pattern changed */ + } + ASSERT (E [e]) ; + ASSERT (k < n_col - nempty_col) ; + Rows [clen] = newrow ; + C [clen] = x ; + clen++ ; +#ifndef NDEBUG + if (Diagonal_map && (newrow == Diagonal_map [k])) + { + DEBUG0 (("Diagonal: old: row "ID" col "ID" : " + "new: row "ID" col "ID" : ", + oldrow, oldcol, newrow, k)) ; + EDEBUGk (0, x) ; + } +#endif + } + } + + if (clen != esize) + { + /* :: pattern change (singleton column too short) :: */ + DEBUGm4 (("bad A column (too short)\n")) ; + return (FALSE) ; /* pattern changed */ + } + } + + /* ---------------------------------------------------------------------- */ + /* free the Rpi and Rpx workspace at the tail end of memory */ + /* ---------------------------------------------------------------------- */ + + UMF_mem_free_tail_block (Numeric, rpi) ; + UMF_mem_free_tail_block (Numeric, rpx) ; + + /* ---------------------------------------------------------------------- */ + /* prune zeros and small entries from the singleton rows and columns */ + /* ---------------------------------------------------------------------- */ + + if (n1 > 0) + { + pnew = Lip [0] ; + ASSERT (pnew == 1) ; + for (k = 0 ; k < n1 ; k++) + { + DEBUGm4 (("\nPrune singleton L col "ID"\n", k)) ; + pnew = packsp (pnew, &Lip [k], &Lilen [k], drop, droptol, Memory) ; + Numeric->lnz += Lilen [k] ; + DEBUGm4 (("\nPrune singleton U row "ID"\n", k)) ; + pnew = packsp (pnew, &Uip [k], &Uilen [k], drop, droptol, Memory) ; + Numeric->unz += Uilen [k] ; + } + /* free the unused space at the head of memory */ + Numeric->ihead = pnew ; + } + + /* ---------------------------------------------------------------------- */ + /* initialize row degrees */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < n1 ; k++) + { + if (Wp [k] != 0) + { + /* :: pattern change (singleton row too short) :: */ + DEBUGm4 (("bad U singleton row (too short)\n")) ; + return (FALSE) ; /* pattern changed */ + } + } + + for (k = n1 ; k < n_row ; k++) + { + DEBUG1 (("Initial row degree k "ID" oldrow "ID" Rdeg "ID"\n", + k, Rperm_init [k], Rdeg [k])) ; + rdeg = Rdeg [k] ; + Row_degree [k] = rdeg ; + if (rdeg > dense_row_threshold && Wp [k] != 0) + { + /* :: pattern change (dense row too short) :: */ + DEBUGm4 (("bad dense row (too short)\n")) ; + return (FALSE) ; + } + } + +#ifndef NDEBUG + if (prefer_diagonal) + { + Int *InvCperm, newcol ; + Entry aij ; + UMF_dump_diagonal_map (Diagonal_map, Diagonal_imap, n1, nn, nempty) ; + InvCperm = (Int *) malloc (n_col * sizeof (Int)) ; + ASSERT (InvCperm != (Int *) NULL) ; + for (newcol = 0 ; newcol < n_col ; newcol++) + { + oldcol = Cperm_init [newcol] ; + InvCperm [oldcol] = newcol ; + } + DEBUGm3 (("Diagonal of P2*A:\n")) ; + for (oldcol = 0 ; oldcol < n_col ; oldcol++) + { + newcol = InvCperm [oldcol] ; + for (p = Ap [oldcol] ; p < Ap [oldcol+1] ; p++) + { + oldrow = Ai [p] ; + newrow = Frpos [oldrow] ; + ASSIGN (aij, Ax [p], Az [p]) ; + if (newrow == Diagonal_map [newcol]) + { + DEBUG0 (("old row "ID" col "ID" new row "ID" col "ID, + oldrow, oldcol, newrow, newcol)) ; + EDEBUGk (0, aij) ; + DEBUG0 ((" scaled ")) ; + if (do_scale) + { +#ifndef NRECIPROCAL + if (do_recip) + { + SCALE_RECIP (aij, Rs [oldrow]) ; + } + else +#endif + { + SCALE_DIV (aij, Rs [oldrow]) ; + } + } + EDEBUGk (0, aij) ; + DEBUG0 (("\n")) ; + } + } + } + free (InvCperm) ; + } +#endif + + Col_degree [n_col] = 0 ; + + /* ---------------------------------------------------------------------- */ + /* pack the element name space */ + /* ---------------------------------------------------------------------- */ + + if (empty_elements) + { + Int e2 = 0 ; + DEBUG0 (("\n\n============= Packing element space\n")) ; + for (e = 1 ; e <= Work->nel ; e++) + { + if (E [e]) + { + e2++ ; + E [e2] = E [e] ; + } + } + Work->nel = e2 ; + } + +#ifndef NDEBUG + DEBUG0 (("Number of initial elements: "ID"\n", Work->nel)) ; + for (e = 0 ; e <= Work->nel ; e++) UMF_dump_element (Numeric, Work,e,TRUE) ; +#endif + + for (e = Work->nel + 1 ; e < Work->elen ; e++) + { + E [e] = 0 ; + } + + /* Frpos no longer needed */ + for (row = 0 ; row <= n_row ; row++) + { + Frpos [row] = EMPTY ; + } + + /* clear Wp */ + for (i = 0 ; i <= nn ; i++) + { + Wp [i] = EMPTY ; + } + + DEBUG1 (("Kernel init head usage: "ID"\n", Numeric->ihead)) ; + + /* ---------------------------------------------------------------------- */ + /* build the tuple lists */ + /* ---------------------------------------------------------------------- */ + + /* if the memory usage changes, then the pattern has changed */ + + (void) UMF_tuple_lengths (Numeric, Work, &unused) ; + if (!UMF_build_tuples (Numeric, Work)) + { + /* :: pattern change (out of memory in umf_build_tuples) :: */ + /* We ran out of memory, which can only mean that */ + /* the pattern (Ap and or Ai) has changed (gotten larger). */ + DEBUG0 (("Pattern has gotten larger - build tuples failed\n")) ; + return (FALSE) ; /* pattern changed */ + } + + Numeric->init_usage = Numeric->max_usage ; + + /* ---------------------------------------------------------------------- */ + /* construct the row merge sets */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i <= Symbolic->nfr ; i++) + { + Work->Front_new1strow [i] = Symbolic->Front_1strow [i] ; + } + +#ifndef NDEBUG + UMF_dump_rowmerge (Numeric, Symbolic, Work) ; + DEBUG6 (("Column form of original matrix:\n")) ; + UMF_dump_col_matrix (Ax, +#ifdef COMPLEX + Az, +#endif + Ai, Ap, n_row, n_col, nz) ; + UMF_dump_memory (Numeric) ; + UMF_dump_matrix (Numeric, Work, FALSE) ; + DEBUG0 (("kernel init done...\n")) ; +#endif + + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_init.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_init.h new file mode 100644 index 0000000000..67deb41e69 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_init.h @@ -0,0 +1,19 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_kernel_init +( + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_wrapup.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_wrapup.c new file mode 100644 index 0000000000..6b32a1bce0 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_wrapup.c @@ -0,0 +1,466 @@ +/* ========================================================================== */ +/* === UMF_kernel_wrapup ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The matrix is factorized. Finish the LU data structure. */ + +#include "umf_internal.h" + +GLOBAL void UMF_kernel_wrapup +( + NumericType *Numeric, + SymbolicType *Symbolic, + WorkType *Work +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int i, k, col, row, llen, ulen, *ip, *Rperm, *Cperm, *Lilen, npiv, lp, + *Uilen, *Lip, *Uip, *Cperm_init, up, pivrow, pivcol, *Lpos, *Upos, *Wr, + *Wc, *Wp, *Frpos, *Fcpos, *Row_degree, *Col_degree, *Rperm_init, + n_row, n_col, n_inner, zero_pivot, nan_pivot, n1 ; + Entry *D, pivot_value ; + double d ; + +#ifndef NDEBUG + UMF_dump_matrix (Numeric, Work, FALSE) ; +#endif + + DEBUG0 (("Kernel complete, Starting Kernel wrapup\n")) ; + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + n_inner = MIN (n_row, n_col) ; + Rperm = Numeric->Rperm ; + Cperm = Numeric->Cperm ; + Lilen = Numeric->Lilen ; + Uilen = Numeric->Uilen ; + Upos = Numeric->Upos ; + Lpos = Numeric->Lpos ; + Lip = Numeric->Lip ; + Uip = Numeric->Uip ; + D = Numeric->D ; + + npiv = Work->npiv ; + Numeric->npiv = npiv ; + Numeric->ulen = Work->ulen ; + + ASSERT (n_row == Numeric->n_row) ; + ASSERT (n_col == Symbolic->n_col) ; + DEBUG0 (("Wrap-up: npiv "ID" ulen "ID"\n", npiv, Numeric->ulen)) ; + ASSERT (npiv <= n_inner) ; + + /* this will be nonzero only if matrix is singular or rectangular */ + ASSERT (IMPLIES (npiv == n_col, Work->ulen == 0)) ; + + /* ---------------------------------------------------------------------- */ + /* find the smallest and largest entries in D */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < npiv ; k++) + { + pivot_value = D [k] ; + ABS (d, pivot_value) ; + zero_pivot = SCALAR_IS_ZERO (d) ; + nan_pivot = SCALAR_IS_NAN (d) ; + + if (!zero_pivot) + { + /* the pivot is nonzero, but might be Inf or NaN */ + Numeric->nnzpiv++ ; + } + + if (k == 0) + { + Numeric->min_udiag = d ; + Numeric->max_udiag = d ; + } + else + { + /* min (abs (diag (U))) behaves as follows: If any entry is zero, + then the result is zero (regardless of the presence of NaN's). + Otherwise, if any entry is NaN, then the result is NaN. + Otherwise, the result is the smallest absolute value on the + diagonal of U. + */ + + if (SCALAR_IS_NONZERO (Numeric->min_udiag)) + { + if (zero_pivot || nan_pivot) + { + Numeric->min_udiag = d ; + } + else if (!SCALAR_IS_NAN (Numeric->min_udiag)) + { + /* d and min_udiag are both non-NaN */ + Numeric->min_udiag = MIN (Numeric->min_udiag, d) ; + } + } + + /* + max (abs (diag (U))) behaves as follows: If any entry is NaN + then the result is NaN. Otherise, the result is the largest + absolute value on the diagonal of U. + */ + + if (nan_pivot) + { + Numeric->max_udiag = d ; + } + else if (!SCALAR_IS_NAN (Numeric->max_udiag)) + { + /* d and max_udiag are both non-NaN */ + Numeric->max_udiag = MAX (Numeric->max_udiag, d) ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* check if matrix is singular or rectangular */ + /* ---------------------------------------------------------------------- */ + + Col_degree = Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Rperm ; /* for NON_PIVOTAL_ROW macro */ + + if (npiv < n_row) + { + /* finalize the row permutation */ + k = npiv ; + DEBUGm3 (("Singular pivot rows "ID" to "ID"\n", k, n_row-1)) ; + for (row = 0 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row)) + { + Rperm [row] = ONES_COMPLEMENT (k) ; + DEBUGm3 (("Singular row "ID" is k: "ID" pivot row\n", row, k)) ; + ASSERT (!NON_PIVOTAL_ROW (row)) ; + Lpos [row] = EMPTY ; + Uip [row] = EMPTY ; + Uilen [row] = 0 ; + k++ ; + } + } + ASSERT (k == n_row) ; + } + + if (npiv < n_col) + { + /* finalize the col permutation */ + k = npiv ; + DEBUGm3 (("Singular pivot cols "ID" to "ID"\n", k, n_col-1)) ; + for (col = 0 ; col < n_col ; col++) + { + if (NON_PIVOTAL_COL (col)) + { + Cperm [col] = ONES_COMPLEMENT (k) ; + DEBUGm3 (("Singular col "ID" is k: "ID" pivot row\n", col, k)) ; + ASSERT (!NON_PIVOTAL_COL (col)) ; + Upos [col] = EMPTY ; + Lip [col] = EMPTY ; + Lilen [col] = 0 ; + k++ ; + } + } + ASSERT (k == n_col) ; + } + + if (npiv < n_inner) + { + /* finalize the diagonal of U */ + DEBUGm3 (("Diag of U is zero, "ID" to "ID"\n", npiv, n_inner-1)) ; + for (k = npiv ; k < n_inner ; k++) + { + CLEAR (D [k]) ; + } + } + + /* save the pattern of the last row of U */ + if (Numeric->ulen > 0) + { + DEBUGm3 (("Last row of U is not empty\n")) ; + Numeric->Upattern = Work->Upattern ; + Work->Upattern = (Int *) NULL ; + } + + DEBUG2 (("Nnzpiv: "ID" npiv "ID"\n", Numeric->nnzpiv, npiv)) ; + ASSERT (Numeric->nnzpiv <= npiv) ; + if (Numeric->nnzpiv < n_inner && !SCALAR_IS_NAN (Numeric->min_udiag)) + { + /* the rest of the diagonal is zero, so min_udiag becomes 0, + * unless it is already NaN. */ + Numeric->min_udiag = 0.0 ; + } + + /* ---------------------------------------------------------------------- */ + /* size n_row, n_col workspaces that can be used here: */ + /* ---------------------------------------------------------------------- */ + + Frpos = Work->Frpos ; /* of size n_row+1 */ + Fcpos = Work->Fcpos ; /* of size n_col+1 */ + Wp = Work->Wp ; /* of size MAX(n_row,n_col)+1 */ + /* Work->Upattern ; cannot be used (in Numeric) */ + Wr = Work->Lpattern ; /* of size n_row+1 */ + Wc = Work->Wrp ; /* of size n_col+1 or bigger */ + + /* ---------------------------------------------------------------------- */ + /* construct Rperm from inverse permutations */ + /* ---------------------------------------------------------------------- */ + + /* use Frpos for temporary copy of inverse row permutation [ */ + + for (pivrow = 0 ; pivrow < n_row ; pivrow++) + { + k = Rperm [pivrow] ; + ASSERT (k < 0) ; + k = ONES_COMPLEMENT (k) ; + ASSERT (k >= 0 && k < n_row) ; + Wp [k] = pivrow ; + Frpos [pivrow] = k ; + } + for (k = 0 ; k < n_row ; k++) + { + Rperm [k] = Wp [k] ; + } + + /* ---------------------------------------------------------------------- */ + /* construct Cperm from inverse permutation */ + /* ---------------------------------------------------------------------- */ + + /* use Fcpos for temporary copy of inverse column permutation [ */ + + for (pivcol = 0 ; pivcol < n_col ; pivcol++) + { + k = Cperm [pivcol] ; + ASSERT (k < 0) ; + k = ONES_COMPLEMENT (k) ; + ASSERT (k >= 0 && k < n_col) ; + Wp [k] = pivcol ; + /* save a copy of the inverse column permutation in Fcpos */ + Fcpos [pivcol] = k ; + } + for (k = 0 ; k < n_col ; k++) + { + Cperm [k] = Wp [k] ; + } + +#ifndef NDEBUG + for (k = 0 ; k < n_col ; k++) + { + col = Cperm [k] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (Fcpos [col] == k) ; /* col is the kth pivot */ + } + for (k = 0 ; k < n_row ; k++) + { + row = Rperm [k] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (Frpos [row] == k) ; /* row is the kth pivot */ + } +#endif + +#ifndef NDEBUG + UMF_dump_lu (Numeric) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* permute Lpos, Upos, Lilen, Lip, Uilen, and Uip */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < npiv ; k++) + { + pivrow = Rperm [k] ; + Wr [k] = Uilen [pivrow] ; + Wp [k] = Uip [pivrow] ; + } + + for (k = 0 ; k < npiv ; k++) + { + Uilen [k] = Wr [k] ; + Uip [k] = Wp [k] ; + } + + for (k = 0 ; k < npiv ; k++) + { + pivrow = Rperm [k] ; + Wp [k] = Lpos [pivrow] ; + } + + for (k = 0 ; k < npiv ; k++) + { + Lpos [k] = Wp [k] ; + } + + for (k = 0 ; k < npiv ; k++) + { + pivcol = Cperm [k] ; + Wc [k] = Lilen [pivcol] ; + Wp [k] = Lip [pivcol] ; + } + + for (k = 0 ; k < npiv ; k++) + { + Lilen [k] = Wc [k] ; + Lip [k] = Wp [k] ; + } + + for (k = 0 ; k < npiv ; k++) + { + pivcol = Cperm [k] ; + Wp [k] = Upos [pivcol] ; + } + + for (k = 0 ; k < npiv ; k++) + { + Upos [k] = Wp [k] ; + } + + /* ---------------------------------------------------------------------- */ + /* terminate the last Uchain and last Lchain */ + /* ---------------------------------------------------------------------- */ + + Upos [npiv] = EMPTY ; + Lpos [npiv] = EMPTY ; + Uip [npiv] = EMPTY ; + Lip [npiv] = EMPTY ; + Uilen [npiv] = 0 ; + Lilen [npiv] = 0 ; + + /* ---------------------------------------------------------------------- */ + /* convert U to the new pivot order */ + /* ---------------------------------------------------------------------- */ + + n1 = Symbolic->n1 ; + + for (k = 0 ; k < n1 ; k++) + { + /* this is a singleton row of U */ + ulen = Uilen [k] ; + DEBUG4 (("K "ID" New U. ulen "ID" Singleton 1\n", k, ulen)) ; + if (ulen > 0) + { + up = Uip [k] ; + ip = (Int *) (Numeric->Memory + up) ; + for (i = 0 ; i < ulen ; i++) + { + col = *ip ; + DEBUG4 ((" old col "ID" new col "ID"\n", col, Fcpos [col])); + ASSERT (col >= 0 && col < n_col) ; + *ip++ = Fcpos [col] ; + } + } + } + + for (k = n1 ; k < npiv ; k++) + { + up = Uip [k] ; + if (up < 0) + { + /* this is the start of a new Uchain (with a pattern) */ + ulen = Uilen [k] ; + DEBUG4 (("K "ID" New U. ulen "ID" End_Uchain 1\n", k, ulen)) ; + if (ulen > 0) + { + up = -up ; + ip = (Int *) (Numeric->Memory + up) ; + for (i = 0 ; i < ulen ; i++) + { + col = *ip ; + DEBUG4 ((" old col "ID" new col "ID"\n", col, Fcpos [col])); + ASSERT (col >= 0 && col < n_col) ; + *ip++ = Fcpos [col] ; + } + } + } + } + + ulen = Numeric->ulen ; + if (ulen > 0) + { + /* convert last pivot row of U to the new pivot order */ + DEBUG4 (("K "ID" (last)\n", k)) ; + for (i = 0 ; i < ulen ; i++) + { + col = Numeric->Upattern [i] ; + DEBUG4 ((" old col "ID" new col "ID"\n", col, Fcpos [col])) ; + Numeric->Upattern [i] = Fcpos [col] ; + } + } + + /* Fcpos no longer needed ] */ + + /* ---------------------------------------------------------------------- */ + /* convert L to the new pivot order */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < n1 ; k++) + { + llen = Lilen [k] ; + DEBUG4 (("K "ID" New L. llen "ID" Singleton col\n", k, llen)) ; + if (llen > 0) + { + lp = Lip [k] ; + ip = (Int *) (Numeric->Memory + lp) ; + for (i = 0 ; i < llen ; i++) + { + row = *ip ; + DEBUG4 ((" old row "ID" new row "ID"\n", row, Frpos [row])) ; + ASSERT (row >= 0 && row < n_row) ; + *ip++ = Frpos [row] ; + } + } + } + + for (k = n1 ; k < npiv ; k++) + { + llen = Lilen [k] ; + DEBUG4 (("K "ID" New L. llen "ID" \n", k, llen)) ; + if (llen > 0) + { + lp = Lip [k] ; + if (lp < 0) + { + /* this starts a new Lchain */ + lp = -lp ; + } + ip = (Int *) (Numeric->Memory + lp) ; + for (i = 0 ; i < llen ; i++) + { + row = *ip ; + DEBUG4 ((" old row "ID" new row "ID"\n", row, Frpos [row])) ; + ASSERT (row >= 0 && row < n_row) ; + *ip++ = Frpos [row] ; + } + } + } + + /* Frpos no longer needed ] */ + + /* ---------------------------------------------------------------------- */ + /* combine symbolic and numeric permutations */ + /* ---------------------------------------------------------------------- */ + + Cperm_init = Symbolic->Cperm_init ; + Rperm_init = Symbolic->Rperm_init ; + + for (k = 0 ; k < n_row ; k++) + { + Rperm [k] = Rperm_init [Rperm [k]] ; + } + + for (k = 0 ; k < n_col ; k++) + { + Cperm [k] = Cperm_init [Cperm [k]] ; + } + + /* Work object will be freed immediately upon return (to UMF_kernel */ + /* and then to UMFPACK_numeric). */ +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_wrapup.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_wrapup.h new file mode 100644 index 0000000000..4550821eba --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_kernel_wrapup.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_kernel_wrapup +( + NumericType *Numeric, + SymbolicType *Symbolic, + WorkType *Work +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_local_search.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_local_search.c new file mode 100644 index 0000000000..7c54cd2aa8 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_local_search.c @@ -0,0 +1,1949 @@ +/* ========================================================================== */ +/* === UMF_local_search ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Perform pivot search to find pivot row and pivot column. + The pivot column is selected from the candidate set. The candidate set + corresponds to a supercolumn from colamd or UMF_analyze. The pivot column + is then removed from that set. Constructs the pivot column pattern and + values. Called by umf_kernel. Returns UMFPACK_OK if successful, or + UMFPACK_WARNING_singular_matrix or UMFPACK_ERROR_different_pattern if not. +*/ + +#include "umf_internal.h" +#include "umf_row_search.h" +#include "umf_mem_free_tail_block.h" + +/* Version 4.1: relaxed amalgamation control parameters are now fixed, and + * cannot be changed via Control [..] settings, as they could in Version 4.0. */ +#define RELAX1 0.25 /* this was UMFPACK_DEFAULT_RELAXED_AMALGAMATION */ +#define SYM_RELAX1 0.0 /* this is new to Version 4.1 */ +#define RELAX2 0.1 /* this was UMFPACK_DEFAULT_RELAXED2_AMALGAMATION */ +#define RELAX3 0.125 /* this was UMFPACK_DEFAULT_RELAXED3_AMALGAMATION */ + +/* ========================================================================== */ +/* === remove_candidate ===================================================== */ +/* ========================================================================== */ + +/* Remove a column from the set of candidate pivot columns. */ + +PRIVATE void remove_candidate (Int jj, WorkType *Work, SymbolicType *Symbolic) +{ + +#ifndef NDEBUG + Int j ; + DEBUGm2 (("pivot column Candidates before remove: nCand "ID" ncand "ID + " lo "ID" hi "ID" jj "ID"\n", Work->nCandidates, Work->ncand, + Work->lo, Work->hi, jj)) ; + for (j = 0 ; j < Work->nCandidates ; j++) + { + Int col = Work->Candidates [j] ; + DEBUGm2 ((ID" ", col)); + ASSERT (col >= 0 && col < Work->n_col) ; + /* ASSERT (NON_PIVOTAL_COL (col)) ; */ + ASSERT (col >= Work->lo && col <= Work->hi) ; + } + DEBUGm2 (("\n")) ; +#endif + + if (Symbolic->fixQ) + { + DEBUGm2 (("FixQ\n")) ; + /* do not modify the column ordering */ + ASSERT (Work->nCandidates == 1) ; + ASSERT (jj == 0) ; + if (Work->ncand > 1) + { + Work->Candidates [0] = Work->nextcand++ ; + } + else + { + Work->nCandidates = 0 ; + } + } + else + { + /* place the next candidate in the set */ + if (Work->ncand > MAX_CANDIDATES) + { + Work->Candidates [jj] = Work->nextcand++ ; + } + else + { + ASSERT (Work->nCandidates == Work->ncand) ; + Work->Candidates [jj] = Work->Candidates [Work->ncand - 1] ; + Work->Candidates [Work->ncand - 1] = EMPTY ; + Work->nCandidates-- ; + } + } + Work->ncand-- ; + +#ifndef NDEBUG + DEBUGm2 (("pivot column Candidates after remove: nCand "ID" ncand "ID + " lo "ID" hi "ID" jj "ID"\n", Work->nCandidates, Work->ncand, Work->lo, + Work->hi, jj)) ; + for (j = 0 ; j < Work->nCandidates ; j++) + { + Int col = Work->Candidates [j] ; + DEBUGm2 ((ID" ", col)); + ASSERT (col >= 0 && col < Work->n_col) ; + /* ASSERT (NON_PIVOTAL_COL (col)) ; */ + ASSERT (col >= Work->lo && col <= Work->hi) ; + } + DEBUGm2 (("\n")) ; + ASSERT (Work->ncand >= 0) ; + ASSERT (Work->nCandidates <= Work->ncand) ; +#endif +} + +/* ========================================================================== */ +/* === UMF_local_search ===================================================== */ +/* ========================================================================== */ + +GLOBAL Int UMF_local_search +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Entry *Flblock, *Fublock, *Fs, *Fcblock, *C, *Wx, *Wy, *Fu, *Flublock, + *Flu ; + double relax1 ; + Int pos, nrows, *Cols, *Rows, e, f, status, max_cdeg, fnzeros, nb, j, col, + i, row, cdeg_in, rdeg [2][2], fnpiv, nothing [2], new_LUsize, + pivrow [2][2], pivcol [2], *Wp, *Fcpos, *Frpos, new_fnzeros, cdeg_out, + *Wm, *Wio, *Woi, *Woo, *Frows, *Fcols, fnrows, fncols, *E, deg, nr_in, + nc, thiscost, bestcost, nr_out, do_update, extra_cols, extra_rows, + extra_zeros, relaxed_front, do_extend, fnr_curr, fnc_curr, tpi, + *Col_tuples, *Col_degree, *Col_tlen, jj, jcand [2], freebie [2], + did_rowmerge, fnrows_new [2][2], fncols_new [2][2], search_pivcol_out, + *Diagonal_map, *Diagonal_imap, row2, col2 ; + Unit *Memory, *p ; + Tuple *tp, *tpend, *tp1, *tp2 ; + Element *ep ; + +#ifndef NDEBUG + Int debug_ok, n_row, n_col, *Row_degree ; + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro only */ +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + Memory = Numeric->Memory ; + E = Work->E ; + Col_degree = Numeric->Cperm ; + + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + + Wx = Work->Wx ; + Wy = Work->Wy ; + Wp = Work->Wp ; + Wm = Work->Wm ; + Woi = Work->Woi ; + Wio = Work->Wio ; + Woo = Work->Woo ; + Fcpos = Work->Fcpos ; + Frpos = Work->Frpos ; + Frows = Work->Frows ; + Fcols = Work->Fcols ; + fnrows = Work->fnrows ; + fncols = Work->fncols ; + nb = Work->nb ; + fnr_curr = Work->fnr_curr ; + fnc_curr = Work->fnc_curr ; + fnpiv = Work->fnpiv ; + nothing [0] = EMPTY ; + nothing [1] = EMPTY ; + relax1 = (Symbolic->prefer_diagonal) ? SYM_RELAX1 : RELAX1 ; + fnzeros = Work->fnzeros ; + new_fnzeros = fnzeros ; + jj = EMPTY ; + + Fcblock = Work->Fcblock ; /* current contribution block */ + Flblock = Work->Flblock ; /* current L block */ + Fublock = Work->Fublock ; /* current U block */ + Flublock = Work->Flublock ; /* current LU block */ + + /* The pivot column degree cannot exceed max_cdeg */ + max_cdeg = Work->fnrows_max ; + ASSERT (Work->fnrows_max <= Symbolic->maxnrows) ; + ASSERT (Work->fncols_max <= Symbolic->maxncols) ; + + if (fnrows == 0 && fncols == 0) + { + /* frontal matrix is empty */ + Work->firstsuper = Work->ksuper ; + } + +#ifndef NDEBUG + n_row = Work->n_row ; + n_col = Work->n_col ; + DEBUG2 (("\n========LOCAL SEARCH: current frontal matrix: ========= \n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; + if (UMF_debug > 0 || MAX (n_row, n_col) < 1000) + { + for (i = 0 ; i < MAX (n_row, n_col) ; i++) + { + ASSERT (Wp [i] < 0) ; + } + } + + DEBUGm2 ((ID" pivot column Candidates: lo "ID" hi "ID"\n", + Work->nCandidates, Work->lo, Work->hi)) ; + for (j = 0 ; j < Work->nCandidates ; j++) + { + col = Work->Candidates [j] ; + DEBUGm2 ((ID" ", col)); + ASSERT (col >= 0 && col < n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + ASSERT (col >= Work->lo && col <= Work->hi) ; + } + + DEBUGm2 (("\n")) ; + /* there are no 0-by-c or r-by-0 fronts, where c and r are > 0 */ + /* a front is either 0-by-0, or r-by-c */ + DEBUG2 (("\n\n::: "ID" : Npiv: "ID" + fnpiv "ID" = "ID". " + "size "ID"-by-"ID"\n", Work->frontid, + Work->npiv, Work->fnpiv, Work->npiv + Work->fnpiv, fnrows, fncols)) ; + ASSERT ((fnrows == 0 && fncols == 0) ||(fnrows != 0 && fncols != 0)) ; +#endif + + /* ====================================================================== */ + /* === PIVOT SEARCH ===================================================== */ + /* ====================================================================== */ + + /* initialize */ + + pivcol [IN] = EMPTY ; + pivcol [OUT] = EMPTY ; + + cdeg_in = Int_MAX ; + cdeg_out = Int_MAX ; + + pivrow [IN][IN] = EMPTY ; + pivrow [IN][OUT] = EMPTY ; + pivrow [OUT][IN] = EMPTY ; + pivrow [OUT][OUT] = EMPTY ; + + rdeg [IN][IN] = Int_MAX ; + rdeg [IN][OUT] = Int_MAX ; + rdeg [OUT][IN] = Int_MAX ; + rdeg [OUT][OUT] = Int_MAX ; + + freebie [IN] = FALSE ; + freebie [OUT] = FALSE ; + + Work->pivot_case = EMPTY ; + bestcost = EMPTY ; + + nr_out = EMPTY ; + nr_in = EMPTY ; + + jcand [IN] = EMPTY ; + jcand [OUT] = EMPTY ; + + fnrows_new [IN][IN] = EMPTY ; + fnrows_new [IN][OUT] = EMPTY ; + fnrows_new [OUT][IN] = EMPTY ; + fnrows_new [OUT][OUT] = EMPTY ; + + fncols_new [IN][IN] = EMPTY ; + fncols_new [IN][OUT] = EMPTY ; + fncols_new [OUT][IN] = EMPTY ; + fncols_new [OUT][OUT] = EMPTY ; + +#ifndef NDEBUG + /* check Frpos */ + DEBUG4 (("Check Frpos : fnrows "ID" col "ID" maxcdeg "ID"\n", + fnrows, pivcol [IN], max_cdeg)) ; + for (i = 0 ; i < fnrows ; i++) + { + row = Frows [i] ; + DEBUG4 ((" row: "ID"\n", row)) ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (Frpos [row] == i) ; + } + DEBUG4 (("All:\n")) ; + if (UMF_debug > 0 || n_row < 1000) + { + Int cnt = fnrows ; + for (row = 0 ; row < n_row ; row++) + { + if (Frpos [row] == EMPTY) + { + cnt++ ; + } + else + { + DEBUG4 ((" row: "ID" pos "ID"\n", row, Frpos [row])) ; + } + } + ASSERT (cnt == n_row) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* find shortest column in the front, and shortest column not in the */ + /* front, from the candidate pivot column set */ + /* ---------------------------------------------------------------------- */ + + /* If there are too many candidates, then only look at the first */ + /* MAX_CANDIDATES of them. Otherwise, if there are O(n) candidates, */ + /* this code could take O(n^2) time. */ + + /* ------------------------------------------------------------------ */ + /* look in the candidate set for the best column */ + /* ------------------------------------------------------------------ */ + + DEBUG2 (("Max candidates %d, Work->ncand "ID" jmax "ID"\n", + MAX_CANDIDATES, Work->ncand, Work->nCandidates)) ; + col = Work->Candidates [0] ; + ASSERT (Work->nCandidates > 0) ; + DEBUG3 (("Pivot column candidate: "ID" j = "ID"\n", col, j)) ; + ASSERT (col >= 0 && col < n_col) ; + + /* there is no Col_degree if fixQ is true */ + deg = Symbolic->fixQ ? EMPTY : Col_degree [col] ; + +#ifndef NDEBUG + DEBUG3 (("Pivot column candidate: "ID" cost: "ID" Fcpos[col] "ID"\n", + col, deg, Fcpos [col])) ; + UMF_dump_rowcol (1, Numeric, Work, col, !Symbolic->fixQ) ; + if (Symbolic->fixQ) + { + DEBUG1 (("FIXQ: Candidates "ID" pivcol "ID" npiv "ID" fnpiv "ID + " ndiscard "ID "\n", Work->nCandidates, col, Work->npiv, + Work->fnpiv, Work->ndiscard)) ; + ASSERT (Work->nCandidates == 1) ; + ASSERT (col == Work->npiv + Work->fnpiv + Work->ndiscard) ; + } +#endif + + if (Fcpos [col] >= 0) + { + /* best column in front, so far */ + pivcol [IN] = col ; + cdeg_in = deg ; /* ignored, if fixQ is true */ + jcand [IN] = 0 ; + } + else + { + /* best column not in front, so far */ + pivcol [OUT] = col ; + cdeg_out = deg ; /* ignored, if fixQ is true */ + jcand [OUT] = 0 ; + } + + /* look at the rest of the candidates */ + for (j = 1 ; j < Work->nCandidates ; j++) + { + col = Work->Candidates [j] ; + + DEBUG3 (("Pivot col candidate: "ID" j = "ID"\n", col, j)) ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (!Symbolic->fixQ) ; + deg = Col_degree [col] ; +#ifndef NDEBUG + DEBUG3 (("Pivot col candidate: "ID" cost: "ID" Fcpos[col] "ID"\n", + col, deg, Fcpos [col])) ; + UMF_dump_rowcol (1, Numeric, Work, col, !Symbolic->fixQ) ; +#endif + if (Fcpos [col] >= 0) + { +#ifndef NDEBUG + Int fs ; + fs = Fcpos [col] / fnr_curr ; + ASSERT (fs >= 0 && fs < fncols) ; +#endif + if (deg < cdeg_in || (deg == cdeg_in && col < pivcol [IN])) + { + /* best column in front, so far */ + pivcol [IN] = col ; + cdeg_in = deg ; + jcand [IN] = j ; + } + } + else + { + if (deg < cdeg_out || (deg == cdeg_out && col < pivcol [OUT])) + { + /* best column not in front, so far */ + pivcol [OUT] = col ; + cdeg_out = deg ; + jcand [OUT] = j ; + } + } + } + + DEBUG2 (("Pivcol in "ID" out "ID"\n", pivcol [IN], pivcol [OUT])) ; + ASSERT ((pivcol [IN] >= 0 && pivcol [IN] < n_col) + || (pivcol [OUT] >= 0 && pivcol [OUT] < n_col)) ; + + cdeg_in = EMPTY ; + cdeg_out = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* construct candidate column in front, and search for pivot rows */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + /* check Frpos */ + DEBUG4 (("Prior to col update: fnrows "ID" col "ID" maxcdeg "ID"\n", + fnrows, pivcol [IN], max_cdeg)) ; + for (i = 0 ; i < fnrows ; i++) + { + row = Frows [i] ; + DEBUG4 ((" row: "ID"\n", row)) ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (Frpos [row] == i) ; + } + DEBUG4 (("All:\n")) ; + if (UMF_debug > 0 || n_row < 1000) + { + Int cnt = fnrows ; + for (row = 0 ; row < n_row ; row++) + { + if (Frpos [row] == EMPTY) + { + cnt++ ; + } + else + { + DEBUG4 ((" row: "ID" pos "ID"\n", row, Frpos [row])) ; + } + } + ASSERT (cnt == n_row) ; + } +#endif + + if (pivcol [IN] != EMPTY) + { + +#ifndef NDEBUG + DEBUG2 (("col[IN] column "ID" in front at position = "ID"\n", + pivcol [IN], Fcpos [pivcol [IN]])) ; + UMF_dump_rowcol (1, Numeric, Work, pivcol [IN], !Symbolic->fixQ) ; +#endif + + /* the only way we can have a pivcol[IN] is if the front is not empty */ + ASSERT (fnrows > 0 && fncols > 0) ; + + DEBUG4 (("Update pivot column:\n")) ; + Fs = Fcblock + Fcpos [pivcol [IN]] ; + Fu = Fublock + (Fcpos [pivcol [IN]] / fnr_curr) ; + Flu = Flublock + fnpiv * nb ; + + /* ------------------------------------------------------------------ */ + /* copy the pivot column from the U block into the LU block */ + /* ------------------------------------------------------------------ */ + + /* This copy is permanent if the pivcol [IN] is chosen. */ + for (i = 0 ; i < fnpiv ; i++) + { + Flu [i] = Fu [i*fnc_curr] ; + } + + /* ------------------------------------------------------------------ */ + /* update the pivot column in the LU block using a triangular solve */ + /* ------------------------------------------------------------------ */ + + /* This work will be discarded if the pivcol [OUT] is chosen instead. + * It is permanent if the pivcol [IN] is chosen. */ + + if (fnpiv > 1) + { + /* solve Lx=b, where b = U (:,k), stored in the LU block */ + +#ifdef USE_NO_BLAS + + /* no BLAS available - use plain C code instead */ + Entry *Flub = Flublock ; + for (j = 0 ; j < fnpiv ; j++) + { + Entry Fuj = Flu [j] ; +#pragma ivdep + for (i = j+1 ; i < fnpiv ; i++) + { + /* Flu [i] -= Flublock [i + j*nb] * Flu [j] ; */ + MULT_SUB (Flu [i], Flub [i], Fuj) ; + } + Flub += nb ; + } + +#else + BLAS_TRSV (fnpiv, Flublock, Flu, nb) ; +#endif + + } + + /* ------------------------------------------------------------------ */ + /* copy the pivot column from the C block into Wy */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < fnrows ; i++) + { + Wy [i] = Fs [i] ; + } + + /* ------------------------------------------------------------------ */ + /* update the pivot column of L using a matrix-vector multiply */ + /* ------------------------------------------------------------------ */ + + /* this work will be discarded if the pivcol [OUT] is chosen instead */ + +#ifdef USE_NO_BLAS + /* no BLAS available - use plain C code instead */ + for (j = 0 ; j < fnpiv ; j++) + { + Entry Fuj, *Flub = Flblock + j * fnr_curr ; + Fuj = Flu [j] ; + if (IS_NONZERO (Fuj)) + { +#pragma ivdep + for (i = 0 ; i < fnrows ; i++) + { + /* Wy [i] -= Flblock [i+j*fnr_curr] * Fuj ; */ + MULT_SUB (Wy [i], Flub [i], Fuj) ; + } + } + /* Flblock += fnr_curr ; */ + } +#else + /* Using 1-based notation: + * Wy (1:fnrows) -= Flblock (1:fnrows,1:fnpiv) * Flu (1:fnpiv) */ + BLAS_GEMV (fnrows, fnpiv, Flblock, Flu, Wy, fnr_curr) ; +#endif + + /* ------------------------------------------------------------------ */ + +#ifndef NDEBUG + DEBUG2 (("Wy after update: fnrows="ID"\n", fnrows)) ; + DEBUG4 ((" fnpiv="ID" \n", fnpiv)) ; + for (i = 0 ; i < fnrows ; i++) + { + DEBUG4 ((ID" "ID" "ID, i, Frows [i], Frpos [Frows [i]])) ; + EDEBUG4 (Wy [i]) ; + DEBUG4 (("\n")) ; + } +#endif + + /* ------------------------------------------------------------------ */ + /* construct the candidate column */ + /* ------------------------------------------------------------------ */ + + cdeg_in = fnrows ; + +#ifndef NDEBUG + /* check Frpos */ + DEBUG4 (("After col update: fnrows "ID" col "ID" maxcdeg "ID"\n", + fnrows, pivcol [IN], max_cdeg)) ; + for (i = 0 ; i < fnrows ; i++) + { + row = Frows [i] ; + DEBUG4 ((" row: "ID"\n", row)) ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (Frpos [row] == i) ; + } + DEBUG4 (("All:\n")) ; + if (UMF_debug > 0 || n_row < 1000) + { + Int cnt = fnrows ; + for (row = 0 ; row < n_row ; row++) + { + if (Frpos [row] == EMPTY) + { + cnt++ ; + } + else + { + DEBUG4 ((" row: "ID" pos "ID"\n", row, Frpos [row])) ; + } + } + ASSERT (cnt == n_row) ; + } +#endif + +#ifndef NDEBUG + /* check Frpos */ + DEBUG4 (("COL ASSEMBLE: cdeg "ID"\nREDUCE COL in "ID" max_cdeg "ID"\n", + cdeg_in, pivcol [IN], max_cdeg)) ; + for (i = 0 ; i < cdeg_in ; i++) + { + row = Frows [i] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (Frpos [row] == i) ; + } + if (UMF_debug > 0 || n_row < 1000) + { + Int cnt = cdeg_in ; + for (row = 0 ; row < n_row ; row++) + { + if (Frpos [row] == EMPTY) cnt++ ; + } + ASSERT (cnt == n_row) ; + } +#endif + + /* assemble column into Wy */ + + ASSERT (pivcol [IN] >= 0 && pivcol [IN] < n_col) ; + ASSERT (NON_PIVOTAL_COL (pivcol [IN])) ; + + tpi = Col_tuples [pivcol [IN]] ; + if (tpi) + { + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Col_tlen [pivcol [IN]] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + if (Cols [f] == EMPTY) continue ; /* column already assembled */ + ASSERT (pivcol [IN] == Cols [f]) ; + + Rows = Cols + ep->ncols ; + nrows = ep->nrows ; + p += UNITS (Int, ep->ncols + nrows) ; + C = ((Entry *) p) + f * nrows ; + + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) /* skip this if already gone from element */ + { + ASSERT (row < n_row) ; + pos = Frpos [row] ; + if (pos < 0) + { + /* new entry in the pattern - save Frpos */ + ASSERT (cdeg_in < n_row) ; + if (cdeg_in >= max_cdeg) + { + /* :: pattern change (cdeg in failure) :: */ + DEBUGm4 (("cdeg_in failure\n")) ; + return (UMFPACK_ERROR_different_pattern) ; + } + Frpos [row] = cdeg_in ; + Frows [cdeg_in] = row ; + Wy [cdeg_in++] = C [i] ; + } + else + { + /* entry already in pattern - sum values in Wy */ + /* Wy [pos] += C [i] ; */ + ASSERT (pos < max_cdeg) ; + ASSEMBLE (Wy [pos], C [i]) ; + } + } + } + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Col_tlen [pivcol [IN]] = tp2 - tp1 ; + } + + /* ------------------------------------------------------------------ */ + +#ifndef NDEBUG + /* check Frpos again */ + DEBUG4 (("COL DONE: cdeg "ID"\nREDUCE COL in "ID" max_cdeg "ID"\n", + cdeg_in, pivcol [IN], max_cdeg)) ; + for (i = 0 ; i < cdeg_in ; i++) + { + row = Frows [i] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (Frpos [row] == i) ; + } + if (UMF_debug > 0 || n_row < 1000) + { + Int cnt = cdeg_in ; + for (row = 0 ; row < n_row ; row++) + { + if (Frpos [row] == EMPTY) cnt++ ; + } + ASSERT (cnt == n_row) ; + } +#endif + +#ifndef NDEBUG + DEBUG4 (("Reduced column: cdeg in "ID" fnrows_max "ID"\n", + cdeg_in, Work->fnrows_max)) ; + for (i = 0 ; i < cdeg_in ; i++) + { + DEBUG4 ((" "ID" "ID" "ID, i, Frows [i], Frpos [Frows [i]])) ; + EDEBUG4 (Wy [i]) ; + DEBUG4 (("\n")) ; + ASSERT (i == Frpos [Frows [i]]) ; + } + ASSERT (cdeg_in <= Work->fnrows_max) ; +#endif + + /* ------------------------------------------------------------------ */ + /* cdeg_in is now the exact degree of this column */ + /* ------------------------------------------------------------------ */ + + nr_in = cdeg_in - fnrows ; + + /* since there are no 0-by-x fronts, if there is a pivcol [IN] the */ + /* front must have at least one row. */ + ASSERT (cdeg_in > 0) ; + + /* new degree of pivcol [IN], excluding current front is nr_in */ + /* column expands by nr_in rows */ + + /* ------------------------------------------------------------------ */ + /* search for two candidate pivot rows */ + /* ------------------------------------------------------------------ */ + + /* for the IN_IN pivot row (if any), */ + /* extend the pattern in place, using Fcols */ + status = UMF_row_search (Numeric, Work, Symbolic, + fnrows, cdeg_in, Frows, Frpos, /* pattern of column to search */ + pivrow [IN], rdeg [IN], Fcols, Wio, nothing, Wy, + pivcol [IN], freebie) ; + ASSERT (!freebie [IN] && !freebie [OUT]) ; + + /* ------------------------------------------------------------------ */ + /* fatal error if matrix pattern has changed since symbolic analysis */ + /* ------------------------------------------------------------------ */ + + if (status == UMFPACK_ERROR_different_pattern) + { + /* :: pattern change (row search IN failure) :: */ + DEBUGm4 (("row search IN failure\n")) ; + return (UMFPACK_ERROR_different_pattern) ; + } + + /* ------------------------------------------------------------------ */ + /* we now must have a structural pivot */ + /* ------------------------------------------------------------------ */ + + /* Since the pivcol[IN] exists, there must be at least one row in the */ + /* current frontal matrix, and so we must have found a structural */ + /* pivot. The numerical value might be zero, of course. */ + + ASSERT (status != UMFPACK_WARNING_singular_matrix) ; + + /* ------------------------------------------------------------------ */ + /* evaluate IN_IN option */ + /* ------------------------------------------------------------------ */ + + if (pivrow [IN][IN] != EMPTY) + { + /* The current front would become an (implicit) LUson. + * Both candidate pivot row and column are in the current front. + * Cost is how much the current front would expand */ + + /* pivrow[IN][IN] candidates are not found via row merge search */ + + ASSERT (fnrows >= 0 && fncols >= 0) ; + + ASSERT (cdeg_in > 0) ; + nc = rdeg [IN][IN] - fncols ; + + thiscost = + /* each column in front (except pivot column) grows by nr_in: */ + (nr_in * (fncols - 1)) + + /* new columns not in old front: */ + (nc * (cdeg_in - 1)) ; + + /* no extra cost to relaxed amalgamation */ + + ASSERT (fnrows + nr_in == cdeg_in) ; + ASSERT (fncols + nc == rdeg [IN][IN]) ; + + /* size of relaxed front (after pivot row column removed): */ + fnrows_new [IN][IN] = (fnrows-1) + nr_in ; + fncols_new [IN][IN] = (fncols-1) + nc ; + /* relaxed_front = fnrows_new [IN][IN] * fncols_new [IN][IN] ; */ + + do_extend = TRUE ; + + DEBUG2 (("Evaluating option IN-IN:\n")) ; + DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_in "ID" nc "ID"\n", + Work->fnzeros, fnpiv, nr_in, nc)) ; + DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ; + + /* determine if BLAS-3 update should be applied before extending. */ + /* update if too many zero entries accumulate in the LU block */ + fnzeros = Work->fnzeros + fnpiv * (nr_in + nc) ; + + DEBUG2 (("fnzeros "ID"\n", fnzeros)) ; + + new_LUsize = (fnpiv+1) * (fnrows + nr_in + fncols + nc) ; + + DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ; + + /* There are fnpiv pivots currently in the front. This one + * will be the (fnpiv+1)st pivot, if it is extended. */ + + /* RELAX2 parameter uses a double relop, but ignore NaN case: */ + do_update = fnpiv > 0 && + (((double) fnzeros) / ((double) new_LUsize)) > RELAX2 ; + + DEBUG2 (("do_update "ID"\n", do_update)) + + DEBUG2 (("option IN IN : nr "ID" nc "ID" cost "ID"(0) relax "ID + "\n", nr_in, nc, thiscost, do_extend)) ; + + /* this is the best option seen so far */ + Work->pivot_case = IN_IN ; + bestcost = thiscost ; + + /* do the amalgamation and extend the front */ + Work->do_extend = do_extend ; + Work->do_update = do_update ; + new_fnzeros = fnzeros ; + + } + + /* ------------------------------------------------------------------ */ + /* evaluate IN_OUT option */ + /* ------------------------------------------------------------------ */ + + if (pivrow [IN][OUT] != EMPTY) + { + /* The current front would become a Uson of the new front. + * Candidate pivot column is in the current front, but the + * candidate pivot row is not. */ + + ASSERT (fnrows >= 0 && fncols > 0) ; + ASSERT (cdeg_in > 0) ; + + /* must be at least one row outside the front */ + /* (the pivrow [IN][OUT] itself) */ + ASSERT (nr_in >= 1) ; + + /* count columns not in current front */ + nc = 0 ; +#ifndef NDEBUG + debug_ok = FALSE ; +#endif + for (i = 0 ; i < rdeg [IN][OUT] ; i++) + { + col = Wio [i] ; + DEBUG4 (("counting col "ID" Fcpos[] = "ID"\n", col, + Fcpos [col])) ; + ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ; + if (Fcpos [col] < 0) nc++ ; +#ifndef NDEBUG + /* we must see the pivot column somewhere */ + if (col == pivcol [IN]) + { + ASSERT (Fcpos [col] >= 0) ; + debug_ok = TRUE ; + } +#endif + } + ASSERT (debug_ok) ; + + thiscost = + /* each row in front grows by nc: */ + (nc * fnrows) + + /* new rows not affected by front: */ + ((nr_in-1) * (rdeg [IN][OUT]-1)) ; + + /* check the cost of relaxed IN_OUT amalgamation */ + + extra_cols = ((fncols-1) + nc ) - (rdeg [IN][OUT] - 1) ; + ASSERT (extra_cols >= 0) ; + ASSERT (fncols + nc == extra_cols + rdeg [IN][OUT]) ; + extra_zeros = (nr_in-1) * extra_cols ; /* symbolic fill-in */ + + ASSERT (fnrows + nr_in == cdeg_in) ; + ASSERT (fncols + nc == rdeg [IN][OUT] + extra_cols) ; + + /* size of relaxed front (after pivot column removed): */ + fnrows_new [IN][OUT] = fnrows + (nr_in-1) ; + fncols_new [IN][OUT] = (fncols-1) + nc ; + relaxed_front = fnrows_new [IN][OUT] * fncols_new [IN][OUT] ; + + /* do relaxed amalgamation if the extra zeros are no more */ + /* than a fraction (default 0.25) of the relaxed front */ + /* if relax = 0: no extra zeros allowed */ + /* if relax = +inf: always amalgamate */ + + /* relax parameter uses a double relop, but ignore NaN case: */ + if (extra_zeros == 0) + { + do_extend = TRUE ; + } + else + { + do_extend = ((double) extra_zeros) < + (relax1 * (double) relaxed_front) ; + } + + if (do_extend) + { + /* count the cost of relaxed amalgamation */ + thiscost += extra_zeros ; + + DEBUG2 (("Evaluating option IN-OUT:\n")) ; + DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_in "ID" nc "ID"\n", + Work->fnzeros, fnpiv, nr_in, nc)) ; + DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ; + + /* determine if BLAS-3 update to be applied before extending. */ + /* update if too many zero entries accumulate in the LU block */ + fnzeros = Work->fnzeros + fnpiv * (nr_in + nc) ; + + DEBUG2 (("fnzeros "ID"\n", fnzeros)) ; + + new_LUsize = (fnpiv+1) * (fnrows + nr_in + fncols + nc) ; + + DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ; + + /* RELAX3 parameter uses a double relop, ignore NaN case: */ + do_update = fnpiv > 0 && + (((double) fnzeros) / ((double) new_LUsize)) > RELAX3 ; + DEBUG2 (("do_update "ID"\n", do_update)) + + } + else + { + /* the current front would not be extended */ + do_update = fnpiv > 0 ; + fnzeros = 0 ; + DEBUG2 (("IN-OUT do_update forced true: "ID"\n", do_update)) ; + + /* The new front would be just big enough to hold the new + * pivot row and column. */ + fnrows_new [IN][OUT] = cdeg_in - 1 ; + fncols_new [IN][OUT] = rdeg [IN][OUT] - 1 ; + + } + + DEBUG2 (("option IN OUT: nr "ID" nc "ID" cost "ID"("ID") relax "ID + "\n", nr_in, nc, thiscost, extra_zeros, do_extend)) ; + + if (bestcost == EMPTY || thiscost < bestcost) + { + /* this is the best option seen so far */ + Work->pivot_case = IN_OUT ; + bestcost = thiscost ; + Work->do_extend = do_extend ; + Work->do_update = do_update ; + new_fnzeros = fnzeros ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* construct candidate column not in front, and search for pivot rows */ + /* ---------------------------------------------------------------------- */ + + search_pivcol_out = (bestcost != 0 && pivcol [OUT] != EMPTY) ; + if (Symbolic->prefer_diagonal) + { + search_pivcol_out = search_pivcol_out && (pivrow [IN][IN] == EMPTY) ; + } + + if (search_pivcol_out) + { + +#ifndef NDEBUG + DEBUG2 (("out_col column "ID" NOT in front at position = "ID"\n", + pivcol [OUT], Fcpos [pivcol [OUT]])) ; + UMF_dump_rowcol (1, Numeric, Work, pivcol [OUT], !Symbolic->fixQ) ; + DEBUG2 (("fncols "ID" fncols_max "ID"\n", fncols, Work->fncols_max)) ; + ASSERT (fncols < Work->fncols_max) ; +#endif + + /* Use Wx as temporary workspace to construct the pivcol [OUT] */ + + + /* ------------------------------------------------------------------ */ + /* construct the candidate column (currently not in the front) */ + /* ------------------------------------------------------------------ */ + + /* Construct the column in Wx, Wm, using Wp for the positions: */ + /* Wm [0..cdeg_out-1] list of row indices in the column */ + /* Wx [0..cdeg_out-1] list of corresponding numerical values */ + /* Wp [0..n-1] starts as all negative, and ends that way too. */ + + cdeg_out = 0 ; + +#ifndef NDEBUG + /* check Wp */ + DEBUG4 (("COL ASSEMBLE: cdeg 0\nREDUCE COL out "ID"\n", pivcol [OUT])) ; + if (UMF_debug > 0 || MAX (n_row, n_col) < 1000) + { + for (i = 0 ; i < MAX (n_row, n_col) ; i++) + { + ASSERT (Wp [i] < 0) ; + } + } + DEBUG4 (("max_cdeg: "ID"\n", max_cdeg)) ; +#endif + + ASSERT (pivcol [OUT] >= 0 && pivcol [OUT] < n_col) ; + ASSERT (NON_PIVOTAL_COL (pivcol [OUT])) ; + + tpi = Col_tuples [pivcol [OUT]] ; + if (tpi) + { + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Col_tlen [pivcol [OUT]] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + if (Cols [f] == EMPTY) continue ; /* column already assembled */ + ASSERT (pivcol [OUT] == Cols [f]) ; + + Rows = Cols + ep->ncols ; + nrows = ep->nrows ; + p += UNITS (Int, ep->ncols + nrows) ; + C = ((Entry *) p) + f * nrows ; + + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) /* skip this if already gone from element */ + { + ASSERT (row < n_row) ; + pos = Wp [row] ; + if (pos < 0) + { + /* new entry in the pattern - save Wp */ + ASSERT (cdeg_out < n_row) ; + if (cdeg_out >= max_cdeg) + { + /* :: pattern change (cdeg out failure) :: */ + DEBUGm4 (("cdeg out failure\n")) ; + return (UMFPACK_ERROR_different_pattern) ; + } + Wp [row] = cdeg_out ; + Wm [cdeg_out] = row ; + Wx [cdeg_out++] = C [i] ; + } + else + { + /* entry already in pattern - sum the values */ + /* Wx [pos] += C [i] ; */ + ASSEMBLE (Wx [pos], C [i]) ; + } + } + } + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Col_tlen [pivcol [OUT]] = tp2 - tp1 ; + } + + /* ------------------------------------------------------------------ */ + +#ifndef NDEBUG + DEBUG4 (("Reduced column: cdeg out "ID"\n", cdeg_out)) ; + for (i = 0 ; i < cdeg_out ; i++) + { + DEBUG4 ((" "ID" "ID" "ID, i, Wm [i], Wp [Wm [i]])) ; + EDEBUG4 (Wx [i]) ; + DEBUG4 (("\n")) ; + ASSERT (i == Wp [Wm [i]]) ; + } +#endif + + /* ------------------------------------------------------------------ */ + /* new degree of pivcol [OUT] is cdeg_out */ + /* ------------------------------------------------------------------ */ + + /* search for two candidate pivot rows */ + status = UMF_row_search (Numeric, Work, Symbolic, + 0, cdeg_out, Wm, Wp, /* pattern of column to search */ + pivrow [OUT], rdeg [OUT], Woi, Woo, pivrow [IN], Wx, + pivcol [OUT], freebie) ; + + /* ------------------------------------------------------------------ */ + /* fatal error if matrix pattern has changed since symbolic analysis */ + /* ------------------------------------------------------------------ */ + + if (status == UMFPACK_ERROR_different_pattern) + { + /* :: pattern change detected in umf_local_search :: */ + return (UMFPACK_ERROR_different_pattern) ; + } + + /* ------------------------------------------------------------------ */ + /* Clear Wp */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < cdeg_out ; i++) + { + Wp [Wm [i]] = EMPTY ; /* clear Wp */ + } + + /* ------------------------------------------------------------------ */ + /* check for rectangular, singular matrix */ + /* ------------------------------------------------------------------ */ + + if (status == UMFPACK_WARNING_singular_matrix) + { + /* Pivot column is empty, and row-merge set is empty too. The + * matrix is structurally singular. The current frontal matrix must + * be empty, too. It it weren't, and pivcol [OUT] exists, then + * there would be at least one row that could be selected. Since + * the current front is empty, pivcol [IN] must also be EMPTY. + */ + + DEBUGm4 (("Note: pivcol [OUT]: "ID" discard\n", pivcol [OUT])) ; + ASSERT ((Work->fnrows == 0 && Work->fncols == 0)) ; + ASSERT (pivcol [IN] == EMPTY) ; + + /* remove the failed pivcol [OUT] from candidate set */ + ASSERT (pivcol [OUT] == Work->Candidates [jcand [OUT]]) ; + remove_candidate (jcand [OUT], Work, Symbolic) ; + Work->ndiscard++ ; + + /* delete all of the tuples, and all contributions to this column */ + DEBUG1 (("Prune tuples of dead outcol: "ID"\n", pivcol [OUT])) ; + Col_tlen [pivcol [OUT]] = 0 ; + UMF_mem_free_tail_block (Numeric, Col_tuples [pivcol [OUT]]) ; + Col_tuples [pivcol [OUT]] = 0 ; + + /* no pivot found at all */ + return (UMFPACK_WARNING_singular_matrix) ; + } + + /* ------------------------------------------------------------------ */ + + if (freebie [IN]) + { + /* the "in" row is the same as the "in" row for the "in" column */ + Woi = Fcols ; + rdeg [OUT][IN] = rdeg [IN][IN] ; + DEBUG4 (("Freebie in, row "ID"\n", pivrow [IN][IN])) ; + } + + if (freebie [OUT]) + { + /* the "out" row is the same as the "out" row for the "in" column */ + Woo = Wio ; + rdeg [OUT][OUT] = rdeg [IN][OUT] ; + DEBUG4 (("Freebie out, row "ID"\n", pivrow [IN][OUT])) ; + } + + /* ------------------------------------------------------------------ */ + /* evaluate OUT_IN option */ + /* ------------------------------------------------------------------ */ + + if (pivrow [OUT][IN] != EMPTY) + { + /* The current front would become an Lson of the new front. + * The candidate pivot row is in the current front, but the + * candidate pivot column is not. */ + + ASSERT (fnrows > 0 && fncols >= 0) ; + + did_rowmerge = (cdeg_out == 0) ; + if (did_rowmerge) + { + /* pivrow [OUT][IN] was found via row merge search */ + /* it is not (yet) in the pivot column pattern (add it now) */ + DEBUGm4 (("did row merge OUT col, IN row\n")) ; + Wm [0] = pivrow [OUT][IN] ; + CLEAR (Wx [0]) ; + cdeg_out = 1 ; + ASSERT (nr_out == EMPTY) ; + } + + nc = rdeg [OUT][IN] - fncols ; + ASSERT (nc >= 1) ; + + /* count rows not in current front */ + nr_out = 0 ; +#ifndef NDEBUG + debug_ok = FALSE ; +#endif + for (i = 0 ; i < cdeg_out ; i++) + { + row = Wm [i] ; + ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ; + if (Frpos [row] < 0 || Frpos [row] >= fnrows) nr_out++ ; +#ifndef NDEBUG + /* we must see the pivot row somewhere */ + if (row == pivrow [OUT][IN]) + { + ASSERT (Frpos [row] >= 0) ; + debug_ok = TRUE ; + } +#endif + } + ASSERT (debug_ok) ; + + thiscost = + /* each column in front grows by nr_out: */ + (nr_out * fncols) + + /* new cols not affected by front: */ + ((nc-1) * (cdeg_out-1)) ; + + /* check the cost of relaxed OUT_IN amalgamation */ + + extra_rows = ((fnrows-1) + nr_out) - (cdeg_out - 1) ; + ASSERT (extra_rows >= 0) ; + ASSERT (fnrows + nr_out == extra_rows + cdeg_out) ; + extra_zeros = (nc-1) * extra_rows ; /* symbolic fill-in */ + + ASSERT (fnrows + nr_out == cdeg_out + extra_rows) ; + ASSERT (fncols + nc == rdeg [OUT][IN]) ; + + /* size of relaxed front (after pivot row removed): */ + fnrows_new [OUT][IN] = (fnrows-1) + nr_out ; + fncols_new [OUT][IN] = fncols + (nc-1) ; + relaxed_front = fnrows_new [OUT][IN] * fncols_new [OUT][IN] ; + + /* do relaxed amalgamation if the extra zeros are no more */ + /* than a fraction (default 0.25) of the relaxed front */ + /* if relax = 0: no extra zeros allowed */ + /* if relax = +inf: always amalgamate */ + if (did_rowmerge) + { + do_extend = FALSE ; + } + else + { + /* relax parameter uses a double relop, but ignore NaN case: */ + if (extra_zeros == 0) + { + do_extend = TRUE ; + } + else + { + do_extend = ((double) extra_zeros) < + (relax1 * (double) relaxed_front) ; + } + } + + if (do_extend) + { + /* count the cost of relaxed amalgamation */ + thiscost += extra_zeros ; + + DEBUG2 (("Evaluating option OUT-IN:\n")) ; + DEBUG2 ((" Work->fnzeros "ID" fnpiv "ID" nr_out "ID" nc "ID"\n", + Work->fnzeros, fnpiv, nr_out, nc)) ; + DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ; + + /* determine if BLAS-3 update to be applied before extending. */ + /* update if too many zero entries accumulate in the LU block */ + fnzeros = Work->fnzeros + fnpiv * (nr_out + nc) ; + + DEBUG2 (("fnzeros "ID"\n", fnzeros)) ; + + new_LUsize = (fnpiv+1) * (fnrows + nr_out + fncols + nc) ; + + DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ; + + /* RELAX3 parameter uses a double relop, ignore NaN case: */ + do_update = fnpiv > 0 && + (((double) fnzeros) / ((double) new_LUsize)) > RELAX3 ; + DEBUG2 (("do_update "ID"\n", do_update)) + } + else + { + /* the current front would not be extended */ + do_update = fnpiv > 0 ; + fnzeros = 0 ; + DEBUG2 (("OUT-IN do_update forced true: "ID"\n", do_update)) ; + + /* The new front would be just big enough to hold the new + * pivot row and column. */ + fnrows_new [OUT][IN] = cdeg_out - 1 ; + fncols_new [OUT][IN] = rdeg [OUT][IN] - 1 ; + } + + DEBUG2 (("option OUT IN : nr "ID" nc "ID" cost "ID"("ID") relax "ID + "\n", nr_out, nc, thiscost, extra_zeros, do_extend)) ; + + if (bestcost == EMPTY || thiscost < bestcost) + { + /* this is the best option seen so far */ + Work->pivot_case = OUT_IN ; + bestcost = thiscost ; + Work->do_extend = do_extend ; + Work->do_update = do_update ; + new_fnzeros = fnzeros ; + } + } + + /* ------------------------------------------------------------------ */ + /* evaluate OUT_OUT option */ + /* ------------------------------------------------------------------ */ + + if (pivrow [OUT][OUT] != EMPTY) + { + /* Neither the candidate pivot row nor the candidate pivot column + * are in the current front. */ + + ASSERT (fnrows >= 0 && fncols >= 0) ; + + did_rowmerge = (cdeg_out == 0) ; + if (did_rowmerge) + { + /* pivrow [OUT][OUT] was found via row merge search */ + /* it is not (yet) in the pivot column pattern (add it now) */ + DEBUGm4 (("did row merge OUT col, OUT row\n")) ; + Wm [0] = pivrow [OUT][OUT] ; + CLEAR (Wx [0]) ; + cdeg_out = 1 ; + ASSERT (nr_out == EMPTY) ; + nr_out = 1 ; + } + + if (fnrows == 0 && fncols == 0) + { + /* the current front is completely empty */ + ASSERT (fnpiv == 0) ; + nc = rdeg [OUT][OUT] ; + extra_cols = 0 ; + nr_out = cdeg_out ; + extra_rows = 0 ; + extra_zeros = 0 ; + + thiscost = (nc-1) * (cdeg_out-1) ; /* new columns only */ + + /* size of new front: */ + fnrows_new [OUT][OUT] = nr_out-1 ; + fncols_new [OUT][OUT] = nc-1 ; + relaxed_front = fnrows_new [OUT][OUT] * fncols_new [OUT][OUT] ; + } + else + { + + /* count rows not in current front */ + if (nr_out == EMPTY) + { + nr_out = 0 ; +#ifndef NDEBUG + debug_ok = FALSE ; +#endif + for (i = 0 ; i < cdeg_out ; i++) + { + row = Wm [i] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + if (Frpos [row] < 0 || Frpos [row] >= fnrows) nr_out++ ; +#ifndef NDEBUG + /* we must see the pivot row somewhere */ + if (row == pivrow [OUT][OUT]) + { + ASSERT (Frpos [row] < 0 || Frpos [row] >= fnrows) ; + debug_ok = TRUE ; + } +#endif + } + ASSERT (debug_ok) ; + } + + /* count columns not in current front */ + nc = 0 ; +#ifndef NDEBUG + debug_ok = FALSE ; +#endif + for (i = 0 ; i < rdeg [OUT][OUT] ; i++) + { + col = Woo [i] ; + ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ; + if (Fcpos [col] < 0) nc++ ; +#ifndef NDEBUG + /* we must see the pivot column somewhere */ + if (col == pivcol [OUT]) + { + ASSERT (Fcpos [col] < 0) ; + debug_ok = TRUE ; + } +#endif + } + ASSERT (debug_ok) ; + + extra_cols = (fncols + (nc-1)) - (rdeg [OUT][OUT] - 1) ; + extra_rows = (fnrows + (nr_out-1)) - (cdeg_out - 1) ; + ASSERT (extra_rows >= 0) ; + ASSERT (extra_cols >= 0) ; + extra_zeros = ((nc-1) * extra_rows) + ((nr_out-1) * extra_cols); + + ASSERT (fnrows + nr_out == cdeg_out + extra_rows) ; + ASSERT (fncols + nc == rdeg [OUT][OUT] + extra_cols) ; + + thiscost = + /* new columns: */ + ((nc-1) * (cdeg_out-1)) + + /* old columns in front grow by nr_out-1: */ + ((nr_out-1) * (fncols - extra_cols)) ; + + /* size of relaxed front: */ + fnrows_new [OUT][OUT] = fnrows + (nr_out-1) ; + fncols_new [OUT][OUT] = fncols + (nc-1) ; + relaxed_front = fnrows_new [OUT][OUT] * fncols_new [OUT][OUT] ; + + } + + /* do relaxed amalgamation if the extra zeros are no more */ + /* than a fraction (default 0.25) of the relaxed front */ + /* if relax = 0: no extra zeros allowed */ + /* if relax = +inf: always amalgamate */ + if (did_rowmerge) + { + do_extend = FALSE ; + } + else + { + /* relax parameter uses a double relop, but ignore NaN case: */ + if (extra_zeros == 0) + { + do_extend = TRUE ; + } + else + { + do_extend = ((double) extra_zeros) < + (relax1 * (double) relaxed_front) ; + } + } + + if (do_extend) + { + /* count the cost of relaxed amalgamation */ + thiscost += extra_zeros ; + + DEBUG2 (("Evaluating option OUT-OUT:\n")) ; + DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_out "ID" nc "ID"\n", + Work->fnzeros, fnpiv, nr_out, nc)) ; + DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ; + + /* determine if BLAS-3 update to be applied before extending. */ + /* update if too many zero entries accumulate in the LU block */ + fnzeros = Work->fnzeros + fnpiv * (nr_out + nc) ; + + DEBUG2 (("fnzeros "ID"\n", fnzeros)) ; + + new_LUsize = (fnpiv+1) * (fnrows + nr_out + fncols + nc) ; + + DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ; + + /* RELAX3 parameter uses a double relop, ignore NaN case: */ + do_update = fnpiv > 0 && + (((double) fnzeros) / ((double) new_LUsize)) > RELAX3 ; + DEBUG2 (("do_update "ID"\n", do_update)) + } + else + { + /* the current front would not be extended */ + do_update = fnpiv > 0 ; + fnzeros = 0 ; + DEBUG2 (("OUT-OUT do_update forced true: "ID"\n", do_update)) ; + + /* The new front would be just big enough to hold the new + * pivot row and column. */ + fnrows_new [OUT][OUT] = cdeg_out - 1 ; + fncols_new [OUT][OUT] = rdeg [OUT][OUT] - 1 ; + } + + DEBUG2 (("option OUT OUT: nr "ID" nc "ID" cost "ID"\n", + rdeg [OUT][OUT], cdeg_out, thiscost)) ; + + if (bestcost == EMPTY || thiscost < bestcost) + { + /* this is the best option seen so far */ + Work->pivot_case = OUT_OUT ; + bestcost = thiscost ; + Work->do_extend = do_extend ; + Work->do_update = do_update ; + new_fnzeros = fnzeros ; + } + } + } + + /* At this point, a structural pivot has been found. */ + /* It may be numerically zero, however. */ + ASSERT (Work->pivot_case != EMPTY) ; + DEBUG2 (("local search, best option "ID", best cost "ID"\n", + Work->pivot_case, bestcost)) ; + + /* ====================================================================== */ + /* Pivot row and column, and extension, now determined */ + /* ====================================================================== */ + + Work->fnzeros = new_fnzeros ; + + /* ---------------------------------------------------------------------- */ + /* finalize the pivot row and column */ + /* ---------------------------------------------------------------------- */ + + switch (Work->pivot_case) + { + case IN_IN: + DEBUG2 (("IN-IN option selected\n")) ; + ASSERT (fnrows > 0 && fncols > 0) ; + Work->pivcol_in_front = TRUE ; + Work->pivrow_in_front = TRUE ; + Work->pivcol = pivcol [IN] ; + Work->pivrow = pivrow [IN][IN] ; + Work->ccdeg = nr_in ; + Work->Wrow = Fcols ; + Work->rrdeg = rdeg [IN][IN] ; + jj = jcand [IN] ; + Work->fnrows_new = fnrows_new [IN][IN] ; + Work->fncols_new = fncols_new [IN][IN] ; + break ; + + case IN_OUT: + DEBUG2 (("IN-OUT option selected\n")) ; + ASSERT (fnrows >= 0 && fncols > 0) ; + Work->pivcol_in_front = TRUE ; + Work->pivrow_in_front = FALSE ; + Work->pivcol = pivcol [IN] ; + Work->pivrow = pivrow [IN][OUT] ; + Work->ccdeg = nr_in ; + Work->Wrow = Wio ; + Work->rrdeg = rdeg [IN][OUT] ; + jj = jcand [IN] ; + Work->fnrows_new = fnrows_new [IN][OUT] ; + Work->fncols_new = fncols_new [IN][OUT] ; + break ; + + case OUT_IN: + DEBUG2 (("OUT-IN option selected\n")) ; + ASSERT (fnrows > 0 && fncols >= 0) ; + Work->pivcol_in_front = FALSE ; + Work->pivrow_in_front = TRUE ; + Work->pivcol = pivcol [OUT] ; + Work->pivrow = pivrow [OUT][IN] ; + Work->ccdeg = cdeg_out ; + /* Wrow might be equivalenced to Fcols (Freebie in): */ + Work->Wrow = Woi ; + Work->rrdeg = rdeg [OUT][IN] ; + /* Work->Wrow[0..fncols-1] is not there. See Fcols instead */ + jj = jcand [OUT] ; + Work->fnrows_new = fnrows_new [OUT][IN] ; + Work->fncols_new = fncols_new [OUT][IN] ; + break ; + + case OUT_OUT: + DEBUG2 (("OUT-OUT option selected\n")) ; + ASSERT (fnrows >= 0 && fncols >= 0) ; + Work->pivcol_in_front = FALSE ; + Work->pivrow_in_front = FALSE ; + Work->pivcol = pivcol [OUT] ; + Work->pivrow = pivrow [OUT][OUT] ; + Work->ccdeg = cdeg_out ; + /* Wrow might be equivalenced to Wio (Freebie out): */ + Work->Wrow = Woo ; + Work->rrdeg = rdeg [OUT][OUT] ; + jj = jcand [OUT] ; + Work->fnrows_new = fnrows_new [OUT][OUT] ; + Work->fncols_new = fncols_new [OUT][OUT] ; + break ; + + } + + ASSERT (IMPLIES (fnrows == 0 && fncols == 0, Work->pivot_case == OUT_OUT)) ; + + if (!Work->pivcol_in_front && pivcol [IN] != EMPTY) + { + /* clear Frpos if pivcol [IN] was searched, but not selected */ + for (i = fnrows ; i < cdeg_in ; i++) + { + Frpos [Frows [i]] = EMPTY; + } + } + + /* ---------------------------------------------------------------------- */ + /* Pivot row and column have been found */ + /* ---------------------------------------------------------------------- */ + + /* ---------------------------------------------------------------------- */ + /* remove pivot column from candidate pivot column set */ + /* ---------------------------------------------------------------------- */ + + ASSERT (jj >= 0 && jj < Work->nCandidates) ; + ASSERT (Work->pivcol == Work->Candidates [jj]) ; + remove_candidate (jj, Work, Symbolic) ; + + /* ---------------------------------------------------------------------- */ + /* check for frontal matrix growth */ + /* ---------------------------------------------------------------------- */ + + DEBUG1 (("Check frontal growth:\n")) ; + DEBUG1 (("fnrows_new "ID" + 1 = "ID", fnr_curr "ID"\n", + Work->fnrows_new, Work->fnrows_new + 1, fnr_curr)) ; + DEBUG1 (("fncols_new "ID" + 1 = "ID", fnc_curr "ID"\n", + Work->fncols_new, Work->fncols_new + 1, fnc_curr)) ; + + Work->do_grow = (Work->fnrows_new + 1 > fnr_curr + || Work->fncols_new + 1 > fnc_curr) ; + if (Work->do_grow) + { + DEBUG0 (("\nNeed to grow frontal matrix, force do_update true\n")) ; + /* If the front must grow, then apply the pending updates and remove + * the current pivot rows/columns from the front prior to growing the + * front. This frees up as much space as possible for the new front. */ + if (!Work->do_update && fnpiv > 0) + { + /* This update would not have to be done if the current front + * was big enough. */ + Work->nforced++ ; + Work->do_update = TRUE ; + } + } + + /* ---------------------------------------------------------------------- */ + /* current pivot column */ + /* ---------------------------------------------------------------------- */ + + /* + c1) If pivot column index is in the current front: + + The pivot column pattern is in Frows [0 .. fnrows-1] and + the extension is in Frows [fnrows ... fnrows+ccdeg-1]. + + Frpos [Frows [0 .. fnrows+ccdeg-1]] is + equal to 0 .. fnrows+ccdeg-1. Wm is not needed. + + The values are in Wy [0 .. fnrows+ccdeg-1]. + + c2) Otherwise, if the pivot column index is not in the current front: + + c2a) If the front is being extended, old row indices in the the + pivot column pattern are in Frows [0 .. fnrows-1]. + + All entries are in Wm [0 ... ccdeg-1], with values in + Wx [0 .. ccdeg-1]. These may include entries already in + Frows [0 .. fnrows-1]. + + Frpos [Frows [0 .. fnrows-1]] is equal to 0 .. fnrows-1. + Frpos [Wm [0 .. ccdeg-1]] for new entries is < 0. + + c2b) If the front is not being extended, then the entire pivot + column pattern is in Wm [0 .. ccdeg-1]. It includes + the pivot row index. It is does not contain the pattern + Frows [0..fnrows-1]. The intersection of these two + sets may or may not be empty. The values are in Wx [0..ccdeg-1] + + In both cases c1 and c2, Frpos [Frows [0 .. fnrows-1]] is equal + to 0 .. fnrows-1, which is the pattern of the current front. + Any entry of Frpos that is not specified above is < 0. + */ + + +#ifndef NDEBUG + DEBUG2 (("\n\nSEARCH DONE: Pivot col "ID" in: ("ID") pivot row "ID" in: ("ID + ") extend: "ID"\n\n", Work->pivcol, Work->pivcol_in_front, + Work->pivrow, Work->pivrow_in_front, Work->do_extend)) ; + UMF_dump_rowcol (1, Numeric, Work, Work->pivcol, !Symbolic->fixQ) ; + DEBUG2 (("Pivot col "ID": fnrows "ID" ccdeg "ID"\n", Work->pivcol, fnrows, + Work->ccdeg)) ; + if (Work->pivcol_in_front) /* case c1 */ + { + Int found = FALSE ; + DEBUG3 (("Pivcol in front\n")) ; + for (i = 0 ; i < fnrows ; i++) + { + row = Frows [i] ; + DEBUG3 ((ID": row:: "ID" in front ", i, row)) ; + ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ; + ASSERT (Frpos [row] == i) ; + EDEBUG3 (Wy [i]) ; + if (row == Work->pivrow) + { + DEBUG3 ((" <- pivrow")) ; + found = TRUE ; + } + DEBUG3 (("\n")) ; + } + ASSERT (found == Work->pivrow_in_front) ; + found = FALSE ; + for (i = fnrows ; i < fnrows + Work->ccdeg ; i++) + { + row = Frows [i] ; + DEBUG3 ((ID": row:: "ID" (new)", i, row)) ; + ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ; + ASSERT (Frpos [row] == i) ; + EDEBUG3 (Wy [i]) ; + if (row == Work->pivrow) + { + DEBUG3 ((" <- pivrow")) ; + found = TRUE ; + } + DEBUG3 (("\n")) ; + } + ASSERT (found == !Work->pivrow_in_front) ; + } + else + { + if (Work->do_extend) + { + Int found = FALSE ; + DEBUG3 (("Pivcol not in front (extend)\n")) ; + for (i = 0 ; i < fnrows ; i++) + { + row = Frows [i] ; + DEBUG3 ((ID": row:: "ID" in front ", i, row)) ; + ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ; + ASSERT (Frpos [row] == i) ; + if (row == Work->pivrow) + { + DEBUG3 ((" <- pivrow")) ; + found = TRUE ; + } + DEBUG3 (("\n")) ; + } + ASSERT (found == Work->pivrow_in_front) ; + found = FALSE ; + DEBUG3 (("----\n")) ; + for (i = 0 ; i < Work->ccdeg ; i++) + { + row = Wm [i] ; + ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ; + DEBUG3 ((ID": row:: "ID" ", i, row)) ; + EDEBUG3 (Wx [i]) ; + if (Frpos [row] < 0) + { + DEBUG3 ((" (new) ")) ; + } + if (row == Work->pivrow) + { + DEBUG3 ((" <- pivrow")) ; + found = TRUE ; + /* ... */ + if (Work->pivrow_in_front) ASSERT (Frpos [row] >= 0) ; + else ASSERT (Frpos [row] < 0) ; + } + DEBUG3 (("\n")) ; + } + ASSERT (found) ; + } + else + { + Int found = FALSE ; + DEBUG3 (("Pivcol not in front (no extend)\n")) ; + for (i = 0 ; i < Work->ccdeg ; i++) + { + row = Wm [i] ; + ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ; + DEBUG3 ((ID": row:: "ID" ", i, row)) ; + EDEBUG3 (Wx [i]) ; + DEBUG3 ((" (new) ")) ; + if (row == Work->pivrow) + { + DEBUG3 ((" <- pivrow")) ; + found = TRUE ; + } + DEBUG3 (("\n")) ; + } + ASSERT (found) ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* current pivot row */ + /* ---------------------------------------------------------------------- */ + + /* + r1) If the pivot row index is in the current front: + + The pivot row pattern is in Fcols [0..fncols-1] and the extenson is + in Wrow [fncols .. rrdeg-1]. If the pivot column is in the current + front, then Fcols and Wrow are equivalenced. + + r2) If the pivot row index is not in the current front: + + r2a) If the front is being extended, the pivot row pattern is in + Fcols [0 .. fncols-1]. New entries are in Wrow [0 .. rrdeg-1], + but these may include entries already in Fcols [0 .. fncols-1]. + + r2b) Otherwise, the pivot row pattern is Wrow [0 .. rrdeg-1]. + + Fcpos [Fcols [0..fncols-1]] is (0..fncols-1) * fnr_curr. + All other entries in Fcpos are < 0. + + These conditions are asserted below. + + ------------------------------------------------------------------------ + Other items in Work structure that are relevant: + + pivcol: the pivot column index + pivrow: the pivot column index + + rrdeg: + ccdeg: + + fnrows: the number of rows in the currnt contribution block + fncols: the number of columns in the current contribution block + + fnrows_new: the number of rows in the new contribution block + fncols_new: the number of rows in the new contribution block + + ------------------------------------------------------------------------ + */ + + +#ifndef NDEBUG + UMF_dump_rowcol (0, Numeric, Work, Work->pivrow, TRUE) ; + DEBUG2 (("Pivot row "ID":\n", Work->pivrow)) ; + if (Work->pivrow_in_front) + { + Int found = FALSE ; + for (i = 0 ; i < fncols ; i++) + { + col = Fcols [i] ; + DEBUG3 ((" col:: "ID" in front\n", col)) ; + ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ; + ASSERT (Fcpos [col] == i * fnr_curr) ; + if (col == Work->pivcol) found = TRUE ; + } + ASSERT (found == Work->pivcol_in_front) ; + found = FALSE ; + ASSERT (IMPLIES (Work->pivcol_in_front, Fcols == Work->Wrow)) ; + for (i = fncols ; i < Work->rrdeg ; i++) + { + col = Work->Wrow [i] ; + ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ; + ASSERT (Fcpos [col] < 0) ; + if (col == Work->pivcol) found = TRUE ; + else DEBUG3 ((" col:: "ID" (new)\n", col)) ; + } + ASSERT (found == !Work->pivcol_in_front) ; + } + else + { + if (Work->do_extend) + { + Int found = FALSE ; + for (i = 0 ; i < fncols ; i++) + { + col = Fcols [i] ; + DEBUG3 ((" col:: "ID" in front\n", col)) ; + ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ; + ASSERT (Fcpos [col] == i * fnr_curr) ; + if (col == Work->pivcol) found = TRUE ; + } + ASSERT (found == Work->pivcol_in_front) ; + found = FALSE ; + for (i = 0 ; i < Work->rrdeg ; i++) + { + col = Work->Wrow [i] ; + ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ; + if (Fcpos [col] >= 0) continue ; + if (col == Work->pivcol) found = TRUE ; + else DEBUG3 ((" col:: "ID" (new, extend)\n", col)) ; + } + ASSERT (found == !Work->pivcol_in_front) ; + } + else + { + Int found = FALSE ; + for (i = 0 ; i < Work->rrdeg ; i++) + { + col = Work->Wrow [i] ; + ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ; + if (col == Work->pivcol) found = TRUE ; + else DEBUG3 ((" col:: "ID" (all new)\n", col)) ; + } + ASSERT (found) ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* determine whether to do scan2-row and scan2-col */ + /* ---------------------------------------------------------------------- */ + + if (Work->do_extend) + { + Work->do_scan2row = (fncols > 0) ; + Work->do_scan2col = (fnrows > 0) ; + } + else + { + Work->do_scan2row = (fncols > 0) && Work->pivrow_in_front ; + Work->do_scan2col = (fnrows > 0) && Work->pivcol_in_front ; + } + + /* ---------------------------------------------------------------------- */ + + DEBUG2 (("LOCAL SEARCH DONE: pivot column "ID" pivot row: "ID"\n", + Work->pivcol, Work->pivrow)) ; + DEBUG2 (("do_extend: "ID"\n", Work->do_extend)) ; + DEBUG2 (("do_update: "ID"\n", Work->do_update)) ; + DEBUG2 (("do_grow: "ID"\n", Work->do_grow)) ; + + /* ---------------------------------------------------------------------- */ + /* keep track of the diagonal */ + /* ---------------------------------------------------------------------- */ + + if (Symbolic->prefer_diagonal + && Work->pivcol < Work->n_col - Symbolic->nempty_col) + { + Diagonal_map = Work->Diagonal_map ; + Diagonal_imap = Work->Diagonal_imap ; + ASSERT (Diagonal_map != (Int *) NULL) ; + ASSERT (Diagonal_imap != (Int *) NULL) ; + + row2 = Diagonal_map [Work->pivcol] ; + col2 = Diagonal_imap [Work->pivrow] ; + + if (row2 < 0) + { + /* this was an off-diagonal pivot row */ + Work->noff_diagonal++ ; + row2 = UNFLIP (row2) ; + } + + ASSERT (Diagonal_imap [row2] == Work->pivcol) ; + ASSERT (UNFLIP (Diagonal_map [col2]) == Work->pivrow) ; + + if (row2 != Work->pivrow) + { + /* swap the diagonal map to attempt to maintain symmetry later on. + * Also mark the map for col2 (via FLIP) to denote that the entry + * now on the diagonal is not the original entry on the diagonal. */ + + DEBUG0 (("Unsymmetric pivot\n")) ; + Diagonal_map [Work->pivcol] = FLIP (Work->pivrow) ; + Diagonal_imap [Work->pivrow] = Work->pivcol ; + + Diagonal_map [col2] = FLIP (row2) ; + Diagonal_imap [row2] = col2 ; + + } + ASSERT (n_row == n_col) ; +#ifndef NDEBUG + UMF_dump_diagonal_map (Diagonal_map, Diagonal_imap, Symbolic->n1, + Symbolic->n_col, Symbolic->nempty_col) ; +#endif + } + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_local_search.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_local_search.h new file mode 100644 index 0000000000..51ca38bdaa --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_local_search.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_local_search +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_lsolve.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_lsolve.c new file mode 100644 index 0000000000..03a84a31e0 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_lsolve.c @@ -0,0 +1,151 @@ +/* ========================================================================== */ +/* === UMF_lsolve =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* solves Lx = b, where L is the lower triangular factor of a matrix */ +/* B is overwritten with the solution X. */ +/* Returns the floating point operation count */ + +#include "umf_internal.h" + +GLOBAL double UMF_lsolve +( + NumericType *Numeric, + Entry X [ ], /* b on input, solution x on output */ + Int Pattern [ ] /* a work array of size n */ +) +{ + Int k, deg, *ip, j, row, *Lpos, *Lilen, *Lip, llen, lp, newLchain, + pos, npiv, n1, *Li ; + Entry *xp, xk, *Lval ; + + /* ---------------------------------------------------------------------- */ + + if (Numeric->n_row != Numeric->n_col) return (0.) ; + npiv = Numeric->npiv ; + Lpos = Numeric->Lpos ; + Lilen = Numeric->Lilen ; + Lip = Numeric->Lip ; + n1 = Numeric->n1 ; + +#ifndef NDEBUG + DEBUG4 (("Lsolve start:\n")) ; + for (j = 0 ; j < Numeric->n_row ; j++) + { + DEBUG4 (("Lsolve start "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* singletons */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < n1 ; k++) + { + DEBUG4 (("Singleton k "ID"\n", k)) ; + xk = X [k] ; + deg = Lilen [k] ; + if (deg > 0 && IS_NONZERO (xk)) + { + lp = Lip [k] ; + Li = (Int *) (Numeric->Memory + lp) ; + lp += UNITS (Int, deg) ; + Lval = (Entry *) (Numeric->Memory + lp) ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Li [j], k)) ; + EDEBUG4 (Lval [j]) ; + DEBUG4 (("\n")) ; + /* X [Li [j]] -= xk * Lval [j] ; */ + MULT_SUB (X [Li [j]], xk, Lval [j]) ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* rest of L */ + /* ---------------------------------------------------------------------- */ + + deg = 0 ; + + for (k = n1 ; k < npiv ; k++) + { + + /* ------------------------------------------------------------------ */ + /* make column of L in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + lp = Lip [k] ; + newLchain = (lp < 0) ; + if (newLchain) + { + lp = -lp ; + deg = 0 ; + DEBUG4 (("start of chain for column of L\n")) ; + } + + /* remove pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" removing row "ID" at position "ID"\n", + k, Pattern [pos], pos)) ; + ASSERT (!newLchain) ; + ASSERT (deg > 0) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + /* concatenate the pattern */ + ip = (Int *) (Numeric->Memory + lp) ; + llen = Lilen [k] ; + for (j = 0 ; j < llen ; j++) + { + row = *ip++ ; + DEBUG4 ((" row "ID" k "ID"\n", row, k)) ; + ASSERT (row > k) ; + Pattern [deg++] = row ; + } + + /* ------------------------------------------------------------------ */ + /* use column k of L */ + /* ------------------------------------------------------------------ */ + + xk = X [k] ; + if (IS_NONZERO (xk)) + { + xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Pattern [j], k)) ; + EDEBUG4 (*xp) ; + DEBUG4 (("\n")) ; + /* X [Pattern [j]] -= xk * (*xp) ; */ + MULT_SUB (X [Pattern [j]], xk, *xp) ; + xp++ ; + } + } + } + +#ifndef NDEBUG + for (j = 0 ; j < Numeric->n_row ; j++) + { + DEBUG4 (("Lsolve done "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } + DEBUG4 (("Lsolve done.\n")) ; +#endif + + return (MULTSUB_FLOPS * ((double) Numeric->lnz)) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_lsolve.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_lsolve.h new file mode 100644 index 0000000000..75d6467823 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_lsolve.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL double UMF_lsolve +( + NumericType *Numeric, + Entry X [ ], + Int Pattern [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_ltsolve.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_ltsolve.c new file mode 100644 index 0000000000..6011f0c24b --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_ltsolve.c @@ -0,0 +1,225 @@ +/* ========================================================================== */ +/* === UMF_ltsolve ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Solves L'x = b or L.'x=b, where L is the lower triangular factor of a */ +/* matrix. B is overwritten with the solution X. */ +/* Returns the floating point operation count */ + +#include "umf_internal.h" + +GLOBAL double +#ifdef CONJUGATE_SOLVE +UMF_lhsolve /* solve L'x=b (complex conjugate transpose) */ +#else +UMF_ltsolve /* solve L.'x=b (array transpose) */ +#endif +( + NumericType *Numeric, + Entry X [ ], /* b on input, solution x on output */ + Int Pattern [ ] /* a work array of size n */ +) +{ + Int k, deg, *ip, j, row, *Lpos, *Lilen, kstart, kend, *Lip, llen, + lp, pos, npiv, n1, *Li ; + Entry *xp, xk, *Lval ; + + /* ---------------------------------------------------------------------- */ + + if (Numeric->n_row != Numeric->n_col) return (0.) ; + npiv = Numeric->npiv ; + Lpos = Numeric->Lpos ; + Lilen = Numeric->Lilen ; + Lip = Numeric->Lip ; + kstart = npiv ; + n1 = Numeric->n1 ; + +#ifndef NDEBUG + DEBUG4 (("Ltsolve start:\n")) ; + for (j = 0 ; j < Numeric->n_row ; j++) + { + DEBUG4 (("Ltsolve start "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* non-singletons */ + /* ---------------------------------------------------------------------- */ + + for (kend = npiv-1 ; kend >= n1 ; kend = kstart-1) + { + + /* ------------------------------------------------------------------ */ + /* find the start of this Lchain */ + /* ------------------------------------------------------------------ */ + + /* for (kstart = kend ; kstart >= 0 && Lip [kstart] > 0 ; kstart--) ; */ + kstart = kend ; + while (kstart >= 0 && Lip [kstart] > 0) + { + kstart-- ; + } + + /* the Lchain goes from kstart to kend */ + + /* ------------------------------------------------------------------ */ + /* scan the whole chain to find the pattern of the last column of L */ + /* ------------------------------------------------------------------ */ + + deg = 0 ; + DEBUG4 (("start of chain for column of L\n")) ; + for (k = kstart ; k <= kend ; k++) + { + ASSERT (k >= 0 && k < npiv) ; + + /* -------------------------------------------------------------- */ + /* make column k of L in Pattern [0..deg-1] */ + /* -------------------------------------------------------------- */ + + /* remove pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" removing row "ID" at position "ID"\n", + k, Pattern [pos], pos)) ; + ASSERT (k != kstart) ; + ASSERT (deg > 0) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + /* concatenate the pattern */ + lp = Lip [k] ; + if (k == kstart) + { + lp = -lp ; + } + ASSERT (lp > 0) ; + ip = (Int *) (Numeric->Memory + lp) ; + llen = Lilen [k] ; + for (j = 0 ; j < llen ; j++) + { + row = *ip++ ; + DEBUG4 ((" row "ID" k "ID"\n", row, k)) ; + ASSERT (row > k) ; + Pattern [deg++] = row ; + } + + } + /* Pattern [0..deg-1] is now the pattern of column kend */ + + /* ------------------------------------------------------------------ */ + /* solve using this chain, in reverse order */ + /* ------------------------------------------------------------------ */ + + DEBUG4 (("Unwinding Lchain\n")) ; + for (k = kend ; k >= kstart ; k--) + { + + /* -------------------------------------------------------------- */ + /* use column k of L */ + /* -------------------------------------------------------------- */ + + ASSERT (k >= 0 && k < npiv) ; + lp = Lip [k] ; + if (k == kstart) + { + lp = -lp ; + } + ASSERT (lp > 0) ; + llen = Lilen [k] ; + xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ; + xk = X [k] ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Pattern [j], k)) ; + EDEBUG4 (*xp) ; + DEBUG4 (("\n")) ; + +#ifdef CONJUGATE_SOLVE + /* xk -= X [Pattern [j]] * conjugate (*xp) ; */ + MULT_SUB_CONJ (xk, X [Pattern [j]], *xp) ; +#else + /* xk -= X [Pattern [j]] * (*xp) ; */ + MULT_SUB (xk, X [Pattern [j]], *xp) ; +#endif + + xp++ ; + } + X [k] = xk ; + + /* -------------------------------------------------------------- */ + /* construct column k-1 of L */ + /* -------------------------------------------------------------- */ + + /* un-concatenate the pattern */ + deg -= llen ; + + /* add pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" adding row "ID" at position "ID"\n", + k, k, pos)) ; + ASSERT (k != kstart) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* singletons */ + /* ---------------------------------------------------------------------- */ + + for (k = n1 - 1 ; k >= 0 ; k--) + { + DEBUG4 (("Singleton k "ID"\n", k)) ; + deg = Lilen [k] ; + if (deg > 0) + { + xk = X [k] ; + lp = Lip [k] ; + Li = (Int *) (Numeric->Memory + lp) ; + lp += UNITS (Int, deg) ; + Lval = (Entry *) (Numeric->Memory + lp) ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Li [j], k)) ; + EDEBUG4 (Lval [j]) ; + DEBUG4 (("\n")) ; +#ifdef CONJUGATE_SOLVE + /* xk -= X [Li [j]] * conjugate (Lval [j]) ; */ + MULT_SUB_CONJ (xk, X [Li [j]], Lval [j]) ; +#else + /* xk -= X [Li [j]] * Lval [j] ; */ + MULT_SUB (xk, X [Li [j]], Lval [j]) ; +#endif + } + X [k] = xk ; + } + } + +#ifndef NDEBUG + for (j = 0 ; j < Numeric->n_row ; j++) + { + DEBUG4 (("Ltsolve done "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } + DEBUG4 (("Ltsolve done.\n")) ; +#endif + + return (MULTSUB_FLOPS * ((double) Numeric->lnz)) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_ltsolve.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_ltsolve.h new file mode 100644 index 0000000000..3b4ad1f53b --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_ltsolve.h @@ -0,0 +1,20 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL double UMF_ltsolve +( + NumericType *Numeric, + Entry X [ ], + Int Pattern [ ] +) ; + +GLOBAL double UMF_lhsolve +( + NumericType *Numeric, + Entry X [ ], + Int Pattern [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_malloc.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_malloc.c new file mode 100644 index 0000000000..9416435e5e --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_malloc.c @@ -0,0 +1,92 @@ +/* ========================================================================== */ +/* === UMF_malloc =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Allocate a block of n objects, each of a given size. This routine does not + handle the case when the size is 1 (allocating char's) because of potential + integer overflow. UMFPACK never does that. + Also maintains the UMFPACK malloc count. +*/ + +#include "umf_internal.h" + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) + +/* + UMF_malloc_count is a count of the objects malloc'd by UMFPACK. If you + suspect a memory leak in your program (caused by not properly destroying + the Symbolic and Numeric objects) then compile with -DUMF_MALLOC_COUNT and + check value of UMF_malloc_count. By default, UMF_MALLOC_COUNT is not + defined, and thus UMFPACK has no global variables. +*/ + +GLOBAL Int UMF_malloc_count = 0 ; + +#endif + +#ifdef UMF_TCOV_TEST +/* For exhaustive statement coverage testing only! */ +GLOBAL Int umf_fail, umf_fail_lo, umf_fail_hi ; +GLOBAL Int umf_realloc_fail, umf_realloc_lo, umf_realloc_hi ; +#endif + +GLOBAL void *UMF_malloc +( + Int n_objects, + size_t size_of_object +) +{ + size_t size ; + void *p ; + +#ifdef UMF_TCOV_TEST + /* For exhaustive statement coverage testing only! */ + /* Pretend to fail, to test out-of-memory conditions. */ + umf_fail-- ; + if (umf_fail <= umf_fail_hi && umf_fail >= umf_fail_lo) + { + DEBUG0 (("umf_malloc: Pretend to fail %d %d %d\n", + umf_fail, umf_fail_hi, umf_fail_lo)) ; + return ((void *) NULL) ; + } +#endif + + DEBUG0 (("UMF_malloc: ")) ; + + /* make sure that we allocate something */ + n_objects = MAX (1, n_objects) ; + + size = (size_t) n_objects ; + ASSERT (size_of_object > 1) ; + if (size > Int_MAX / size_of_object) + { + /* object is too big for integer pointer arithmetic */ + return ((void *) NULL) ; + } + size *= size_of_object ; + + /* see umf_config.h for the memory allocator selection */ + p = ALLOCATE (size) ; + + DEBUG0 ((ID"\n", (Int) p)) ; + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) + if (p) + { + /* One more object has been malloc'ed. Keep track of the count. */ + /* (purely for sanity checks). */ + UMF_malloc_count++ ; + DEBUG0 ((" successful, new malloc count: "ID"\n", UMF_malloc_count)) ; + } +#endif + + return (p) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_malloc.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_malloc.h new file mode 100644 index 0000000000..44529b57c9 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_malloc.h @@ -0,0 +1,16 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) +GLOBAL extern Int UMF_malloc_count ; +#endif + +GLOBAL void *UMF_malloc +( + Int n_objects, + size_t size_of_object +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_element.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_element.c new file mode 100644 index 0000000000..6eefa35504 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_element.c @@ -0,0 +1,83 @@ +/* ========================================================================== */ +/* === UMF_mem_alloc_element ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +/* Allocate a nrows-by-ncols element, and initialize it. */ +/* Returns the index into Numeric->Memory if successful, or 0 on failure. */ + +#include "umf_internal.h" +#include "umf_mem_alloc_tail_block.h" + +GLOBAL Int UMF_mem_alloc_element +( + NumericType *Numeric, + Int nrows, + Int ncols, + Int **Rows, + Int **Cols, + Entry **C, + Int *size, + Element **epout +) +{ + + Element *ep ; + Unit *p ; + Int i ; + + ASSERT (Numeric != (NumericType *) NULL) ; + ASSERT (Numeric->Memory != (Unit *) NULL) ; + + *size = GET_ELEMENT_SIZE (nrows, ncols) ; + if (INT_OVERFLOW (DGET_ELEMENT_SIZE (nrows, ncols) + 1)) + { + /* :: allocate element, int overflow :: */ + return (0) ; /* problem is too large */ + } + + i = UMF_mem_alloc_tail_block (Numeric, *size) ; + (*size)++ ; + if (!i) + { + DEBUG0 (("alloc element failed - out of memory\n")) ; + return (0) ; /* out of memory */ + } + p = Numeric->Memory + i ; + + ep = (Element *) p ; + + DEBUG2 (("alloc_element done ("ID" x "ID"): p: "ID" i "ID"\n", + nrows, ncols, (Int) (p-Numeric->Memory), i)) ; + + /* Element data structure, in order: */ + p += UNITS (Element, 1) ; /* (1) Element header */ + *Cols = (Int *) p ; /* (2) col [0..ncols-1] indices */ + *Rows = *Cols + ncols ; /* (3) row [0..nrows-1] indices */ + p += UNITS (Int, ncols + nrows) ; + *C = (Entry *) p ; /* (4) C [0..nrows-1, 0..ncols-1] */ + + ep->nrows = nrows ; /* initialize the header information */ + ep->ncols = ncols ; + ep->nrowsleft = nrows ; + ep->ncolsleft = ncols ; + ep->cdeg = 0 ; + ep->rdeg = 0 ; + ep->next = EMPTY ; + + DEBUG2 (("new block size: "ID" ", GET_BLOCK_SIZE (Numeric->Memory + i))) ; + DEBUG2 (("Element size needed "ID"\n", GET_ELEMENT_SIZE (nrows, ncols))) ; + + *epout = ep ; + + /* return the offset into Numeric->Memory */ + return (i) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_element.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_element.h new file mode 100644 index 0000000000..b3886cf396 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_element.h @@ -0,0 +1,18 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_mem_alloc_element +( + NumericType *Numeric, + Int nrows, + Int ncols, + Int **Rows, + Int **Cols, + Entry **C, + Int *size, + Element **epout +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_head_block.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_head_block.c new file mode 100644 index 0000000000..9f94c2c775 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_head_block.c @@ -0,0 +1,55 @@ +/* ========================================================================== */ +/* === UMF_mem_alloc_head_block ============================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +/* allocate nunits from head of Numeric->Memory. No header allocated. */ +/* Returns the index into Numeric->Memory if successful, or 0 on failure. */ + +#include "umf_internal.h" + +GLOBAL Int UMF_mem_alloc_head_block +( + NumericType *Numeric, + Int nunits +) +{ + Int p, usage ; + DEBUG2 (("GET BLOCK: from head, size "ID" ", nunits)) ; + + ASSERT (Numeric != (NumericType *) NULL) ; + ASSERT (Numeric->Memory != (Unit *) NULL) ; + +#ifndef NDEBUG + if (UMF_allocfail) + { + /* pretend to fail, to test garbage_collection */ + DEBUGm2 (("UMF_mem_alloc_head_block: pretend to fail\n")) ; + UMF_allocfail = FALSE ; /* don't fail the next time */ + return (0) ; + } +#endif + + if (nunits > (Numeric->itail - Numeric->ihead)) + { + DEBUG2 ((" failed\n")) ; + return (0) ; + } + + /* return p as an offset from Numeric->Memory */ + p = Numeric->ihead ; + Numeric->ihead += nunits ; + + DEBUG2 (("p: "ID"\n", p)) ; + usage = Numeric->ihead + Numeric->tail_usage ; + Numeric->max_usage = MAX (Numeric->max_usage, usage) ; + return (p) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_head_block.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_head_block.h new file mode 100644 index 0000000000..7217e51049 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_head_block.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_mem_alloc_head_block +( + NumericType *Numeric, + Int nunits +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_tail_block.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_tail_block.c new file mode 100644 index 0000000000..a6ca5cf7c5 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_tail_block.c @@ -0,0 +1,134 @@ +/* ========================================================================== */ +/* === UMF_mem_alloc_tail_block ============================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +#include "umf_internal.h" + +/* allocate nunits from tail of Numeric->Memory */ +/* (requires nunits+1, for header). */ +/* Returns the index into Numeric->Memory if successful, or 0 on failure. */ + +GLOBAL Int UMF_mem_alloc_tail_block +( + NumericType *Numeric, + Int nunits +) +{ + Int bigsize, usage ; + Unit *p, *pnext, *pbig ; + + ASSERT (Numeric != (NumericType *) NULL) ; + ASSERT (Numeric->Memory != (Unit *) NULL) ; + +#ifndef NDEBUG + if (UMF_allocfail) + { + /* pretend to fail, to test garbage_collection */ + DEBUGm2 (("UMF_mem_alloc_tail_block: pretend to fail\n")) ; + UMF_allocfail = FALSE ; /* don't fail the next time */ + return (0) ; + } + DEBUG2 (("UMF_mem_alloc_tail_block, size: "ID" + 1 = "ID": ", + nunits, nunits+1)) ; +#endif + + bigsize = 0 ; + pbig = (Unit *) NULL ; + + ASSERT (nunits > 0) ; /* size must be positive */ + if (Numeric->ibig != EMPTY) + { + ASSERT (Numeric->ibig > Numeric->itail) ; + ASSERT (Numeric->ibig < Numeric->size) ; + pbig = Numeric->Memory + Numeric->ibig ; + bigsize = -pbig->header.size ; + ASSERT (bigsize > 0) ; /* Numeric->ibig is free */ + ASSERT (pbig->header.prevsize >= 0) ; /* prev. is not free */ + } + + if (pbig && bigsize >= nunits) + { + + /* use the biggest block, somewhere in middle of memory */ + p = pbig ; + pnext = p + 1 + bigsize ; + /* next is in range */ + ASSERT (pnext < Numeric->Memory + Numeric->size) ; + /* prevsize of next = this size */ + ASSERT (pnext->header.prevsize == bigsize) ; + /* next is not free */ + ASSERT (pnext->header.size > 0) ; + bigsize -= nunits + 1 ; + + if (bigsize < 4) + { + /* internal fragmentation would be too small */ + /* allocate the entire free block */ + p->header.size = -p->header.size ; + DEBUG2 (("GET BLOCK: p: "ID" size: "ID", all of big: "ID" size: " + ID"\n", (Int) (p-Numeric->Memory), nunits, Numeric->ibig, + p->header.size)) ; + /* no more biggest block */ + Numeric->ibig = EMPTY ; + + } + else + { + + /* allocate just the first nunits Units of the free block */ + p->header.size = nunits ; + /* make a new free block */ + Numeric->ibig += nunits + 1 ; + pbig = Numeric->Memory + Numeric->ibig ; + pbig->header.size = -bigsize ; + pbig->header.prevsize = nunits ; + pnext->header.prevsize = bigsize ; + DEBUG2 (("GET BLOCK: p: "ID" size: "ID", some of big: "ID" left: " + ID"\n", (Int) (p-Numeric->Memory), nunits, Numeric->ibig, + bigsize)) ; + } + + } + else + { + + /* allocate from the top of tail */ + pnext = Numeric->Memory + Numeric->itail ; + DEBUG2 (("GET BLOCK: from tail ")) ; + if ((nunits + 1) > (Numeric->itail - Numeric->ihead)) + { + DEBUG2 (("\n")) ; + return (0) ; + } + Numeric->itail -= (nunits + 1) ; + p = Numeric->Memory + Numeric->itail ; + p->header.size = nunits ; + p->header.prevsize = 0 ; + pnext->header.prevsize = nunits ; + DEBUG2 (("p: "ID" size: "ID", new tail "ID"\n", + (Int) (p-Numeric->Memory), nunits, Numeric->itail)) ; + } + + Numeric->tail_usage += p->header.size + 1 ; + usage = Numeric->ihead + Numeric->tail_usage ; + Numeric->max_usage = MAX (Numeric->max_usage, usage) ; + +#ifndef NDEBUG + UMF_debug -= 10 ; + UMF_dump_memory (Numeric) ; + UMF_debug += 10 ; +#endif + + /* p points to the header. Add one to point to the usable block itself. */ + /* return the offset into Numeric->Memory */ + return ((p - Numeric->Memory) + 1) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_tail_block.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_tail_block.h new file mode 100644 index 0000000000..b0502e0727 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_alloc_tail_block.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_mem_alloc_tail_block +( + NumericType *Numeric, + Int nunits +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_free_tail_block.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_free_tail_block.c new file mode 100644 index 0000000000..842d4bca6c --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_free_tail_block.c @@ -0,0 +1,143 @@ +/* ========================================================================== */ +/* === UMF_mem_free_tail_block ============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +/* free a block from the tail of Numeric->memory */ + +#include "umf_internal.h" + +GLOBAL void UMF_mem_free_tail_block +( + NumericType *Numeric, + Int i +) +{ + Unit *pprev, *pnext, *p, *pbig ; + Int sprev ; + + ASSERT (Numeric != (NumericType *) NULL) ; + ASSERT (Numeric->Memory != (Unit *) NULL) ; + if (i == EMPTY || i == 0) return ; /* already deallocated */ + + /* ---------------------------------------------------------------------- */ + /* get the block */ + /* ---------------------------------------------------------------------- */ + + p = Numeric->Memory + i ; + + p-- ; /* get the corresponding header */ + DEBUG2 (("free block: p: "ID, (Int) (p-Numeric->Memory))) ; + ASSERT (p >= Numeric->Memory + Numeric->itail) ; + ASSERT (p < Numeric->Memory + Numeric->size) ; + ASSERT (p->header.size > 0) ; /* block not already free */ + ASSERT (p->header.prevsize >= 0) ; + + Numeric->tail_usage -= p->header.size + 1 ; + + /* ---------------------------------------------------------------------- */ + /* merge with next free block, if any */ + /* ---------------------------------------------------------------------- */ + + pnext = p + 1 + p->header.size ; + DEBUG2 (("size: "ID" next: "ID" ", p->header.size, + (Int) (pnext-Numeric->Memory))) ; + ASSERT (pnext < Numeric->Memory + Numeric->size) ; + ASSERT (pnext->header.prevsize == p->header.size) ; + ASSERT (pnext->header.size != 0) ; + + if (pnext->header.size < 0) + { + /* next block is also free - merge with current block */ + p->header.size += (-(pnext->header.size)) + 1 ; + DEBUG2 ((" NEXT FREE ")) ; + } + + /* ---------------------------------------------------------------------- */ + /* merge with previous free block, if any */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + if (p == Numeric->Memory + Numeric->itail) + { + DEBUG2 ((" at top of tail ")) ; + ASSERT (p->header.prevsize == 0) ; + } +#endif + + if (p > Numeric->Memory + Numeric->itail) + { + ASSERT (p->header.prevsize > 0) ; + pprev = p - 1 - p->header.prevsize ; + DEBUG2 ((" prev: "ID" ", (Int) (pprev-Numeric->Memory))) ; + ASSERT (pprev >= Numeric->Memory + Numeric->itail) ; + sprev = pprev->header.size ; + if (sprev < 0) + { + /* previous block is also free - merge it with current block */ + ASSERT (p->header.prevsize == -sprev) ; + pprev->header.size = p->header.size + (-sprev) + 1 ; + p = pprev ; + DEBUG2 ((" PREV FREE ")) ; + /* note that p may now point to Numeric->itail */ + } +#ifndef NDEBUG + else + { + ASSERT (p->header.prevsize == sprev) ; + } +#endif + } + + /* ---------------------------------------------------------------------- */ + /* free the block, p */ + /* ---------------------------------------------------------------------- */ + + pnext = p + 1 + p->header.size ; + ASSERT (pnext < Numeric->Memory + Numeric->size) ; + + if (p == Numeric->Memory + Numeric->itail) + { + /* top block in list is freed */ + Numeric->itail = pnext - Numeric->Memory ; + pnext->header.prevsize = 0 ; + DEBUG2 ((" NEW TAIL : "ID" ", Numeric->itail)) ; + ASSERT (pnext->header.size > 0) ; + if (Numeric->ibig != EMPTY && Numeric->ibig <= Numeric->itail) + { + /* the big free block is now above the tail */ + Numeric->ibig = EMPTY ; + } + } + else + { + /* keep track of the biggest free block seen */ + if (Numeric->ibig == EMPTY) + { + Numeric->ibig = p - Numeric->Memory ; + } + else + { + pbig = Numeric->Memory + Numeric->ibig ; + if (-(pbig->header.size) < p->header.size) + { + Numeric->ibig = p - Numeric->Memory ; + } + } + /* flag the block as free, somewhere in the middle of the tail */ + pnext->header.prevsize = p->header.size ; + p->header.size = -(p->header.size) ; + } + + DEBUG2 (("new p: "ID" freesize: "ID"\n", (Int) (p-Numeric->Memory), + -(p->header.size))) ; + +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_free_tail_block.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_free_tail_block.h new file mode 100644 index 0000000000..87b232bae1 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_free_tail_block.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_mem_free_tail_block +( + NumericType *Numeric, + Int i +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_init_memoryspace.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_init_memoryspace.c new file mode 100644 index 0000000000..5a2310d5d2 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_init_memoryspace.c @@ -0,0 +1,65 @@ +/* ========================================================================== */ +/* === UMF_mem_init_memoryspace ============================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +#include "umf_internal.h" + +/* initialize the LU and element workspace (Numeric->Memory) */ + +GLOBAL void UMF_mem_init_memoryspace +( + NumericType *Numeric +) +{ + Unit *p ; + + ASSERT (Numeric != (NumericType *) NULL) ; + ASSERT (Numeric->Memory != (Unit *) NULL) ; + ASSERT (Numeric->size >= 3) ; + DEBUG0 (("Init memory space, size "ID"\n", Numeric->size)) ; + + Numeric->ngarbage = 0 ; + Numeric->nrealloc = 0 ; + Numeric->ncostly = 0 ; + Numeric->ibig = EMPTY ; + Numeric->ihead = 0 ; + Numeric->itail = Numeric->size ; + +#ifndef NDEBUG + UMF_allocfail = FALSE ; +#endif + + /* allocate the 2-unit tail marker block and initialize it */ + Numeric->itail -= 2 ; + p = Numeric->Memory + Numeric->itail ; + DEBUG2 (("p "ID" tail "ID"\n", (Int) (p-Numeric->Memory), Numeric->itail)) ; + Numeric->tail_usage = 2 ; + p->header.prevsize = 0 ; + p->header.size = 1 ; + + /* allocate a 1-unit head marker block at the head of memory */ + /* this is done so that an offset of zero is treated as a NULL pointer */ + Numeric->ihead++ ; + + /* initial usage in Numeric->Memory */ + Numeric->max_usage = 3 ; + Numeric->init_usage = Numeric->max_usage ; + + /* Note that UMFPACK_*symbolic ensures that Numeric->Memory is of size */ + /* at least 3, so this initialization will always succeed. */ + +#ifndef NDEBUG + DEBUG2 (("init_memoryspace, all free (except one unit at head\n")) ; + UMF_dump_memory (Numeric) ; +#endif + +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_init_memoryspace.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_init_memoryspace.h new file mode 100644 index 0000000000..8626eeb9ac --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_mem_init_memoryspace.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_mem_init_memoryspace +( + NumericType *Numeric +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_realloc.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_realloc.c new file mode 100644 index 0000000000..339705bd61 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_realloc.c @@ -0,0 +1,76 @@ +/* ========================================================================== */ +/* === UMF_realloc ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Realloc a block previously allocated by UMF_malloc. + Return NULL on failure (in which case the block is still allocated, and will + be kept at is present size). This routine is only used for Numeric->Memory. +*/ + +#include "umf_internal.h" + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) +#include "umf_malloc.h" +#endif + +GLOBAL void *UMF_realloc +( + void *p, + Int n_objects, + size_t size_of_object +) +{ + size_t size ; + void *p2 ; + +#ifdef UMF_TCOV_TEST + /* For exhaustive statement coverage testing only! */ + /* Pretend to fail, to test out-of-memory conditions. */ + umf_realloc_fail-- ; + if (umf_realloc_fail <= umf_realloc_hi && + umf_realloc_fail >= umf_realloc_lo) + { + return ((void *) NULL) ; + } +#endif + + /* make sure that we allocate something */ + n_objects = MAX (1, n_objects) ; + + size = (size_t) n_objects ; + ASSERT (size_of_object > 1) ; + if (size > Int_MAX / size_of_object) + { + /* :: int overflow in umf_realloc :: */ + return ((void *) NULL) ; + } + size *= size_of_object ; + + DEBUG0 (("UMF_realloc: "ID" n_objects "ID" size_of_object "ID"\n", + (Int) p, n_objects, (Int) size_of_object)) ; + + /* see umf_config.h for the memory allocator selection */ + p2 = REALLOCATE (p, size) ; + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) + /* If p didn't exist on input, and p2 exists, then a new object has been + * allocated. */ + if (p == (void *) NULL && p2 != (void *) NULL) + { + UMF_malloc_count++ ; + } +#endif + + DEBUG0 (("UMF_realloc: "ID" new malloc count "ID"\n", + (Int) p2, UMF_malloc_count)) ; + + return (p2) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_realloc.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_realloc.h new file mode 100644 index 0000000000..376de63a3a --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_realloc.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void *UMF_realloc +( + void *p, + Int n_objects, + size_t size_of_object +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_perm.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_perm.c new file mode 100644 index 0000000000..0f67f3cf4d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_perm.c @@ -0,0 +1,86 @@ +/* ========================================================================== */ +/* === UMF_report_perm ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" + +#define PRINTF4U(params) { if (user || prl >= 4) PRINTF (params) ; } + +GLOBAL Int UMF_report_perm +( + Int n, + const Int P [ ], + Int W [ ], /* workspace of size n */ + Int prl, + Int user +) +{ + Int i, k, valid, prl1 ; + + ASSERT (prl >= 3) ; + + PRINTF4U (("permutation vector, n = "ID". ", n)) ; + + if (n <= 0) + { + PRINTF (("ERROR: length of permutation is <= 0\n\n")) ; + return (UMFPACK_ERROR_n_nonpositive) ; + } + + if (!P) + { + /* if P is (Int *) NULL, this is the identity permutation */ + PRINTF (("(not present)\n\n")) ; + return (UMFPACK_OK) ; + } + + if (!W) + { + PRINTF (("ERROR: out of memory\n\n")) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + PRINTF4 (("\n")) ; + + for (i = 0 ; i < n ; i++) + { + W [i] = TRUE ; + } + + prl1 = prl ; + for (k = 0 ; k < n ; k++) + { + i = P [k] ; + PRINTF4 ((" "ID" : "ID" ", INDEX (k), INDEX (i))) ; + valid = (i >= 0 && i < n) ; + if (valid) + { + valid = W [i] ; + W [i] = FALSE ; + } + if (!valid) + { + /* out of range or duplicate entry */ + PRINTF (("ERROR: invalid\n\n")) ; + return (UMFPACK_ERROR_invalid_permutation) ; + } + PRINTF4 (("\n")) ; + if (prl == 4 && k == 9 && n > 10) + { + PRINTF ((" ...\n")) ; + prl-- ; + } + } + prl = prl1 ; + + PRINTF4 ((" permutation vector ")) ; + PRINTF4U (("OK\n\n")) ; + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_perm.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_perm.h new file mode 100644 index 0000000000..5fd7a7ab07 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_perm.h @@ -0,0 +1,15 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_report_perm +( + Int n, + const Int P [ ], + Int W [ ], + Int prl, + Int user +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_vector.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_vector.c new file mode 100644 index 0000000000..d811d57421 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_vector.c @@ -0,0 +1,119 @@ +/* ========================================================================== */ +/* === UMF_report_vector ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" + +/* ========================================================================== */ +/* === print_value ========================================================== */ +/* ========================================================================== */ + +PRIVATE void print_value +( + Int i, + const double Xx [ ], + const double Xz [ ], + Int scalar /* if true, then print real part only */ +) +{ + Entry *X, xi ; + /* if Xz is null, then X is in "merged" format (compatible with Entry, */ + /* and ANSI C99 double _Complex type). */ + X = (Entry *) Xx ; + PRINTF ((" "ID" :", INDEX (i))) ; + if (scalar) + { + PRINT_SCALAR (Xx [i]) ; + } + else + { + if (Xz != (double *) NULL) + { + ASSIGN (xi, Xx [i], Xz [i]) ; + } + else + { + xi = X [i] ; + } + PRINT_ENTRY (xi) ; + } + PRINTF (("\n")) ; +} + +/* ========================================================================== */ +/* === UMF_report_vector ==================================================== */ +/* ========================================================================== */ + +GLOBAL Int UMF_report_vector +( + Int n, + const double Xx [ ], + const double Xz [ ], + Int prl, + Int user, + Int scalar +) +{ + Int n2, i ; + + if (user || prl >= 4) + { + PRINTF (("dense vector, n = "ID". ", n)) ; + } + + if (user) + { + if (!Xx) + { + PRINTF (("ERROR: vector not present\n\n")) ; + return (UMFPACK_ERROR_argument_missing) ; + } + if (n < 0) + { + PRINTF (("ERROR: length of vector is < 0\n\n")) ; + return (UMFPACK_ERROR_n_nonpositive) ; + } + } + + if (user || prl >= 4) + { + PRINTF4 (("\n")) ; + } + + if (prl == 4) + { + /* print level of 4 */ + n2 = MIN (10, n) ; + for (i = 0 ; i < n2 ; i++) + { + print_value (i, Xx, Xz, scalar) ; + } + if (n2 < n) + { + PRINTF ((" ...\n")) ; + print_value (n-1, Xx, Xz, scalar) ; + } + } + else if (prl > 4) + { + /* print level 4 or more */ + for (i = 0 ; i < n ; i++) + { + print_value (i, Xx, Xz, scalar) ; + } + } + + PRINTF4 ((" dense vector ")) ; + if (user || prl >= 4) + { + PRINTF (("OK\n\n")) ; + } + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_vector.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_vector.h new file mode 100644 index 0000000000..9ca5ae8197 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_report_vector.h @@ -0,0 +1,16 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_report_vector +( + Int n, + const double Xx [ ], + const double Xz [ ], + Int prl, + Int user, + Int scalar +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_row_search.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_row_search.c new file mode 100644 index 0000000000..3a73a7252c --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_row_search.c @@ -0,0 +1,838 @@ +/* ========================================================================== */ +/* === UMF_row_search ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Find two candidate pivot rows in a column: the best one in the front, + and the best one not in the front. Return the two pivot row patterns and + their exact degrees. Called by UMF_local_search. + + Returns UMFPACK_OK if successful, or UMFPACK_WARNING_singular_matrix or + UMFPACK_ERROR_different_pattern if not. + +*/ + +#include "umf_internal.h" +#include "umf_row_search.h" + +GLOBAL Int UMF_row_search +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic, + Int cdeg0, /* length of column in Front */ + Int cdeg1, /* length of column outside Front */ + const Int Pattern [ ], /* pattern of column, Pattern [0..cdeg1 -1] */ + const Int Pos [ ], /* Pos [Pattern [0..cdeg1 -1]] = 0..cdeg1 -1 */ + Int pivrow [2], /* pivrow [IN] and pivrow [OUT] */ + Int rdeg [2], /* rdeg [IN] and rdeg [OUT] */ + Int W_i [ ], /* pattern of pivrow [IN], */ + /* either Fcols or Woi */ + Int W_o [ ], /* pattern of pivrow [OUT], */ + /* either Wio or Woo */ + Int prior_pivrow [2], /* the two other rows just scanned, if any */ + const Entry Wxy [ ], /* numerical values Wxy [0..cdeg1-1], + either Wx or Wy */ + + Int pivcol, /* the candidate column being searched */ + Int freebie [ ] +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + double maxval, toler, toler2, value, pivot [2] ; + Int i, row, deg, col, *Frpos, fnrows, *E, j, ncols, *Cols, *Rows, + e, f, Wrpflag, *Fcpos, fncols, tpi, max_rdeg, nans_in_col, was_offdiag, + diag_row, prefer_diagonal, *Wrp, found, *Diagonal_map ; + Tuple *tp, *tpend, *tp1, *tp2 ; + Unit *Memory, *p ; + Element *ep ; + Int *Row_tuples, *Row_degree, *Row_tlen ; + +#ifndef NDEBUG + Int *Col_degree ; + DEBUG2 (("Row_search:\n")) ; + for (i = 0 ; i < cdeg1 ; i++) + { + row = Pattern [i] ; + DEBUG4 ((" row: "ID"\n", row)) ; + ASSERT (row >= 0 && row < Numeric->n_row) ; + ASSERT (i == Pos [row]) ; + } + /* If row is not in Pattern [0..cdeg1-1], then Pos [row] == EMPTY */ + if (UMF_debug > 0 || Numeric->n_row < 1000) + { + Int cnt = cdeg1 ; + DEBUG4 (("Scan all rows:\n")) ; + for (row = 0 ; row < Numeric->n_row ; row++) + { + if (Pos [row] < 0) + { + cnt++ ; + } + else + { + DEBUG4 ((" row: "ID" pos "ID"\n", row, Pos [row])) ; + } + } + ASSERT (cnt == Numeric->n_row) ; + } + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro only */ + ASSERT (pivcol >= 0 && pivcol < Work->n_col) ; + ASSERT (NON_PIVOTAL_COL (pivcol)) ; +#endif + + pivot [IN] = 0. ; + pivot [OUT] = 0. ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + Row_degree = Numeric->Rperm ; + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Wrp = Work->Wrp ; + Frpos = Work->Frpos ; + E = Work->E ; + Memory = Numeric->Memory ; + fnrows = Work->fnrows ; + + prefer_diagonal = Symbolic->prefer_diagonal ; + Diagonal_map = Work->Diagonal_map ; + + if (Diagonal_map) + { + diag_row = Diagonal_map [pivcol] ; + was_offdiag = diag_row < 0 ; + if (was_offdiag) + { + /* the "diagonal" entry in this column was permuted here by an + * earlier pivot choice. The tighter off-diagonal tolerance will + * be used instead of the symmetric tolerance. */ + diag_row = FLIP (diag_row) ; + } + ASSERT (diag_row >= 0 && diag_row < Numeric->n_row) ; + } + else + { + diag_row = EMPTY ; /* unused */ + was_offdiag = EMPTY ; /* unused */ + } + + /* pivot row degree cannot exceed max_rdeg */ + max_rdeg = Work->fncols_max ; + + /* ---------------------------------------------------------------------- */ + /* scan pivot column for candidate rows */ + /* ---------------------------------------------------------------------- */ + + maxval = 0.0 ; + nans_in_col = FALSE ; + + for (i = 0 ; i < cdeg1 ; i++) + { + APPROX_ABS (value, Wxy [i]) ; + if (SCALAR_IS_NAN (value)) + { + nans_in_col = TRUE ; + maxval = value ; + break ; + } + /* This test can now ignore the NaN case: */ + maxval = MAX (maxval, value) ; + } + + /* if maxval is zero, the matrix is numerically singular */ + + toler = Numeric->relpt * maxval ; + toler2 = Numeric->relpt2 * maxval ; + toler2 = was_offdiag ? toler : toler2 ; + + DEBUG5 (("Row_search begins [ maxval %g toler %g %g\n", + maxval, toler, toler2)) ; + if (SCALAR_IS_NAN (toler) || SCALAR_IS_NAN (toler2)) + { + nans_in_col = TRUE ; + } + + if (!nans_in_col) + { + + /* look for the diagonal entry, if it exists */ + found = FALSE ; + ASSERT (!SCALAR_IS_NAN (toler)) ; + + if (prefer_diagonal) + { + ASSERT (diag_row != EMPTY) ; + i = Pos [diag_row] ; + if (i >= 0) + { + double a ; + ASSERT (i < cdeg1) ; + ASSERT (diag_row == Pattern [i]) ; + + APPROX_ABS (a, Wxy [i]) ; + + ASSERT (!SCALAR_IS_NAN (a)) ; + ASSERT (!SCALAR_IS_NAN (toler2)) ; + + if (SCALAR_IS_NONZERO (a) && a >= toler2) + { + /* found it! */ + DEBUG3 (("Symmetric pivot: "ID" "ID"\n", pivcol, diag_row)); + found = TRUE ; + if (Frpos [diag_row] >= 0 && Frpos [diag_row] < fnrows) + { + pivrow [IN] = diag_row ; + pivrow [OUT] = EMPTY ; + } + else + { + pivrow [IN] = EMPTY ; + pivrow [OUT] = diag_row ; + } + } + } + } + + /* either no diagonal found, or we didn't look for it */ + if (!found) + { + if (cdeg0 > 0) + { + + /* this is a column in the front */ + for (i = 0 ; i < cdeg0 ; i++) + { + double a ; + APPROX_ABS (a, Wxy [i]) ; + ASSERT (!SCALAR_IS_NAN (a)) ; + ASSERT (!SCALAR_IS_NAN (toler)) ; + if (SCALAR_IS_NONZERO (a) && a >= toler) + { + row = Pattern [i] ; + deg = Row_degree [row] ; +#ifndef NDEBUG + DEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n", + i, row, deg, a)) ; + UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ; +#endif + ASSERT (Frpos [row] >= 0 && Frpos [row] < fnrows) ; + ASSERT (Frpos [row] == i) ; + /* row is in the current front */ + DEBUG4 ((" in front\n")) ; + if (deg < rdeg [IN] + /* break ties by picking the largest entry: */ + || (deg == rdeg [IN] && a > pivot [IN]) + /* break ties by picking the diagonal entry: */ + /* || (deg == rdeg [IN] && row == diag_row) */ + ) + { + /* best row in front, so far */ + pivrow [IN] = row ; + rdeg [IN] = deg ; + pivot [IN] = a ; + } + } + } + for ( ; i < cdeg1 ; i++) + { + double a ; + APPROX_ABS (a, Wxy [i]) ; + ASSERT (!SCALAR_IS_NAN (a)) ; + ASSERT (!SCALAR_IS_NAN (toler)) ; + if (SCALAR_IS_NONZERO (a) && a >= toler) + { + row = Pattern [i] ; + deg = Row_degree [row] ; +#ifndef NDEBUG + DEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n", + i, row, deg, a)) ; + UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ; +#endif + ASSERT (Frpos [row] == i) ; + /* row is not in the current front */ + DEBUG4 ((" NOT in front\n")) ; + if (deg < rdeg [OUT] + /* break ties by picking the largest entry: */ + || (deg == rdeg [OUT] && a > pivot [OUT]) + /* break ties by picking the diagonal entry: */ + /* || (deg == rdeg [OUT] && row == diag_row) */ + ) + { + /* best row not in front, so far */ + pivrow [OUT] = row ; + rdeg [OUT] = deg ; + pivot [OUT] = a ; + } + } + } + + } + else + { + + /* this column is not in the front */ + for (i = 0 ; i < cdeg1 ; i++) + { + double a ; + APPROX_ABS (a, Wxy [i]) ; + ASSERT (!SCALAR_IS_NAN (a)) ; + ASSERT (!SCALAR_IS_NAN (toler)) ; + if (SCALAR_IS_NONZERO (a) && a >= toler) + { + row = Pattern [i] ; + deg = Row_degree [row] ; +#ifndef NDEBUG + DEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n", + i, row, deg, a)) ; + UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ; +#endif + if (Frpos [row] >= 0 && Frpos [row] < fnrows) + { + /* row is in the current front */ + DEBUG4 ((" in front\n")) ; + if (deg < rdeg [IN] + /* break ties by picking the largest entry: */ + || (deg == rdeg [IN] && a > pivot [IN]) + /* break ties by picking the diagonal entry: */ + /* || (deg == rdeg [IN] && row == diag_row) */ + ) + { + /* best row in front, so far */ + pivrow [IN] = row ; + rdeg [IN] = deg ; + pivot [IN] = a ; + } + } + else + { + /* row is not in the current front */ + DEBUG4 ((" NOT in front\n")) ; + if (deg < rdeg [OUT] + /* break ties by picking the largest entry: */ + || (deg == rdeg[OUT] && a > pivot [OUT]) + /* break ties by picking the diagonal entry: */ + /* || (deg == rdeg[OUT] && row == diag_row) */ + ) + { + /* best row not in front, so far */ + pivrow [OUT] = row ; + rdeg [OUT] = deg ; + pivot [OUT] = a ; + } + } + } + } + } + } + } + + /* ---------------------------------------------------------------------- */ + /* NaN handling */ + /* ---------------------------------------------------------------------- */ + + /* if cdeg1 > 0 then we must have found a pivot row ... unless NaN's */ + /* exist. Try with no numerical tests if no pivot found. */ + + if (cdeg1 > 0 && pivrow [IN] == EMPTY && pivrow [OUT] == EMPTY) + { + /* cleanup for the NaN case */ + DEBUG0 (("Found a NaN in pivot column!\n")) ; + + /* grab the first entry in the pivot column, ignoring degree, */ + /* numerical stability, and symmetric preference */ + row = Pattern [0] ; + deg = Row_degree [row] ; + if (Frpos [row] >= 0 && Frpos [row] < fnrows) + { + /* row is in the current front */ + DEBUG4 ((" in front\n")) ; + pivrow [IN] = row ; + rdeg [IN] = deg ; + } + else + { + /* row is not in the current front */ + DEBUG4 ((" NOT in front\n")) ; + pivrow [OUT] = row ; + rdeg [OUT] = deg ; + } + + /* We are now guaranteed to have a pivot, no matter how broken */ + /* (non-IEEE compliant) the underlying numerical operators are. */ + /* This is particularly a problem for Microsoft compilers (they do */ + /* not handle NaN's properly). Now try to find a sparser pivot, if */ + /* possible. */ + + for (i = 1 ; i < cdeg1 ; i++) + { + row = Pattern [i] ; + deg = Row_degree [row] ; + + if (Frpos [row] >= 0 && Frpos [row] < fnrows) + { + /* row is in the current front */ + DEBUG4 ((" in front\n")) ; + if (deg < rdeg [IN] || (deg == rdeg [IN] && row == diag_row)) + { + /* best row in front, so far */ + pivrow [IN] = row ; + rdeg [IN] = deg ; + } + } + else + { + /* row is not in the current front */ + DEBUG4 ((" NOT in front\n")) ; + if (deg < rdeg [OUT] || (deg == rdeg [OUT] && row == diag_row)) + { + /* best row not in front, so far */ + pivrow [OUT] = row ; + rdeg [OUT] = deg ; + } + } + } + } + + /* We found a pivot if there are entries (even zero ones) in pivot col */ + ASSERT (IMPLIES (cdeg1 > 0, pivrow[IN] != EMPTY || pivrow[OUT] != EMPTY)) ; + + /* If there are no entries in the pivot column, then no pivot is found */ + ASSERT (IMPLIES (cdeg1 == 0, pivrow[IN] == EMPTY && pivrow[OUT] == EMPTY)) ; + + /* ---------------------------------------------------------------------- */ + /* check for singular matrix */ + /* ---------------------------------------------------------------------- */ + + if (cdeg1 == 0) + { + if (fnrows > 0) + { + /* + Get the pivrow [OUT][IN] from the current front. + The frontal matrix looks like this: + + pivcol[OUT] + | + v + x x x x 0 <- so grab this row as the pivrow [OUT][IN]. + x x x x 0 + x x x x 0 + 0 0 0 0 0 + + The current frontal matrix has some rows in it. The degree + of the pivcol[OUT] is zero. The column is empty, and the + current front does not contribute to it. + + */ + pivrow [IN] = Work->Frows [0] ; + DEBUGm4 (("Got zero pivrow[OUT][IN] "ID" from current front\n", + pivrow [IN])) ; + } + else + { + + /* + Get a pivot row from the row-merge tree, use as + pivrow [OUT][OUT]. pivrow [IN] remains EMPTY. + This can only happen if the current front is 0-by-0. + */ + + Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1, + row2, fleftmost, nfr, n_row, frontid ; + + ASSERT (Work->fncols == 0) ; + + Front_leftmostdesc = Symbolic->Front_leftmostdesc ; + Front_1strow = Symbolic->Front_1strow ; + Front_new1strow = Work->Front_new1strow ; + nfr = Symbolic->nfr ; + n_row = Numeric->n_row ; + frontid = Work->frontid ; + + DEBUGm4 (("Note: pivcol: "ID" is empty front "ID"\n", + pivcol, frontid)) ; +#ifndef NDEBUG + DEBUG1 (("Calling dump rowmerge\n")) ; + UMF_dump_rowmerge (Numeric, Symbolic, Work) ; +#endif + + /* Row-merge set is the non-pivotal rows in the range */ + /* Front_new1strow [Front_leftmostdesc [frontid]] to */ + /* Front_1strow [frontid+1] - 1. */ + /* If this is empty, then use the empty rows, in the range */ + /* Front_new1strow [nfr] to n_row-1. */ + /* If this too is empty, then pivrow [OUT] will be empty. */ + /* In both cases, update Front_new1strow [...]. */ + + fleftmost = Front_leftmostdesc [frontid] ; + row1 = Front_new1strow [fleftmost] ; + row2 = Front_1strow [frontid+1] - 1 ; + DEBUG1 (("Leftmost: "ID" Rows ["ID" to "ID"] srch ["ID" to "ID"]\n", + fleftmost, Front_1strow [frontid], row2, row1, row2)) ; + + /* look in the range row1 ... row2 */ + for (row = row1 ; row <= row2 ; row++) + { + DEBUG3 ((" Row: "ID"\n", row)) ; + if (NON_PIVOTAL_ROW (row)) + { + /* found it */ + DEBUG3 ((" Row: "ID" found\n", row)) ; + ASSERT (Frpos [row] == EMPTY) ; + pivrow [OUT] = row ; + DEBUGm4 (("got row merge pivrow %d\n", pivrow [OUT])) ; + break ; + } + } + Front_new1strow [fleftmost] = row ; + + if (pivrow [OUT] == EMPTY) + { + /* not found, look in empty row set in "dummy" front */ + row1 = Front_new1strow [nfr] ; + row2 = n_row-1 ; + DEBUG3 (("Empty: "ID" Rows ["ID" to "ID"] srch["ID" to "ID"]\n", + nfr, Front_1strow [nfr], row2, row1, row2)) ; + + /* look in the range row1 ... row2 */ + for (row = row1 ; row <= row2 ; row++) + { + DEBUG3 ((" Empty Row: "ID"\n", row)) ; + if (NON_PIVOTAL_ROW (row)) + { + /* found it */ + DEBUG3 ((" Empty Row: "ID" found\n", row)) ; + ASSERT (Frpos [row] == EMPTY) ; + pivrow [OUT] = row ; + DEBUGm4 (("got dummy row pivrow %d\n", pivrow [OUT])) ; + break ; + } + } + Front_new1strow [nfr] = row ; + } + + if (pivrow [OUT] == EMPTY) + { + /* Row-merge set is empty. We can just discard */ + /* the candidate pivot column. */ + DEBUG0 (("Note: row-merge set empty\n")) ; + DEBUGm4 (("got no pivrow \n")) ; + return (UMFPACK_WARNING_singular_matrix) ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* construct the candidate row in the front, if any */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + /* check Wrp */ + ASSERT (Work->Wrpflag > 0) ; + if (UMF_debug > 0 || Work->n_col < 1000) + { + for (i = 0 ; i < Work->n_col ; i++) + { + ASSERT (Wrp [i] < Work->Wrpflag) ; + } + } +#endif + +#ifndef NDEBUG + DEBUG4 (("pivrow [IN]: "ID"\n", pivrow [IN])) ; + UMF_dump_rowcol (0, Numeric, Work, pivrow [IN], TRUE) ; +#endif + + if (pivrow [IN] != EMPTY) + { + + /* the row merge candidate row is not pivrow [IN] */ + freebie [IN] = (pivrow [IN] == prior_pivrow [IN]) && (cdeg1 > 0) ; + ASSERT (cdeg1 >= 0) ; + + if (!freebie [IN]) + { + /* include current front in the degree of this row */ + + Fcpos = Work->Fcpos ; + fncols = Work->fncols ; + + Wrpflag = Work->Wrpflag ; + + /* -------------------------------------------------------------- */ + /* construct the pattern of the IN row */ + /* -------------------------------------------------------------- */ + +#ifndef NDEBUG + /* check Fcols */ + DEBUG5 (("ROW ASSEMBLE: rdeg "ID"\nREDUCE ROW "ID"\n", + fncols, pivrow [IN])) ; + for (j = 0 ; j < fncols ; j++) + { + col = Work->Fcols [j] ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (Fcpos [col] >= 0) ; + } + if (UMF_debug > 0 || Work->n_col < 1000) + { + Int cnt = fncols ; + for (col = 0 ; col < Work->n_col ; col++) + { + if (Fcpos [col] < 0) cnt++ ; + } + ASSERT (cnt == Work->n_col) ; + } +#endif + + rdeg [IN] = fncols ; + + ASSERT (pivrow [IN] >= 0 && pivrow [IN] < Work->n_row) ; + ASSERT (NON_PIVOTAL_ROW (pivrow [IN])) ; + + /* add the pivot column itself */ + ASSERT (Wrp [pivcol] != Wrpflag) ; + if (Fcpos [pivcol] < 0) + { + DEBUG3 (("Adding pivot col to pivrow [IN] pattern\n")) ; + if (rdeg [IN] >= max_rdeg) + { + /* :: pattern change (in) :: */ + return (UMFPACK_ERROR_different_pattern) ; + } + Wrp [pivcol] = Wrpflag ; + W_i [rdeg [IN]++] = pivcol ; + } + + tpi = Row_tuples [pivrow [IN]] ; + if (tpi) + { + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Row_tlen [pivrow [IN]] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) + { + continue ; /* element already deallocated */ + } + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + ncols = ep->ncols ; + Rows = Cols + ncols ; + if (Rows [f] == EMPTY) + { + continue ; /* row already assembled */ + } + ASSERT (pivrow [IN] == Rows [f]) ; + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + ASSERT (col >= EMPTY && col < Work->n_col) ; + if ((col >= 0) && (Wrp [col] != Wrpflag) + && Fcpos [col] <0) + { + ASSERT (NON_PIVOTAL_COL (col)) ; + if (rdeg [IN] >= max_rdeg) + { + /* :: pattern change (rdeg in failure) :: */ + DEBUGm4 (("rdeg [IN] >= max_rdeg failure\n")) ; + return (UMFPACK_ERROR_different_pattern) ; + } + Wrp [col] = Wrpflag ; + W_i [rdeg [IN]++] = col ; + } + } + + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Row_tlen [pivrow [IN]] = tp2 - tp1 ; + } + +#ifndef NDEBUG + DEBUG4 (("Reduced IN row:\n")) ; + for (j = 0 ; j < fncols ; j++) + { + DEBUG6 ((" "ID" "ID" "ID"\n", + j, Work->Fcols [j], Fcpos [Work->Fcols [j]])) ; + ASSERT (Fcpos [Work->Fcols [j]] >= 0) ; + } + for (j = fncols ; j < rdeg [IN] ; j++) + { + DEBUG6 ((" "ID" "ID" "ID"\n", j, W_i [j], Wrp [W_i [j]])); + ASSERT (W_i [j] >= 0 && W_i [j] < Work->n_col) ; + ASSERT (Wrp [W_i [j]] == Wrpflag) ; + } + /* mark the end of the pattern in case we scan it by mistake */ + /* Note that this means W_i must be of size >= fncols_max + 1 */ + W_i [rdeg [IN]] = EMPTY ; +#endif + + /* rdeg [IN] is now the exact degree of the IN row */ + + /* clear Work->Wrp. */ + Work->Wrpflag++ ; + /* All Wrp [0..n_col] is now < Wrpflag */ + } + } + +#ifndef NDEBUG + /* check Wrp */ + if (UMF_debug > 0 || Work->n_col < 1000) + { + for (i = 0 ; i < Work->n_col ; i++) + { + ASSERT (Wrp [i] < Work->Wrpflag) ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* construct the candidate row not in the front, if any */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG4 (("pivrow [OUT]: "ID"\n", pivrow [OUT])) ; + UMF_dump_rowcol (0, Numeric, Work, pivrow [OUT], TRUE) ; +#endif + + /* If this is a candidate row from the row merge set, force it to be */ + /* scanned (ignore prior_pivrow [OUT]). */ + + if (pivrow [OUT] != EMPTY) + { + freebie [OUT] = (pivrow [OUT] == prior_pivrow [OUT]) && cdeg1 > 0 ; + ASSERT (cdeg1 >= 0) ; + + if (!freebie [OUT]) + { + + Wrpflag = Work->Wrpflag ; + + /* -------------------------------------------------------------- */ + /* construct the pattern of the row */ + /* -------------------------------------------------------------- */ + + rdeg [OUT] = 0 ; + + ASSERT (pivrow [OUT] >= 0 && pivrow [OUT] < Work->n_row) ; + ASSERT (NON_PIVOTAL_ROW (pivrow [OUT])) ; + + /* add the pivot column itself */ + ASSERT (Wrp [pivcol] != Wrpflag) ; + DEBUG3 (("Adding pivot col to pivrow [OUT] pattern\n")) ; + if (rdeg [OUT] >= max_rdeg) + { + /* :: pattern change (out) :: */ + return (UMFPACK_ERROR_different_pattern) ; + } + Wrp [pivcol] = Wrpflag ; + W_o [rdeg [OUT]++] = pivcol ; + + tpi = Row_tuples [pivrow [OUT]] ; + if (tpi) + { + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Row_tlen [pivrow [OUT]] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) + { + continue ; /* element already deallocated */ + } + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + ncols = ep->ncols ; + Rows = Cols + ncols ; + if (Rows [f] == EMPTY) + { + continue ; /* row already assembled */ + } + ASSERT (pivrow [OUT] == Rows [f]) ; + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + ASSERT (col >= EMPTY && col < Work->n_col) ; + if ((col >= 0) && (Wrp [col] != Wrpflag)) + { + ASSERT (NON_PIVOTAL_COL (col)) ; + if (rdeg [OUT] >= max_rdeg) + { + /* :: pattern change (rdeg out failure) :: */ + DEBUGm4 (("rdeg [OUT] failure\n")) ; + return (UMFPACK_ERROR_different_pattern) ; + } + Wrp [col] = Wrpflag ; + W_o [rdeg [OUT]++] = col ; + } + } + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Row_tlen [pivrow [OUT]] = tp2 - tp1 ; + } + +#ifndef NDEBUG + DEBUG4 (("Reduced row OUT:\n")) ; + for (j = 0 ; j < rdeg [OUT] ; j++) + { + DEBUG6 ((" "ID" "ID" "ID"\n", j, W_o [j], Wrp [W_o [j]])) ; + ASSERT (W_o [j] >= 0 && W_o [j] < Work->n_col) ; + ASSERT (Wrp [W_o [j]] == Wrpflag) ; + } + /* mark the end of the pattern in case we scan it by mistake */ + /* Note that this means W_o must be of size >= fncols_max + 1 */ + W_o [rdeg [OUT]] = EMPTY ; +#endif + + /* rdeg [OUT] is now the exact degree of the row */ + + /* clear Work->Wrp. */ + Work->Wrpflag++ ; + /* All Wrp [0..n] is now < Wrpflag */ + + } + + } + DEBUG5 (("Row_search end ] \n")) ; + +#ifndef NDEBUG + /* check Wrp */ + if (UMF_debug > 0 || Work->n_col < 1000) + { + for (i = 0 ; i < Work->n_col ; i++) + { + ASSERT (Wrp [i] < Work->Wrpflag) ; + } + } +#endif + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_row_search.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_row_search.h new file mode 100644 index 0000000000..9490c87532 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_row_search.h @@ -0,0 +1,33 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_row_search +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic, + Int cdeg0, + Int cdeg1, + const Int Pattern [ ], + const Int Pos [ ], + Int pivrow [2], + Int rdeg [2], + Int W_i [ ], + Int W_o [ ], + Int prior_pivrow [2], + const Entry Wxy [ ], + Int pivcol, + Int freebie [2] +) ; + +#define IN 0 +#define OUT 1 + +#define IN_IN 0 +#define IN_OUT 1 +#define OUT_IN 2 +#define OUT_OUT 3 diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale.c new file mode 100644 index 0000000000..b07654f9db --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale.c @@ -0,0 +1,128 @@ +/* ========================================================================== */ +/* === UMF_scale ============================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Divide a vector of stride 1 by the pivot value. */ + +#include "umf_internal.h" + +GLOBAL void UMF_scale +( + Int n, + Entry pivot, + Entry X [ ] +) +{ + Int i ; + Entry x ; + double s ; + + /* ---------------------------------------------------------------------- */ + /* compute the approximate absolute value of the pivot, and select method */ + /* ---------------------------------------------------------------------- */ + + APPROX_ABS (s, pivot) ; + + if (s < RECIPROCAL_TOLERANCE || IS_NAN (pivot)) + { + /* ------------------------------------------------------------------ */ + /* tiny, or zero, pivot case */ + /* ------------------------------------------------------------------ */ + + /* The pivot is tiny, or NaN. Do not divide zero by the pivot value, + * and do not multiply by 1/pivot, either. */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] /= pivot ; */ + x = X [i] ; + +#ifndef NO_DIVIDE_BY_ZERO + if (IS_NONZERO (x)) + { + DIV (X [i], x, pivot) ; + } +#else + /* Do not divide by zero */ + if (IS_NONZERO (x) && IS_NONZERO (pivot)) + { + DIV (X [i], x, pivot) ; + } +#endif + + } + + } + else + { + + /* ------------------------------------------------------------------ */ + /* normal case. select the x/pivot or x * (1/pivot) method */ + /* ------------------------------------------------------------------ */ + + /* The pivot is not tiny, and is not NaN. Don't bother to check for + * zeros in the pivot column, X. */ + +#if !defined (NRECIPROCAL) && !(defined (__GNUC__) && defined (COMPLEX)) + + /* -------------------------------------------------------------- */ + /* multiply x by (1/pivot) */ + /* -------------------------------------------------------------- */ + + /* Slightly less accurate, but faster. It allows the use of + * the level-1 BLAS dscal or zscal routine. This not used when + * UMFPACK is used in MATLAB (either as a built-in routine, or as + * a mexFunction). + * + * Using gcc version 3.2 can cause the following code to fail for + * some complex matrices (not all), with or without the BLAS. This + * was found in Red Hat Linux 7.3 on a Dell Latitude C840 with a + * Pentium 4M. Thus, this code is not used when gcc is used, for + * the complex case. + * + * It works just fine with Intel's icc compiler, version 7.0. + */ + + /* pivot = 1 / pivot */ + RECIPROCAL (pivot) ; + +#if defined (USE_NO_BLAS) + for (i = 0 ; i < n ; i++) + { + /* X [i] *= pivot ; */ + x = X [i] ; + MULT (X [i], x, pivot) ; + } +#else + BLAS_SCAL (n, pivot, X) ; +#endif + +#else + + /* -------------------------------------------------------------- */ + /* divide x by the pivot */ + /* -------------------------------------------------------------- */ + + /* This is slightly more accurate, particularly if the pivot column + * consists of only IEEE subnormals. Always do this if UMFPACK is + * being compiled as a built-in routine or mexFunction in MATLAB, + * or if gcc is being used with complex matrices. */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] /= pivot ; */ + x = X [i] ; + DIV (X [i], x, pivot) ; + } + +#endif + + } +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale.h new file mode 100644 index 0000000000..74967a064e --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_scale +( + Int n, + Entry alpha, + Entry X [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale_column.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale_column.c new file mode 100644 index 0000000000..2c47898280 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale_column.c @@ -0,0 +1,434 @@ +/* ========================================================================== */ +/* === UMF_scale_column ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Scale the current pivot column, move the pivot row and column into place, + and log the permutation. +*/ + +#include "umf_internal.h" +#include "umf_mem_free_tail_block.h" +#include "umf_scale.h" + +/* ========================================================================== */ +/* === shift_pivot_row ====================================================== */ +/* ========================================================================== */ + +/* Except for the BLAS, most of the time is typically spent in the following + * shift_pivot_row routine. It copies the pivot row into the U block, and + * then fills in the whole in the C block by shifting the last row of C into + * the row vacated by the pivot row. + */ + +PRIVATE void shift_pivot_row (Entry *Fd, Entry *Fs, Entry *Fe, Int len, Int d) +{ + Int j ; +#pragma ivdep + for (j = 0 ; j < len ; j++) + { + Fd [j] = Fs [j*d] ; + Fs [j*d] = Fe [j*d] ; + } +} + +/* ========================================================================== */ +/* === UMF_scale_column ===================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_scale_column +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int k, k1, fnr_curr, fnrows, fncols, *Frpos, *Fcpos, pivrow, pivcol, + *Frows, *Fcols, fnc_curr, fnpiv, *Row_tuples, nb, + *Col_tuples, *Rperm, *Cperm, fspos, col2, row2 ; + Entry pivot_value, *Fcol, + *Flublock, *Flblock, *Fublock, *Fcblock ; +#ifndef NDEBUG + Int *Col_degree, *Row_degree ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + fnrows = Work->fnrows ; + fncols = Work->fncols ; + fnpiv = Work->fnpiv ; + + /* ---------------------------------------------------------------------- */ + + Rperm = Numeric->Rperm ; + Cperm = Numeric->Cperm ; + + /* ---------------------------------------------------------------------- */ + + Flublock = Work->Flublock ; + Flblock = Work->Flblock ; + Fublock = Work->Fublock ; + Fcblock = Work->Fcblock ; + + fnr_curr = Work->fnr_curr ; + fnc_curr = Work->fnc_curr ; + Frpos = Work->Frpos ; + Fcpos = Work->Fcpos ; + Frows = Work->Frows ; + Fcols = Work->Fcols ; + pivrow = Work->pivrow ; + pivcol = Work->pivcol ; + + ASSERT (pivrow >= 0 && pivrow < Work->n_row) ; + ASSERT (pivcol >= 0 && pivcol < Work->n_col) ; + +#ifndef NDEBUG + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ +#endif + + Row_tuples = Numeric->Uip ; + Col_tuples = Numeric->Lip ; + nb = Work->nb ; + +#ifndef NDEBUG + ASSERT (fnrows == Work->fnrows_new + 1) ; + ASSERT (fncols == Work->fncols_new + 1) ; + DEBUG1 (("SCALE COL: fnrows "ID" fncols "ID"\n", fnrows, fncols)) ; + DEBUG2 (("\nFrontal matrix, including all space:\n" + "fnr_curr "ID" fnc_curr "ID" nb "ID"\n" + "fnrows "ID" fncols "ID" fnpiv "ID"\n", + fnr_curr, fnc_curr, nb, fnrows, fncols, fnpiv)) ; + DEBUG2 (("\nJust the active part:\n")) ; + DEBUG7 (("C block: ")) ; + UMF_dump_dense (Fcblock, fnr_curr, fnrows, fncols) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (Flblock, fnr_curr, fnrows, fnpiv); + DEBUG7 (("U' block: ")) ; + UMF_dump_dense (Fublock, fnc_curr, fncols, fnpiv) ; + DEBUG7 (("LU block: ")) ; + UMF_dump_dense (Flublock, nb, fnpiv, fnpiv) ; +#endif + + /* ====================================================================== */ + /* === Shift pivot row and column ======================================= */ + /* ====================================================================== */ + + /* ---------------------------------------------------------------------- */ + /* move pivot column into place */ + /* ---------------------------------------------------------------------- */ + + /* Note that the pivot column is already in place. Just shift the last + * column into the position vacated by the pivot column. */ + + fspos = Fcpos [pivcol] ; + + /* one less column in the contribution block */ + fncols = --(Work->fncols) ; + + if (fspos != fncols * fnr_curr) + { + + Int fs = fspos / fnr_curr ; + + DEBUG6 (("Shift pivot column in front\n")) ; + DEBUG6 (("fspos: "ID" flpos: "ID"\n", fspos, fncols * fnr_curr)) ; + + /* ------------------------------------------------------------------ */ + /* move Fe => Fs */ + /* ------------------------------------------------------------------ */ + + /* column of the contribution block: */ + { + /* Fs: current position of pivot column in contribution block */ + /* Fe: position of last column in contribution block */ + Int i ; + Entry *Fs, *Fe ; + Fs = Fcblock + fspos ; + Fe = Fcblock + fncols * fnr_curr ; +#pragma ivdep + for (i = 0 ; i < fnrows ; i++) + { + Fs [i] = Fe [i] ; + } + } + + /* column of the U2 block */ + { + /* Fs: current position of pivot column in U block */ + /* Fe: last column in U block */ + Int i ; + Entry *Fs, *Fe ; + Fs = Fublock + fs ; + Fe = Fublock + fncols ; +#pragma ivdep + for (i = 0 ; i < fnpiv ; i++) + { + Fs [i * fnc_curr] = Fe [i * fnc_curr] ; + } + } + + /* move column Fe to Fs in the Fcols pattern */ + col2 = Fcols [fncols] ; + Fcols [fs] = col2 ; + Fcpos [col2] = fspos ; + } + + /* pivot column is no longer in the frontal matrix */ + Fcpos [pivcol] = EMPTY ; + +#ifndef NDEBUG + DEBUG2 (("\nFrontal matrix after col swap, including all space:\n" + "fnr_curr "ID" fnc_curr "ID" nb "ID"\n" + "fnrows "ID" fncols "ID" fnpiv "ID"\n", + fnr_curr, fnc_curr, nb, + fnrows, fncols, fnpiv)) ; + DEBUG2 (("\nJust the active part:\n")) ; + DEBUG7 (("C block: ")) ; + UMF_dump_dense (Fcblock, fnr_curr, fnrows, fncols) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (Flblock, fnr_curr, fnrows, fnpiv+1); + DEBUG7 (("U' block: ")) ; + UMF_dump_dense (Fublock, fnc_curr, fncols, fnpiv) ; + DEBUG7 (("LU block: ")) ; + UMF_dump_dense (Flublock, nb, fnpiv, fnpiv+1) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* move pivot row into place */ + /* ---------------------------------------------------------------------- */ + + fspos = Frpos [pivrow] ; + + /* one less row in the contribution block */ + fnrows = --(Work->fnrows) ; + + DEBUG6 (("Swap/shift pivot row in front:\n")) ; + DEBUG6 (("fspos: "ID" flpos: "ID"\n", fspos, fnrows)) ; + + if (fspos == fnrows) + { + + /* ------------------------------------------------------------------ */ + /* move Fs => Fd */ + /* ------------------------------------------------------------------ */ + + DEBUG6 (("row case 1\n")) ; + + /* row of the contribution block: */ + { + Int j ; + Entry *Fd, *Fs ; + Fd = Fublock + fnpiv * fnc_curr ; + Fs = Fcblock + fspos ; +#pragma ivdep + for (j = 0 ; j < fncols ; j++) + { + Fd [j] = Fs [j * fnr_curr] ; + } + } + + /* row of the L2 block: */ + if (Work->pivrow_in_front) + { + Int j ; + Entry *Fd, *Fs ; + Fd = Flublock + fnpiv ; + Fs = Flblock + fspos ; +#pragma ivdep + for (j = 0 ; j <= fnpiv ; j++) + { + Fd [j * nb] = Fs [j * fnr_curr] ; + } + } + else + { + Int j ; + Entry *Fd, *Fs ; + Fd = Flublock + fnpiv ; + Fs = Flblock + fspos ; +#pragma ivdep + for (j = 0 ; j < fnpiv ; j++) + { + ASSERT (IS_ZERO (Fs [j * fnr_curr])) ; + CLEAR (Fd [j * nb]) ; + } + Fd [fnpiv * nb] = Fs [fnpiv * fnr_curr] ; + } + } + else + { + + /* ------------------------------------------------------------------ */ + /* move Fs => Fd */ + /* move Fe => Fs */ + /* ------------------------------------------------------------------ */ + + DEBUG6 (("row case 2\n")) ; + /* this is the most common case, by far */ + + /* row of the contribution block: */ + { + /* Fd: destination of pivot row on U block */ + /* Fs: current position of pivot row in contribution block */ + /* Fe: position of last row in contribution block */ + Entry *Fd, *Fs, *Fe ; + Fd = Fublock + fnpiv * fnc_curr ; + Fs = Fcblock + fspos ; + Fe = Fcblock + fnrows ; + shift_pivot_row (Fd, Fs, Fe, fncols, fnr_curr) ; + } + + /* row of the L2 block: */ + if (Work->pivrow_in_front) + { + /* Fd: destination of pivot row in LU block */ + /* Fs: current position of pivot row in L block */ + /* Fe: last row in L block */ + Int j ; + Entry *Fd, *Fs, *Fe ; + Fd = Flublock + fnpiv ; + Fs = Flblock + fspos ; + Fe = Flblock + fnrows ; +#pragma ivdep + for (j = 0 ; j <= fnpiv ; j++) + { + Fd [j * nb] = Fs [j * fnr_curr] ; + Fs [j * fnr_curr] = Fe [j * fnr_curr] ; + } + } + else + { + Int j ; + Entry *Fd, *Fs, *Fe ; + Fd = Flublock + fnpiv ; + Fs = Flblock + fspos ; + Fe = Flblock + fnrows ; +#pragma ivdep + for (j = 0 ; j < fnpiv ; j++) + { + ASSERT (IS_ZERO (Fs [j * fnr_curr])) ; + CLEAR (Fd [j * nb]) ; + Fs [j * fnr_curr] = Fe [j * fnr_curr] ; + } + Fd [fnpiv * nb] = Fs [fnpiv * fnr_curr] ; + Fs [fnpiv * fnr_curr] = Fe [fnpiv * fnr_curr] ; + } + + /* move row Fe to Fs in the Frows pattern */ + row2 = Frows [fnrows] ; + Frows [fspos] = row2 ; + Frpos [row2] = fspos ; + + } + /* pivot row is no longer in the frontal matrix */ + Frpos [pivrow] = EMPTY ; + +#ifndef NDEBUG + DEBUG2 (("\nFrontal matrix after row swap, including all space:\n" + "fnr_curr "ID" fnc_curr "ID" nb "ID"\n" + "fnrows "ID" fncols "ID" fnpiv "ID"\n", + Work->fnr_curr, Work->fnc_curr, Work->nb, + Work->fnrows, Work->fncols, Work->fnpiv)) ; + DEBUG2 (("\nJust the active part:\n")) ; + DEBUG7 (("C block: ")) ; + UMF_dump_dense (Fcblock, fnr_curr, fnrows, fncols) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (Flblock, fnr_curr, fnrows, fnpiv+1); + DEBUG7 (("U' block: ")) ; + UMF_dump_dense (Fublock, fnc_curr, fncols, fnpiv+1) ; + DEBUG7 (("LU block: ")) ; + UMF_dump_dense (Flublock, nb, fnpiv+1, fnpiv+1) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* Frpos [row] >= 0 for each row in pivot column pattern. */ + /* offset into pattern is given by: */ + /* Frpos [row] == offset - 1 */ + /* Frpos [pivrow] is EMPTY */ + + /* Fcpos [col] >= 0 for each col in pivot row pattern. */ + /* Fcpos [col] == (offset - 1) * fnr_curr */ + /* Fcpos [pivcol] is EMPTY */ + + /* Fcols [0..fncols-1] is the pivot row pattern (excl pivot cols) */ + /* Frows [0..fnrows-1] is the pivot col pattern (excl pivot rows) */ + + /* ====================================================================== */ + /* === scale pivot column =============================================== */ + /* ====================================================================== */ + + /* pivot column (except for pivot entry itself) */ + Fcol = Flblock + fnpiv * fnr_curr ; + /* fnpiv-th pivot in frontal matrix located in Flublock (fnpiv, fnpiv) */ + pivot_value = Flublock [fnpiv + fnpiv * nb] ; + + /* this is the kth global pivot */ + k = Work->npiv + fnpiv ; + + DEBUG4 (("Pivot value: ")) ; + EDEBUG4 (pivot_value) ; + DEBUG4 (("\n")) ; + + UMF_scale (fnrows, pivot_value, Fcol) ; + + /* ---------------------------------------------------------------------- */ + /* deallocate the pivot row and pivot column tuples */ + /* ---------------------------------------------------------------------- */ + + UMF_mem_free_tail_block (Numeric, Row_tuples [pivrow]) ; + UMF_mem_free_tail_block (Numeric, Col_tuples [pivcol]) ; + + Row_tuples [pivrow] = 0 ; + Col_tuples [pivcol] = 0 ; + + DEBUG5 (("number of pivots prior to this one: "ID"\n", k)) ; + ASSERT (NON_PIVOTAL_ROW (pivrow)) ; + ASSERT (NON_PIVOTAL_COL (pivcol)) ; + + /* save row and column inverse permutation */ + k1 = ONES_COMPLEMENT (k) ; + Rperm [pivrow] = k1 ; /* aliased with Row_degree */ + Cperm [pivcol] = k1 ; /* aliased with Col_degree */ + + ASSERT (!NON_PIVOTAL_ROW (pivrow)) ; + ASSERT (!NON_PIVOTAL_COL (pivcol)) ; + + /* ---------------------------------------------------------------------- */ + /* Keep track of the pivot order. This is the kth pivot row and column. */ + /* ---------------------------------------------------------------------- */ + + /* keep track of pivot rows and columns in the LU, L, and U blocks */ + ASSERT (fnpiv < MAXNB) ; + Work->Pivrow [fnpiv] = pivrow ; + Work->Pivcol [fnpiv] = pivcol ; + + /* ====================================================================== */ + /* === one step in the factorization is done ============================ */ + /* ====================================================================== */ + + /* One more step is done, except for pending updates to the U and C blocks + * of this frontal matrix. Those are saved up, and applied by + * UMF_blas3_update when enough pivots have accumulated. Also, the + * LU factors for these pending pivots have not yet been stored. */ + + Work->fnpiv++ ; + +#ifndef NDEBUG + DEBUG7 (("Current frontal matrix: (after pivcol scale)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale_column.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale_column.h new file mode 100644 index 0000000000..fe383635ed --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_scale_column.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_scale_column +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_set_stats.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_set_stats.c new file mode 100644 index 0000000000..63e51a7bb9 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_set_stats.c @@ -0,0 +1,134 @@ +/* ========================================================================== */ +/* === UMF_set_stats ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Sets statistics in Info array. Calculates everything in double precision, + rather than Int or size_t, so that usage estimates can be computed even if + the problem is so large that it would cause integer overflow. + + This routine has many double relop's, but the NaN case is ignored. +*/ + +#include "umf_internal.h" +#include "umf_symbolic_usage.h" + +GLOBAL void UMF_set_stats +( + double Info [ ], + SymbolicType *Symbolic, + double max_usage, /* peak size of Numeric->Memory, in Units */ + double num_mem_size, /* final size of Numeric->Memory, in Units */ + double flops, /* "true flops" */ + double lnz, /* nz in L */ + double unz, /* nz in U */ + double maxfrsize, /* largest front size */ + double ulen, /* size of Numeric->Upattern */ + double npiv, /* number of pivots found */ + double maxnrows, /* largest #rows in front */ + double maxncols, /* largest #cols in front */ + Int scale, /* true if scaling the rows of A */ + Int prefer_diagonal, /* true if diagonal pivoting (only square A) */ + Int what /* ESTIMATE or ACTUAL */ +) +{ + + double sym_size, work_usage, nn, n_row, n_col, n_inner, num_On_size1, + num_On_size2, num_usage, sym_maxncols, sym_maxnrows, elen, n1 ; + + n_col = Symbolic->n_col ; + n_row = Symbolic->n_row ; + n1 = Symbolic->n1 ; + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + sym_maxncols = MIN (Symbolic->maxncols + Symbolic->nb, n_col) ; + sym_maxnrows = MIN (Symbolic->maxnrows + Symbolic->nb, n_row) ; + elen = (n_col - n1) + (n_row - n1) + MIN (n_col - n1, n_row - n1) + 1 ; + + /* final Symbolic object size */ + sym_size = UMF_symbolic_usage (Symbolic->n_row, Symbolic->n_col, + Symbolic->nchains, Symbolic->nfr, Symbolic->esize, prefer_diagonal) ; + + /* size of O(n) part of Numeric object during factorization, */ + /* except Numeric->Memory and Numeric->Upattern */ + num_On_size1 = + DUNITS (NumericType, 1) /* Numeric structure */ + + DUNITS (Entry, n_inner+1) /* D */ + + 4 * DUNITS (Int, n_row+1) /* Rperm, Lpos, Uilen, Uip */ + + 4 * DUNITS (Int, n_col+1) /* Cperm, Upos, Lilen, Lip */ + + (scale ? DUNITS (Entry, n_row) : 0) ; /* Rs, row scale factors */ + + /* size of O(n) part of Numeric object after factorization, */ + /* except Numeric->Memory and Numeric->Upattern */ + num_On_size2 = + DUNITS (NumericType, 1) /* Numeric structure */ + + DUNITS (Entry, n_inner+1) /* D */ + + DUNITS (Int, n_row+1) /* Rperm */ + + DUNITS (Int, n_col+1) /* Cperm */ + + 6 * DUNITS (Int, npiv+1) /* Lpos, Uilen, Uip, Upos, Lilen, Lip */ + + (scale ? DUNITS (Entry, n_row) : 0) ; /* Rs, row scale factors */ + + DEBUG1 (("num O(n) size2: %g\n", num_On_size2)) ; + + /* peak size of Numeric->Memory, including LU factors, current frontal + * matrix, elements, and tuple lists. */ + Info [UMFPACK_VARIABLE_PEAK + what] = max_usage ; + + /* final size of Numeric->Memory (LU factors only) */ + Info [UMFPACK_VARIABLE_FINAL + what] = num_mem_size ; + + /* final size of Numeric object, including Numeric->Memory and ->Upattern */ + Info [UMFPACK_NUMERIC_SIZE + what] = + num_On_size2 + + num_mem_size /* final Numeric->Memory size */ + + DUNITS (Int, ulen+1) ;/* Numeric->Upattern (from Work->Upattern) */ + + DEBUG1 (("num mem size: %g\n", num_mem_size)) ; + DEBUG1 (("ulen units %g\n", DUNITS (Int, ulen))) ; + DEBUG1 (("numeric size %g\n", Info [UMFPACK_NUMERIC_SIZE + what])) ; + + /* largest front size (working array size, or actual size used) */ + Info [UMFPACK_MAX_FRONT_SIZE + what] = maxfrsize ; + Info [UMFPACK_MAX_FRONT_NROWS + what] = maxnrows ; + Info [UMFPACK_MAX_FRONT_NCOLS + what] = maxncols ; + DEBUGm4 (("maxnrows %g maxncols %g\n", maxnrows, maxncols)) ; + DEBUGm4 (("maxfrsize %g\n", maxfrsize)) ; + + /* UMF_kernel usage, from work_alloc routine in umf_kernel.c */ + work_usage = + /* Work-> arrays, except for current frontal matrix which is allocated + * inside Numeric->Memory. */ + 2 * DUNITS (Entry, sym_maxnrows + 1) /* Wx, Wy */ + + 2 * DUNITS (Int, n_row+1) /* Frpos, Lpattern */ + + 2 * DUNITS (Int, n_col+1) /* Fcpos, Upattern */ + + DUNITS (Int, nn + 1) /* Wp */ + + DUNITS (Int, MAX (n_col, sym_maxnrows) + 1) /* Wrp */ + + 2 * DUNITS (Int, sym_maxnrows + 1) /* Frows, Wm */ + + 3 * DUNITS (Int, sym_maxncols + 1) /* Fcols, Wio, Woi */ + + DUNITS (Int, MAX (sym_maxnrows, sym_maxncols) + 1) /* Woo */ + + DUNITS (Int, elen) /* E */ + + DUNITS (Int, Symbolic->nfr + 1) /* Front_new1strow */ + + ((n_row == n_col) ? (2 * DUNITS (Int, nn)) : 0) ; /* Diag map,imap */ + + /* Peak memory for just UMFPACK_numeric. */ + num_usage = + sym_size /* size of Symbolic object */ + + num_On_size1 /* O(n) part of Numeric object (excl. Upattern) */ + + work_usage /* Work-> arrays (including Upattern) */ + + max_usage ; /* peak size of Numeric->Memory */ + + /* peak memory usage for both UMFPACK_*symbolic and UMFPACK_numeric. */ + Info [UMFPACK_PEAK_MEMORY + what] = + MAX (Symbolic->peak_sym_usage, num_usage) ; + + Info [UMFPACK_FLOPS + what] = flops ; + Info [UMFPACK_LNZ + what] = lnz ; + Info [UMFPACK_UNZ + what] = unz ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_set_stats.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_set_stats.h new file mode 100644 index 0000000000..5b0a6c4cd2 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_set_stats.h @@ -0,0 +1,25 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_set_stats +( + double Info [ ], + SymbolicType *Symbolic, + double max_usage, + double num_mem_size, + double flops, + double lnz, + double unz, + double maxfrsize, + double ulen, + double npiv, + double maxnrows, + double maxncols, + Int scale, + Int prefer_diagonal, + Int what +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_singletons.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_singletons.c new file mode 100644 index 0000000000..841edeed03 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_singletons.c @@ -0,0 +1,891 @@ +/* ========================================================================== */ +/* === UMF_singletons ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Find and order the row and column singletons of a matrix A. If there are + * row and column singletons, the output is a row and column permutation such + * that the matrix is in the following form: + * + * x x x x x x x x x + * 0 x x x x x x x x + * 0 0 x x x x x x x + * 0 0 0 x 0 0 0 0 0 + * 0 0 0 x x 0 0 0 0 + * 0 0 0 x x s s s s + * 0 0 0 x x s s s s + * 0 0 0 x x s s s s + * 0 0 0 x x s s s s + * + * The above example has 3 column singletons (the first three columns and + * their corresponding pivot rows) and 2 row singletons. The singletons are + * ordered first, because they have zero Markowitz cost. The LU factorization + * for these first five rows and columns is free - there is no work to do + * (except to scale the pivot columns for the 2 row singletons), and no + * fill-in occurs. * The remaining * submatrix (4-by-4 in the above example) + * has no rows or columns with degree one. It may have empty rows or columns. + * + * This algorithm does not perform a full permutation to block triangular + * form. If there are one or more singletons, then the matrix can be + * permuted to block triangular form, but UMFPACK does not perform the full + * BTF permutation (see also "dmperm" in MATLAB). + */ + +#include "umf_internal.h" + +#ifndef NDEBUG + +/* ========================================================================== */ +/* === debug routines ======================================================= */ +/* ========================================================================== */ + +/* Dump the singleton queue */ + +PRIVATE void dump_singletons +( + Int head, /* head of the queue */ + Int tail, /* tail of the queue */ + Int Next [ ], /* Next [i] is the next object after i */ + char *name, /* "row" or "col" */ + Int Deg [ ], /* Deg [i] is the degree of object i */ + Int n /* objects are in the range 0 to n-1 */ +) +{ + Int i, next, cnt ; + DEBUG6 (("%s Singleton list: head "ID" tail "ID"\n", name, head, tail)) ; + i = head ; + ASSERT (head >= EMPTY && head < n) ; + ASSERT (tail >= EMPTY && tail < n) ; + cnt = 0 ; + while (i != EMPTY) + { + DEBUG7 ((" "ID": "ID" deg: "ID"\n", cnt, i, Deg [i])) ; + ASSERT (i >= 0 && i < n) ; + next = Next [i] ; + if (i == tail) ASSERT (next == EMPTY) ; + i = next ; + cnt++ ; + ASSERT (cnt <= n) ; + } +} + +PRIVATE void dump_mat +( + char *xname, + char *yname, + Int nx, + Int ny, + const Int Xp [ ], + const Int Xi [ ], + Int Xdeg [ ], + Int Ydeg [ ] +) +{ + Int x, y, p, p1, p2, xdeg, do_xdeg, ydeg ; + DEBUG6 (("\n ==== Dump %s mat:\n", xname)) ; + for (x = 0 ; x < nx ; x++) + { + p1 = Xp [x] ; + p2 = Xp [x+1] ; + xdeg = Xdeg [x] ; + DEBUG6 (("Dump %s "ID" p1 "ID" p2 "ID" deg "ID"\n", + xname, x, p1, p2, xdeg)) ; + do_xdeg = (xdeg >= 0) ; + for (p = p1 ; p < p2 ; p++) + { + y = Xi [p] ; + DEBUG7 ((" %s "ID" deg: ", yname, y)) ; + ASSERT (y >= 0 && y < ny) ; + ydeg = Ydeg [y] ; + DEBUG7 ((ID"\n", ydeg)) ; + if (do_xdeg && ydeg >= 0) + { + xdeg-- ; + } + } + ASSERT (IMPLIES (do_xdeg, xdeg == 0)) ; + } +} +#endif + +/* ========================================================================== */ +/* === create_row_form ====================================================== */ +/* ========================================================================== */ + +/* Create the row-form R of the column-form input matrix A. This could be done + * by UMF_transpose, except that Rdeg has already been computed. + */ + +PRIVATE void create_row_form +( + /* input, not modified: */ + Int n_row, /* A is n_row-by-n_col, nz = Ap [n_col] */ + Int n_col, + const Int Ap [ ], /* Ap [0..n_col]: column pointers for A */ + const Int Ai [ ], /* Ai [0..nz-1]: row indices for A */ + Int Rdeg [ ], /* Rdeg [0..n_row-1]: row degrees */ + + /* output, not defined on input: */ + Int Rp [ ], /* Rp [0..n_row]: row pointers for R */ + Int Ri [ ], /* Ri [0..nz-1]: column indices for R */ + + /* workspace, not defined on input or output */ + Int W [ ] /* size n_row */ +) +{ + Int row, col, p, p2 ; + + /* create the row pointers */ + Rp [0] = 0 ; + W [0] = 0 ; + for (row = 0 ; row < n_row ; row++) + { + Rp [row+1] = Rp [row] + Rdeg [row] ; + W [row] = Rp [row] ; + } + + /* create the indices for the row-form */ + for (col = 0 ; col < n_col ; col++) + { + p2 = Ap [col+1] ; + for (p = Ap [col] ; p < p2 ; p++) + { + Ri [W [Ai [p]]++] = col ; + } + } +} + +/* ========================================================================== */ +/* === order_singletons ===================================================== */ +/* ========================================================================== */ + +PRIVATE int order_singletons /* return new number of singletons */ +( + Int k, /* the number of singletons so far */ + Int head, + Int tail, + Int Next [ ], + Int Xdeg [ ], Int Xperm [ ], const Int Xp [ ], const Int Xi [ ], + Int Ydeg [ ], Int Yperm [ ], const Int Yp [ ], const Int Yi [ ] +#ifndef NDEBUG + , char *xname, char *yname, Int nx, Int ny +#endif +) +{ + Int xpivot, x, y, ypivot, p, p2, deg ; + +#ifndef NDEBUG + Int i, k1 = k ; + dump_singletons (head, tail, Next, xname, Xdeg, nx) ; + dump_mat (xname, yname, nx, ny, Xp, Xi, Xdeg, Ydeg) ; + dump_mat (yname, xname, ny, nx, Yp, Yi, Ydeg, Xdeg) ; +#endif + + while (head != EMPTY) + { + /* remove the singleton at the head of the queue */ + xpivot = head ; + DEBUG1 (("------ Order %s singleton: "ID"\n", xname, xpivot)) ; + head = Next [xpivot] ; + if (head == EMPTY) tail = EMPTY ; + +#ifndef NDEBUG + if (k % 100 == 0) dump_singletons (head, tail, Next, xname, Xdeg, nx) ; +#endif + + ASSERT (Xdeg [xpivot] >= 0) ; + if (Xdeg [xpivot] != 1) + { + /* This row/column x is empty. The matrix is singular. + * x will be ordered last in Xperm. */ + DEBUG1 (("empty %s, after singletons removed\n", xname)) ; + continue ; + } + + /* find the ypivot to match with this xpivot */ +#ifndef NDEBUG + /* there can only be one ypivot, since the degree of x is 1 */ + deg = 0 ; + p2 = Xp [xpivot+1] ; + for (p = Xp [xpivot] ; p < p2 ; p++) + { + y = Xi [p] ; + DEBUG1 (("%s: "ID"\n", yname, y)) ; + if (Ydeg [y] >= 0) + { + /* this is a live index in this xpivot vector */ + deg++ ; + } + } + ASSERT (deg == 1) ; +#endif + + ypivot = EMPTY ; + p2 = Xp [xpivot+1] ; + for (p = Xp [xpivot] ; p < p2 ; p++) + { + y = Xi [p] ; + DEBUG1 (("%s: "ID"\n", yname, y)) ; + if (Ydeg [y] >= 0) + { + /* this is a live index in this xpivot vector */ + ypivot = y ; + break ; + } + } + + DEBUG1 (("Pivot %s: "ID"\n", yname, ypivot)) ; + ASSERT (ypivot != EMPTY) ; + DEBUG1 (("deg "ID"\n", Ydeg [ypivot])) ; + ASSERT (Ydeg [ypivot] >= 0) ; + + /* decrement the degrees after removing this singleton */ + DEBUG1 (("p1 "ID"\n", Yp [ypivot])) ; + DEBUG1 (("p2 "ID"\n", Yp [ypivot+1])) ; + p2 = Yp [ypivot+1] ; + for (p = Yp [ypivot] ; p < p2 ; p++) + { + x = Yi [p] ; + DEBUG1 ((" %s: "ID" deg: "ID"\n", xname, x, Xdeg [x])) ; + if (Xdeg [x] < 0) continue ; + ASSERT (Xdeg [x] > 0) ; + if (x == xpivot) continue ; + deg = --(Xdeg [x]) ; + ASSERT (Xdeg [x] >= 0) ; + if (deg == 1) + { + /* this is a new singleton, put at the end of the queue */ + Next [x] = EMPTY ; + if (head == EMPTY) + { + head = x ; + } + else + { + ASSERT (tail != EMPTY) ; + Next [tail] = x ; + } + tail = x ; + DEBUG1 ((" New %s singleton: "ID"\n", xname, x)) ; +#ifndef NDEBUG + if (k % 100 == 0) + { + dump_singletons (head, tail, Next, xname, Xdeg, nx) ; + } +#endif + } + } + + /* flag the xpivot and ypivot by FLIP'ing the degrees */ + Xdeg [xpivot] = FLIP (1) ; + Ydeg [ypivot] = FLIP (Ydeg [ypivot]) ; + + /* keep track of the pivot row and column */ + Xperm [k] = xpivot ; + Yperm [k] = ypivot ; + k++ ; + +#ifndef NDEBUG + if (k % 1000 == 0) + { + dump_mat (xname, yname, nx, ny, Xp, Xi, Xdeg, Ydeg) ; + dump_mat (yname, xname, ny, nx, Yp, Yi, Ydeg, Xdeg) ; + } +#endif + } + +#ifndef NDEBUG + DEBUGm4 (("%s singletons: k = "ID"\n", xname, k)) ; + for (i = k1 ; i < k ; i++) + { + DEBUG1 ((" %s: "ID" %s: "ID"\n", xname, Xperm [i], yname, Yperm [i])) ; + } + ASSERT (k > 0) ; +#endif + + return (k) ; +} + +/* ========================================================================== */ +/* === find_any_singletons ================================================== */ +/* ========================================================================== */ + +PRIVATE Int find_any_singletons /* returns # of singletons found */ +( + /* input, not modified: */ + Int n_row, + Int n_col, + const Int Ap [ ], /* size n_col+1 */ + const Int Ai [ ], /* size nz = Ap [n_col] */ + + /* input, modified on output: */ + Int Cdeg [ ], /* size n_col */ + Int Rdeg [ ], /* size n_row */ + + /* output, not defined on input: */ + Int Cperm [ ], /* size n_col */ + Int Rperm [ ], /* size n_row */ + Int *p_n1r, /* # of row singletons */ + Int *p_n1c, /* # of col singletons */ + + /* workspace, not defined on input or output */ + Int Rp [ ], /* size n_row+1 */ + Int Ri [ ], /* size nz */ + Int W [ ], /* size n_row */ + Int Next [ ] /* size MAX (n_row, n_col) */ +) +{ + Int n1, col, row, row_form, head, tail, n1r, n1c ; + + /* ---------------------------------------------------------------------- */ + /* eliminate column singletons */ + /* ---------------------------------------------------------------------- */ + + n1 = 0 ; + n1r = 0 ; + n1c = 0 ; + row_form = FALSE ; + + head = EMPTY ; + tail = EMPTY ; + for (col = n_col-1 ; col >= 0 ; col--) + { + if (Cdeg [col] == 1) + { + /* put the column singleton in the queue */ + if (head == EMPTY) tail = col ; + Next [col] = head ; + head = col ; + DEBUG1 (("Column singleton: "ID"\n", col)) ; + } + } + + if (head != EMPTY) + { + + /* ------------------------------------------------------------------ */ + /* create the row-form of A */ + /* ------------------------------------------------------------------ */ + + create_row_form (n_row, n_col, Ap, Ai, Rdeg, Rp, Ri, W) ; + row_form = TRUE ; + + /* ------------------------------------------------------------------ */ + /* find and order the column singletons */ + /* ------------------------------------------------------------------ */ + + n1 = order_singletons (0, head, tail, Next, + Cdeg, Cperm, Ap, Ai, + Rdeg, Rperm, Rp, Ri +#ifndef NDEBUG + , "col", "row", n_col, n_row +#endif + ) ; + n1c = n1 ; + } + + /* ---------------------------------------------------------------------- */ + /* eliminate row singletons */ + /* ---------------------------------------------------------------------- */ + + head = EMPTY ; + tail = EMPTY ; + for (row = n_row-1 ; row >= 0 ; row--) + { + if (Rdeg [row] == 1) + { + /* put the row singleton in the queue */ + if (head == EMPTY) tail = row ; + Next [row] = head ; + head = row ; + DEBUG1 (("Row singleton: "ID"\n", row)) ; + } + } + + if (head != EMPTY) + { + + /* ------------------------------------------------------------------ */ + /* create the row-form of A, if not already created */ + /* ------------------------------------------------------------------ */ + + if (!row_form) + { + create_row_form (n_row, n_col, Ap, Ai, Rdeg, Rp, Ri, W) ; + } + + /* ------------------------------------------------------------------ */ + /* find and order the row singletons */ + /* ------------------------------------------------------------------ */ + + n1 = order_singletons (n1, head, tail, Next, + Rdeg, Rperm, Rp, Ri, + Cdeg, Cperm, Ap, Ai +#ifndef NDEBUG + , "row", "col", n_row, n_col +#endif + ) ; + n1r = n1 - n1c ; + } + + DEBUG0 (("n1 "ID"\n", n1)) ; + *p_n1r = n1r ; + *p_n1c = n1c ; + return (n1) ; +} + +/* ========================================================================== */ +/* === find_user_singletons ================================================= */ +/* ========================================================================== */ + +PRIVATE Int find_user_singletons /* returns # singletons found */ +( + /* input, not modified: */ + Int n_row, + Int n_col, + const Int Ap [ ], /* size n_col+1 */ + const Int Ai [ ], /* size nz = Ap [n_col] */ + const Int Quser [ ], /* size n_col if present */ + + /* input, modified on output: */ + Int Cdeg [ ], /* size n_col */ + Int Rdeg [ ], /* size n_row */ + + /* output, not defined on input */ + Int Cperm [ ], /* size n_col */ + Int Rperm [ ], /* size n_row */ + Int *p_n1r, /* # of row singletons */ + Int *p_n1c, /* # of col singletons */ + + /* workspace, not defined on input or output */ + Int Rp [ ], /* size n_row+1 */ + Int Ri [ ], /* size nz */ + Int W [ ] /* size n_row */ +) +{ + Int n1, col, row, p, p2, pivcol, pivrow, found, k, n1r, n1c ; + + n1 = 0 ; + n1r = 0 ; + n1c = 0 ; + *p_n1r = 0 ; + *p_n1c = 0 ; + + /* find singletons in the user column permutation, Quser */ + pivcol = Quser [0] ; + found = (Cdeg [pivcol] == 1) ; + DEBUG0 (("Is first col: "ID" a col singleton?: "ID"\n", pivcol, found)) ; + if (!found) + { + /* the first column is not a column singleton, check for a row + * singleton in the first column. */ + for (p = Ap [pivcol] ; p < Ap [pivcol+1] ; p++) + { + if (Rdeg [Ai [p]] == 1) + { + DEBUG0 (("Row singleton in first col: "ID" row: "ID"\n", + pivcol, Ai [p])) ; + found = TRUE ; + break ; + } + } + } + + if (!found) + { + /* no singletons in the leading part of A (:,Quser) */ + return (0) ; + } + + /* there is at least one row or column singleton. Look for more. */ + create_row_form (n_row, n_col, Ap, Ai, Rdeg, Rp, Ri, W) ; + + n1 = 0 ; + + for (k = 0 ; k < n_col ; k++) + { + pivcol = Quser [k] ; + pivrow = EMPTY ; + + /* ------------------------------------------------------------------ */ + /* check if col is a column singleton, or contains a row singleton */ + /* ------------------------------------------------------------------ */ + + found = (Cdeg [pivcol] == 1) ; + + if (found) + { + + /* -------------------------------------------------------------- */ + /* pivcol is a column singleton */ + /* -------------------------------------------------------------- */ + + DEBUG0 (("Found a col singleton: k "ID" pivcol "ID"\n", k, pivcol)); + + /* find the pivrow to match with this pivcol */ +#ifndef NDEBUG + /* there can only be one pivrow, since the degree of pivcol is 1 */ + { + Int deg = 0 ; + p2 = Ap [pivcol+1] ; + for (p = Ap [pivcol] ; p < p2 ; p++) + { + row = Ai [p] ; + DEBUG1 (("row: "ID"\n", row)) ; + if (Rdeg [row] >= 0) + { + /* this is a live index in this column vector */ + deg++ ; + } + } + ASSERT (deg == 1) ; + } +#endif + + p2 = Ap [pivcol+1] ; + for (p = Ap [pivcol] ; p < p2 ; p++) + { + row = Ai [p] ; + DEBUG1 (("row: "ID"\n", row)) ; + if (Rdeg [row] >= 0) + { + /* this is a live index in this pivcol vector */ + pivrow = row ; + break ; + } + } + + DEBUG1 (("Pivot row: "ID"\n", pivrow)) ; + ASSERT (pivrow != EMPTY) ; + DEBUG1 (("deg "ID"\n", Rdeg [pivrow])) ; + ASSERT (Rdeg [pivrow] >= 0) ; + + /* decrement the degrees after removing this col singleton */ + DEBUG1 (("p1 "ID"\n", Rp [pivrow])) ; + DEBUG1 (("p2 "ID"\n", Rp [pivrow+1])) ; + p2 = Rp [pivrow+1] ; + for (p = Rp [pivrow] ; p < p2 ; p++) + { + col = Ri [p] ; + DEBUG1 ((" col: "ID" deg: "ID"\n", col, Cdeg [col])) ; + if (Cdeg [col] < 0) continue ; + ASSERT (Cdeg [col] > 0) ; + Cdeg [col]-- ; + ASSERT (Cdeg [col] >= 0) ; + } + + /* flag the pivcol and pivrow by FLIP'ing the degrees */ + Cdeg [pivcol] = FLIP (1) ; + Rdeg [pivrow] = FLIP (Rdeg [pivrow]) ; + n1c++ ; + + } + else + { + + /* -------------------------------------------------------------- */ + /* pivcol may contain a row singleton */ + /* -------------------------------------------------------------- */ + + p2 = Ap [pivcol+1] ; + for (p = Ap [pivcol] ; p < p2 ; p++) + { + pivrow = Ai [p] ; + if (Rdeg [pivrow] == 1) + { + DEBUG0 (("Row singleton in pivcol: "ID" row: "ID"\n", + pivcol, pivrow)) ; + found = TRUE ; + break ; + } + } + + if (!found) + { + DEBUG0 (("End of user singletons\n")) ; + break ; + } + +#ifndef NDEBUG + /* there can only be one pivrow, since the degree of pivcol is 1 */ + { + Int deg = 0 ; + p2 = Rp [pivrow+1] ; + for (p = Rp [pivrow] ; p < p2 ; p++) + { + col = Ri [p] ; + DEBUG1 (("col: "ID" cdeg::: "ID"\n", col, Cdeg [col])) ; + if (Cdeg [col] >= 0) + { + /* this is a live index in this column vector */ + ASSERT (col == pivcol) ; + deg++ ; + } + } + ASSERT (deg == 1) ; + } +#endif + + DEBUG1 (("Pivot row: "ID"\n", pivrow)) ; + DEBUG1 (("pivcol deg "ID"\n", Cdeg [pivcol])) ; + ASSERT (Cdeg [pivcol] > 1) ; + + /* decrement the degrees after removing this row singleton */ + DEBUG1 (("p1 "ID"\n", Ap [pivcol])) ; + DEBUG1 (("p2 "ID"\n", Ap [pivcol+1])) ; + p2 = Ap [pivcol+1] ; + for (p = Ap [pivcol] ; p < p2 ; p++) + { + row = Ai [p] ; + DEBUG1 ((" row: "ID" deg: "ID"\n", row, Rdeg [row])) ; + if (Rdeg [row] < 0) continue ; + ASSERT (Rdeg [row] > 0) ; + Rdeg [row]-- ; + ASSERT (Rdeg [row] >= 0) ; + } + + /* flag the pivcol and pivrow by FLIP'ing the degrees */ + Cdeg [pivcol] = FLIP (Cdeg [pivcol]) ; + Rdeg [pivrow] = FLIP (1) ; + n1r++ ; + } + + /* keep track of the pivot row and column */ + Cperm [k] = pivcol ; + Rperm [k] = pivrow ; + n1++ ; + +#ifndef NDEBUG + dump_mat ("col", "row", n_col, n_row, Ap, Ai, Cdeg, Rdeg) ; + dump_mat ("row", "col", n_row, n_col, Rp, Ri, Rdeg, Cdeg) ; +#endif + + } + + DEBUGm4 (("User singletons found: "ID"\n", n1)) ; + ASSERT (n1 > 0) ; + + *p_n1r = n1r ; + *p_n1c = n1c ; + return (n1) ; +} + +/* ========================================================================== */ +/* === finish_permutation =================================================== */ +/* ========================================================================== */ + +/* Complete the permutation for the pruned submatrix. The singletons are + * already ordered, but remove their flags. Place rows/columns that are empty + * in the pruned submatrix at the end of the output permutation. This can only + * occur if the matrix is singular. + */ + +PRIVATE Int finish_permutation +( + Int n1, + Int nx, + Int Xdeg [ ], + const Int Xuser [ ], + Int Xperm [ ], + Int *p_max_deg +) +{ + Int nempty, x, deg, s, max_deg, k ; + nempty = 0 ; + s = n1 ; + max_deg = 0 ; + DEBUG0 (("n1 "ID" nempty "ID"\n", n1, nempty)) ; + for (k = 0 ; k < nx ; k++) + { + x = (Xuser != (Int *) NULL) ? Xuser [k] : k ; + DEBUG0 (("finish perm k "ID" x "ID" nx "ID"\n", k, x, nx)) ; + deg = Xdeg [x] ; + if (deg == 0) + { + /* this row/col is empty in the pruned submatrix */ + ASSERT (s < nx - nempty) ; + nempty++ ; + Xperm [nx - nempty] = x ; + } + else if (deg > 0) + { + /* this row/col is nonempty in the pruned submatrix */ + ASSERT (s < nx - nempty) ; + Xperm [s++] = x ; + max_deg = MAX (max_deg, deg) ; + } + else + { + /* This is a singleton row/column - it is already ordered. + * Just clear the flag. */ + Xdeg [x] = FLIP (deg) ; + } + } + ASSERT (s == nx - nempty) ; + *p_max_deg = max_deg ; + return (nempty) ; +} + +/* ========================================================================== */ +/* === UMF_singletons ======================================================= */ +/* ========================================================================== */ + +GLOBAL Int UMF_singletons +( + + /* input, not modified: */ + Int n_row, + Int n_col, + const Int Ap [ ], /* size n_col+1 */ + const Int Ai [ ], /* size nz = Ap [n_col] */ + const Int Quser [ ], /* size n_col if present */ + + /* output, not defined on input: */ + Int Cdeg [ ], /* size n_col */ + Int Cperm [ ], /* size n_col */ + Int Rdeg [ ], /* size n_row */ + Int Rperm [ ], /* size n_row */ + Int InvRperm [ ], /* size n_row, the inverse of Rperm */ + Int *p_n1, /* # of col and row singletons */ + Int *p_n1c, /* # of col singletons */ + Int *p_n1r, /* # of row singletons */ + Int *p_nempty_col, /* # of empty columns in pruned submatrix */ + Int *p_nempty_row, /* # of empty columns in pruned submatrix */ + Int *p_is_sym, /* TRUE if pruned submatrix is square and has been + * symmetrically permuted by Cperm and Rperm */ + Int *p_max_rdeg, /* maximum Rdeg in pruned submatrix */ + + /* workspace, not defined on input or output */ + Int Rp [ ], /* size n_row+1 */ + Int Ri [ ], /* size nz */ + Int W [ ], /* size n_row */ + Int Next [ ] /* size MAX (n_row, n_col) */ +) +{ + Int n1, s, col, row, p, p1, p2, cdeg, last_row, is_sym, k, + nempty_row, nempty_col, max_cdeg, max_rdeg, n1c, n1r ; + + /* ---------------------------------------------------------------------- */ + /* initializations */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + UMF_dump_start ( ) ; + DEBUGm4 (("Starting umf_singletons\n")) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* scan the columns, check for errors and count row degrees */ + /* ---------------------------------------------------------------------- */ + + if (Ap [0] != 0 || Ap [n_col] < 0) + { + return (UMFPACK_ERROR_invalid_matrix) ; + } + for (row = 0 ; row < n_row ; row++) + { + Rdeg [row] = 0 ; + } + for (col = 0 ; col < n_col ; col++) + { + p1 = Ap [col] ; + p2 = Ap [col+1] ; + cdeg = p2 - p1 ; + if (cdeg < 0) + { + return (UMFPACK_ERROR_invalid_matrix) ; + } + last_row = EMPTY ; + for (p = p1 ; p < p2 ; p++) + { + row = Ai [p] ; + if (row <= last_row || row >= n_row) + { + return (UMFPACK_ERROR_invalid_matrix) ; + } + Rdeg [row]++ ; + last_row = row ; + } + Cdeg [col] = cdeg ; + } + + /* ---------------------------------------------------------------------- */ + /* find singletons */ + /* ---------------------------------------------------------------------- */ + + if (Quser != (Int *) NULL) + { + /* look for singletons, but respect the user's input permutation */ + n1 = find_user_singletons (n_row, n_col, Ap, Ai, Quser, + Cdeg, Rdeg, Cperm, Rperm, &n1r, &n1c, Rp, Ri, W) ; + } + else + { + /* look for singletons anywhere */ + n1 = find_any_singletons (n_row, n_col, Ap, Ai, + Cdeg, Rdeg, Cperm, Rperm, &n1r, &n1c, Rp, Ri, W, Next) ; + } + + /* ---------------------------------------------------------------------- */ + /* eliminate empty columns and complete the column permutation */ + /* ---------------------------------------------------------------------- */ + + nempty_col = finish_permutation (n1, n_col, Cdeg, Quser, Cperm, &max_cdeg) ; + + /* ---------------------------------------------------------------------- */ + /* eliminate empty rows and complete the row permutation */ + /* ---------------------------------------------------------------------- */ + + nempty_row = finish_permutation (n1, n_row, Rdeg, (Int *) NULL, Rperm, + &max_rdeg) ; + + /* ---------------------------------------------------------------------- */ + /* compute the inverse of Rperm */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < n_row ; k++) + { + ASSERT (Rperm [k] >= 0 && Rperm [k] < n_row) ; + InvRperm [Rperm [k]] = k ; + } + + /* ---------------------------------------------------------------------- */ + /* see if pruned submatrix is square and has been symmetrically permuted */ + /* ---------------------------------------------------------------------- */ + + if (n_row == n_col && nempty_row == nempty_col) + { + /* is_sym is true if the submatrix is square, and + * Rperm [n1..n_row-nempty_row-1] = Cperm [n1..n_col-nempty_col-1] */ + is_sym = TRUE ; + for (s = n1 ; s < n_col - nempty_col ; s++) + { + if (Cperm [s] != Rperm [s]) + { + is_sym = FALSE ; + break ; + } + } + } + else + { + is_sym = FALSE ; + } + DEBUGm4 (("Submatrix square and symmetrically permuted? "ID"\n", is_sym)) ; + DEBUGm4 (("singletons "ID" row "ID" col "ID"\n", n1, n1r, n1c)) ; + DEBUGm4 (("Empty cols "ID" rows "ID"\n", nempty_col, nempty_row)) ; + *p_n1 = n1 ; + *p_n1r = n1r ; + *p_n1c = n1c ; + *p_is_sym = is_sym ; + *p_nempty_col = nempty_col ; + *p_nempty_row = nempty_row ; + *p_max_rdeg = max_rdeg ; + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_singletons.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_singletons.h new file mode 100644 index 0000000000..460defa95c --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_singletons.h @@ -0,0 +1,31 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_singletons +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + const Int Quser [ ], + Int Cdeg [ ], + Int Cperm [ ], + Int Rdeg [ ], + Int Rperm [ ], + Int InvRperm [ ], + Int *n1, + Int *n1c, + Int *n1r, + Int *nempty_col, + Int *nempty_row, + Int *is_sym, + Int *max_rdeg, + Int Rp [ ], + Int Ri [ ], + Int W [ ], + Int Next [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_solve.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_solve.c new file mode 100644 index 0000000000..8450c40ee8 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_solve.c @@ -0,0 +1,1372 @@ +/* ========================================================================== */ +/* === UMF_solve ============================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Not user-callable. Solves a linear system using the numerical factorization + computed by UMFPACK_numeric. No workspace is dynamically allocated. Counts + flops, but excludes floating-point comparisons (thus real abs (...) are + zero flops, but complex abs (...) takes 6 flops). + + Returns UMFPACK_OK if successful, UMFPACK_ERROR_argument_missing if + required arguments are missing, UMFPACK_ERROR_invalid_system if the sys + string is not valid or if the matrix A is not square. + + Uses the sparse backward error method of Arioli, Demmel, and Duff + (Solving sparse linear systems with sparse backward error, SIAM J. Matrix + Analysis and Applic., vol 10, pp. 165-190). +*/ + +#include "umf_internal.h" +#include "umf_lsolve.h" +#include "umf_usolve.h" +#include "umf_ltsolve.h" +#include "umf_utsolve.h" +#include "umf_report_vector.h" + +PRIVATE Int do_step +( + double omega [3], + Int step, + const double B2 [ ], + Entry X [ ], + const Entry W [ ], + const double Y [ ], + const double Z2 [ ], + Entry S [ ], + Int n, + double Info [UMFPACK_INFO] +) ; + +/* ========================================================================== */ +/* === UMF_solve ============================================================ */ +/* ========================================================================== */ + +GLOBAL Int UMF_solve +( + Int sys, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], + double Xx [ ], + const double Bx [ ], +#ifdef COMPLEX + const double Az [ ], + double Xz [ ], + const double Bz [ ], +#endif + NumericType *Numeric, + Int irstep, + double Info [UMFPACK_INFO], + Int Pattern [ ], /* size n */ + double SolveWork [ ] /* if irstep>0 real: size 5*n. complex:10*n */ + /* otherwise real: size n. complex: 4*n */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int *Rperm, *Cperm, i, n, p, step, j, nz, status, p2, do_scale ; + Entry axx, wi, xj, zi, xi, aij, *W, *Z, *S, *X, bi ; + double omega [3], d, *Z2, *Y, z2i, yi, *B2, *Rs, flops ; + +#ifndef NRECIPROCAL + Int do_recip = Numeric->do_recip ; +#endif + + /* ---------------------------------------------------------------------- */ + /* initializations */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + UMF_dump_lu (Numeric) ; + ASSERT (Numeric && Xx && Bx && Pattern && SolveWork && Info) ; +#ifdef COMPLEX + ASSERT (Xz && Bz) ; +#endif +#endif + + nz = 0 ; + omega [0] = 0. ; + omega [1] = 0. ; + omega [2] = 0. ; + Rperm = Numeric->Rperm ; + Cperm = Numeric->Cperm ; + Rs = Numeric->Rs ; /* row scale factors */ + do_scale = (Rs != (double *) NULL) ; + flops = 0 ; + Info [UMFPACK_SOLVE_FLOPS] = 0 ; + Info [UMFPACK_IR_TAKEN] = 0 ; + Info [UMFPACK_IR_ATTEMPTED] = 0 ; + + /* UMFPACK_solve does not call this routine if A is rectangular */ + ASSERT (Numeric->n_row == Numeric->n_col) ; + n = Numeric->n_row ; + if (Numeric->nnzpiv < n + || SCALAR_IS_ZERO (Numeric->rcond) || SCALAR_IS_NAN (Numeric->rcond)) + { + /* Note that systems involving just L return UMFPACK_OK, even if */ + /* A is singular (L is always has a unit diagonal). */ + DEBUGm4 (("Note, matrix is singular in umf_solve\n")) ; + status = UMFPACK_WARNING_singular_matrix ; + irstep = 0 ; + } + else + { + status = UMFPACK_OK ; + } + irstep = MAX (0, irstep) ; /* make sure irstep is >= 0 */ + + W = (Entry *) SolveWork ; /* Entry W [0..n-1] */ + + Z = (Entry *) NULL ; /* unused if no iterative refinement */ + S = (Entry *) NULL ; + Y = (double *) NULL ; + Z2 = (double *) NULL ; + B2 = (double *) NULL ; + +#ifdef COMPLEX + X = (Entry *) (SolveWork + 2*n) ; /* Entry X [0..n-1] */ + if (irstep > 0) + { + if (!Ap || !Ai || !Ax || !Az) + { + return (UMFPACK_ERROR_argument_missing) ; + } + Z = (Entry *) (SolveWork + 4*n) ; /* Entry Z [0..n-1] */ + S = (Entry *) (SolveWork + 6*n) ; /* Entry S [0..n-1] */ + Y = (double *) (SolveWork + 8*n) ; /* double Y [0..n-1] */ + B2 = (double *) (SolveWork + 9*n) ; /* double B2 [0..n-1] */ + Z2 = (double *) Z ; /* double Z2 [0..n-1], equiv. to Z */ + } +#else + X = (Entry *) Xx ; /* Entry X [0..n-1] */ + if (irstep > 0) + { + if (!Ap || !Ai || !Ax) + { + return (UMFPACK_ERROR_argument_missing) ; + } + Z = (Entry *) (SolveWork + n) ; /* Entry Z [0..n-1] */ + S = (Entry *) (SolveWork + 2*n) ; /* Entry S [0..n-1] */ + Y = (double *) (SolveWork + 3*n) ; /* double Y [0..n-1] */ + B2 = (double *) (SolveWork + 4*n) ; /* double B2 [0..n-1] */ + Z2 = (double *) Z ; /* double Z2 [0..n-1], equiv. to Z */ + } +#endif + + /* ---------------------------------------------------------------------- */ + /* determine which system to solve */ + /* ---------------------------------------------------------------------- */ + + if (sys == UMFPACK_A) + { + + /* ------------------------------------------------------------------ */ + /* solve A x = b with optional iterative refinement */ + /* ------------------------------------------------------------------ */ + + if (irstep > 0) + { + + /* -------------------------------------------------------------- */ + /* using iterative refinement: compute Y and B2 */ + /* -------------------------------------------------------------- */ + + nz = Ap [n] ; + Info [UMFPACK_NZ] = nz ; + + /* A is stored by column */ + /* Y (i) = ||R A_i||, 1-norm of row i of R A */ + for (i = 0 ; i < n ; i++) + { + Y [i] = 0. ; + } + flops += (ABS_FLOPS + 1) * nz ; + p2 = Ap [n] ; + for (p = 0 ; p < p2 ; p++) + { + /* Y [Ai [p]] += ABS (Ax [p]) ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + Y [Ai [p]] += d ; + } + + /* B2 = abs (B) */ + flops += ABS_FLOPS * n ; + for (i = 0 ; i < n ; i++) + { + /* B2 [i] = ABS (B [i]) ; */ + ASSIGN (bi, Bx [i], Bz [i]) ; + ABS (B2 [i], bi) ; + } + + /* scale Y and B2. */ + if (do_scale) + { + /* Y = R Y */ + /* B2 = R B2 */ +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + Y [i] *= Rs [i] ; + B2 [i] *= Rs [i] ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + Y [i] /= Rs [i] ; + B2 [i] /= Rs [i] ; + } + } + + flops += 2 * n ; + } + + } + + for (step = 0 ; step <= irstep ; step++) + { + + /* -------------------------------------------------------------- */ + /* Solve A x = b (step 0): */ + /* x = Q (U \ (L \ (P R b))) */ + /* and then perform iterative refinement (step > 0): */ + /* x = x + Q (U \ (L \ (P R (b - A x)))) */ + /* -------------------------------------------------------------- */ + + if (step == 0) + { + if (do_scale) + { + /* W = P R b, using X as workspace, since Z is not + * allocated if irstep = 0. */ +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + ASSIGN (X [i], Bx [i], Bz [i]) ; + SCALE_RECIP (X [i], Rs [i]) ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + ASSIGN (X [i], Bx [i], Bz [i]) ; + SCALE_DIV (X [i], Rs [i]) ; + } + } + flops += SCALE_FLOPS * n ; + for (i = 0 ; i < n ; i++) + { + W [i] = X [Rperm [i]] ; + } + } + else + { + /* W = P b, since the row scaling R = I */ + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [Rperm [i]] ; */ + ASSIGN (W [i], Bx [Rperm [i]], Bz [Rperm [i]]) ; + } + } + } + else + { + for (i = 0 ; i < n ; i++) + { + /* Z [i] = B [i] ; */ + ASSIGN (Z [i], Bx [i], Bz [i]) ; + } + flops += MULTSUB_FLOPS * nz ; + for (i = 0 ; i < n ; i++) + { + xi = X [i] ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* Z [Ai [p]] -= Ax [p] * xi ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT_SUB (Z [Ai [p]], aij, xi) ; + } + } + /* scale, Z = R Z */ + if (do_scale) + { +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_RECIP (Z [i], Rs [i]) ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_DIV (Z [i], Rs [i]) ; + } + } + flops += SCALE_FLOPS * n ; + } + for (i = 0 ; i < n ; i++) + { + W [i] = Z [Rperm [i]] ; + } + } + + flops += UMF_lsolve (Numeric, W, Pattern) ; + flops += UMF_usolve (Numeric, W, Pattern) ; + + if (step == 0) + { + for (i = 0 ; i < n ; i++) + { + X [Cperm [i]] = W [i] ; + } + } + else + { + flops += ASSEMBLE_FLOPS * n ; + for (i = 0 ; i < n ; i++) + { + /* X [Cperm [i]] += W [i] ; */ + ASSEMBLE (X [Cperm [i]], W [i]) ; + } + } + + /* -------------------------------------------------------------- */ + /* sparse backward error estimate */ + /* -------------------------------------------------------------- */ + + if (irstep > 0) + { + + /* ---------------------------------------------------------- */ + /* A is stored by column */ + /* W (i) = R (b - A x)_i, residual */ + /* Z2 (i) = R (|A||x|)_i */ + /* ---------------------------------------------------------- */ + + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [i] ; */ + ASSIGN (W [i], Bx [i], Bz [i]) ; + Z2 [i] = 0. ; + } + flops += (MULT_FLOPS + DECREMENT_FLOPS + ABS_FLOPS + 1) * nz ; + for (j = 0 ; j < n ; j++) + { + xj = X [j] ; + p2 = Ap [j+1] ; + for (p = Ap [j] ; p < p2 ; p++) + { + i = Ai [p] ; + + /* axx = Ax [p] * xj ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT (axx, aij, xj) ; + + /* W [i] -= axx ; */ + DECREMENT (W [i], axx) ; + + /* Z2 [i] += ABS (axx) ; */ + ABS (d, axx) ; + Z2 [i] += d ; + } + } + + /* scale W and Z2 */ + if (do_scale) + { + /* Z2 = R Z2 */ + /* W = R W */ +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_RECIP (W [i], Rs [i]) ; + Z2 [i] *= Rs [i] ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_DIV (W [i], Rs [i]) ; + Z2 [i] /= Rs [i] ; + } + } + flops += (SCALE_FLOPS + 1) * n ; + } + + flops += (2*ABS_FLOPS + 5) * n ; + if (do_step (omega, step, B2, X, W, Y, Z2, S, n, Info)) + { + /* iterative refinement is done */ + break ; + } + + } + + } + + } + else if (sys == UMFPACK_At) + { + + /* ------------------------------------------------------------------ */ + /* solve A' x = b with optional iterative refinement */ + /* ------------------------------------------------------------------ */ + + /* A' is the complex conjugate transpose */ + + if (irstep > 0) + { + + /* -------------------------------------------------------------- */ + /* using iterative refinement: compute Y */ + /* -------------------------------------------------------------- */ + + nz = Ap [n] ; + Info [UMFPACK_NZ] = nz ; + + /* A' is stored by row */ + /* Y (i) = ||(A' R)_i||, 1-norm of row i of A' R */ + + if (do_scale) + { + flops += (ABS_FLOPS + 2) * nz ; +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + yi = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* yi += ABS (Ax [p]) * Rs [Ai [p]] ; */ + /* note that abs (aij) is the same as + * abs (conj (aij)) */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + yi += (d * Rs [Ai [p]]) ; + } + Y [i] = yi ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + yi = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* yi += ABS (Ax [p]) / Rs [Ai [p]] ; */ + /* note that abs (aij) is the same as + * abs (conj (aij)) */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + yi += (d / Rs [Ai [p]]) ; + } + Y [i] = yi ; + } + } + } + else + { + /* no scaling */ + flops += (ABS_FLOPS + 1) * nz ; + for (i = 0 ; i < n ; i++) + { + yi = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* yi += ABS (Ax [p]) ; */ + /* note that abs (aij) is the same as + * abs (conj (aij)) */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + yi += d ; + } + Y [i] = yi ; + } + } + + /* B2 = abs (B) */ + for (i = 0 ; i < n ; i++) + { + /* B2 [i] = ABS (B [i]) ; */ + ASSIGN (bi, Bx [i], Bz [i]) ; + ABS (B2 [i], bi) ; + } + + } + + for (step = 0 ; step <= irstep ; step++) + { + + /* -------------------------------------------------------------- */ + /* Solve A' x = b (step 0): */ + /* x = R P' (L' \ (U' \ (Q' b))) */ + /* and then perform iterative refinement (step > 0): */ + /* x = x + R P' (L' \ (U' \ (Q' (b - A' x)))) */ + /* -------------------------------------------------------------- */ + + if (step == 0) + { + /* W = Q' b */ + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [Cperm [i]] ; */ + ASSIGN (W [i], Bx [Cperm [i]], Bz [Cperm [i]]) ; + } + } + else + { + /* Z = b - A' x */ + for (i = 0 ; i < n ; i++) + { + /* Z [i] = B [i] ; */ + ASSIGN (Z [i], Bx [i], Bz [i]) ; + } + flops += MULTSUB_FLOPS * nz ; + for (i = 0 ; i < n ; i++) + { + zi = Z [i] ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* zi -= conjugate (Ax [p]) * X [Ai [p]] ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT_SUB_CONJ (zi, X [Ai [p]], aij) ; + } + Z [i] = zi ; + } + /* W = Q' Z */ + for (i = 0 ; i < n ; i++) + { + W [i] = Z [Cperm [i]] ; + } + } + + flops += UMF_uhsolve (Numeric, W, Pattern) ; + flops += UMF_lhsolve (Numeric, W, Pattern) ; + + if (step == 0) + { + + /* X = R P' W */ + /* do not use Z, since it isn't allocated if irstep = 0 */ + + /* X = P' W */ + for (i = 0 ; i < n ; i++) + { + X [Rperm [i]] = W [i] ; + } + if (do_scale) + { + /* X = R X */ +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_RECIP (X [i], Rs [i]) ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_DIV (X [i], Rs [i]) ; + } + } + flops += SCALE_FLOPS * n ; + } + + } + else + { + + /* Z = P' W */ + for (i = 0 ; i < n ; i++) + { + Z [Rperm [i]] = W [i] ; + } + if (do_scale) + { + /* Z = R Z */ +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_RECIP (Z [i], Rs [i]) ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_DIV (Z [i], Rs [i]) ; + } + } + flops += SCALE_FLOPS * n ; + } + + flops += ASSEMBLE_FLOPS * n ; + /* X += Z */ + for (i = 0 ; i < n ; i++) + { + /* X [i] += W [i] ; */ + ASSEMBLE (X [i], W [i]) ; + } + } + + /* -------------------------------------------------------------- */ + /* sparse backward error estimate */ + /* -------------------------------------------------------------- */ + + if (irstep > 0) + { + + /* ---------------------------------------------------------- */ + /* A' is stored by row */ + /* W (i) = (b - A' x)_i, residual */ + /* Z2 (i) = (|A'||x|)_i */ + /* ---------------------------------------------------------- */ + + flops += (MULT_FLOPS + DECREMENT_FLOPS + ABS_FLOPS + 1) * nz ; + for (i = 0 ; i < n ; i++) + { + /* wi = B [i] ; */ + ASSIGN (wi, Bx [i], Bz [i]) ; + z2i = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* axx = conjugate (Ax [p]) * X [Ai [p]] ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT_CONJ (axx, X [Ai [p]], aij) ; + + /* wi -= axx ; */ + DECREMENT (wi, axx) ; + + /* z2i += ABS (axx) ; */ + ABS (d, axx) ; + z2i += d ; + } + W [i] = wi ; + Z2 [i] = z2i ; + } + + flops += (2*ABS_FLOPS + 5) * n ; + if (do_step (omega, step, B2, X, W, Y, Z2, S, n, Info)) + { + /* iterative refinement is done */ + break ; + } + + } + + } + + } + else if (sys == UMFPACK_Aat) + { + + /* ------------------------------------------------------------------ */ + /* solve A.' x = b with optional iterative refinement */ + /* ------------------------------------------------------------------ */ + + /* A' is the array transpose */ + + if (irstep > 0) + { + + /* -------------------------------------------------------------- */ + /* using iterative refinement: compute Y */ + /* -------------------------------------------------------------- */ + + nz = Ap [n] ; + Info [UMFPACK_NZ] = nz ; + + /* A.' is stored by row */ + /* Y (i) = ||(A.' R)_i||, 1-norm of row i of A.' R */ + + if (do_scale) + { + flops += (ABS_FLOPS + 2) * nz ; +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + yi = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* yi += ABS (Ax [p]) * Rs [Ai [p]] ; */ + /* note that A.' is the array transpose, + * so no conjugate */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + yi += (d * Rs [Ai [p]]) ; + } + Y [i] = yi ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + yi = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* yi += ABS (Ax [p]) / Rs [Ai [p]] ; */ + /* note that A.' is the array transpose, + * so no conjugate */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + yi += (d / Rs [Ai [p]]) ; + } + Y [i] = yi ; + } + } + } + else + { + /* no scaling */ + flops += (ABS_FLOPS + 1) * nz ; + for (i = 0 ; i < n ; i++) + { + yi = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* yi += ABS (Ax [p]) */ + /* note that A.' is the array transpose, + * so no conjugate */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + yi += d ; + } + Y [i] = yi ; + } + } + + /* B2 = abs (B) */ + for (i = 0 ; i < n ; i++) + { + /* B2 [i] = ABS (B [i]) ; */ + ASSIGN (bi, Bx [i], Bz [i]) ; + ABS (B2 [i], bi) ; + } + + } + + for (step = 0 ; step <= irstep ; step++) + { + + /* -------------------------------------------------------------- */ + /* Solve A.' x = b (step 0): */ + /* x = R P' (L.' \ (U.' \ (Q' b))) */ + /* and then perform iterative refinement (step > 0): */ + /* x = x + R P' (L.' \ (U.' \ (Q' (b - A.' x)))) */ + /* -------------------------------------------------------------- */ + + if (step == 0) + { + /* W = Q' b */ + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [Cperm [i]] ; */ + ASSIGN (W [i], Bx [Cperm [i]], Bz [Cperm [i]]) ; + } + } + else + { + /* Z = b - A.' x */ + for (i = 0 ; i < n ; i++) + { + /* Z [i] = B [i] ; */ + ASSIGN (Z [i], Bx [i], Bz [i]) ; + } + flops += MULTSUB_FLOPS * nz ; + for (i = 0 ; i < n ; i++) + { + zi = Z [i] ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* zi -= Ax [p] * X [Ai [p]] ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT_SUB (zi, aij, X [Ai [p]]) ; + } + Z [i] = zi ; + } + /* W = Q' Z */ + for (i = 0 ; i < n ; i++) + { + W [i] = Z [Cperm [i]] ; + } + } + + flops += UMF_utsolve (Numeric, W, Pattern) ; + flops += UMF_ltsolve (Numeric, W, Pattern) ; + + if (step == 0) + { + + /* X = R P' W */ + /* do not use Z, since it isn't allocated if irstep = 0 */ + + /* X = P' W */ + for (i = 0 ; i < n ; i++) + { + X [Rperm [i]] = W [i] ; + } + if (do_scale) + { + /* X = R X */ +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_RECIP (X [i], Rs [i]) ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_DIV (X [i], Rs [i]) ; + } + } + flops += SCALE_FLOPS * n ; + } + + } + else + { + + /* Z = P' W */ + for (i = 0 ; i < n ; i++) + { + Z [Rperm [i]] = W [i] ; + } + if (do_scale) + { + /* Z = R Z */ +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_RECIP (Z [i], Rs [i]) ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + SCALE_DIV (Z [i], Rs [i]) ; + } + } + flops += SCALE_FLOPS * n ; + } + + flops += ASSEMBLE_FLOPS * n ; + /* X += Z */ + for (i = 0 ; i < n ; i++) + { + /* X [i] += W [i] ; */ + ASSEMBLE (X [i], W [i]) ; + } + } + + /* -------------------------------------------------------------- */ + /* sparse backward error estimate */ + /* -------------------------------------------------------------- */ + + if (irstep > 0) + { + + /* ---------------------------------------------------------- */ + /* A.' is stored by row */ + /* W (i) = (b - A.' x)_i, residual */ + /* Z (i) = (|A.'||x|)_i */ + /* ---------------------------------------------------------- */ + + flops += (MULT_FLOPS + DECREMENT_FLOPS + ABS_FLOPS + 1) * nz ; + for (i = 0 ; i < n ; i++) + { + /* wi = B [i] ; */ + ASSIGN (wi, Bx [i], Bz [i]) ; + z2i = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* axx = Ax [p] * X [Ai [p]] ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT (axx, aij, X [Ai [p]]) ; + + /* wi -= axx ; */ + DECREMENT (wi, axx) ; + + /* z2i += ABS (axx) ; */ + ABS (d, axx) ; + z2i += d ; + } + W [i] = wi ; + Z2 [i] = z2i ; + } + + flops += (2*ABS_FLOPS + 5) * n ; + if (do_step (omega, step, B2, X, W, Y, Z2, S, n, Info)) + { + /* iterative refinement is done */ + break ; + } + + } + + } + + } + else if (sys == UMFPACK_Pt_L) + { + + /* ------------------------------------------------------------------ */ + /* Solve P'Lx=b: x = L \ Pb */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [Rperm [i]] ; */ + ASSIGN (X [i], Bx [Rperm [i]], Bz [Rperm [i]]) ; + } + flops = UMF_lsolve (Numeric, X, Pattern) ; + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_L) + { + + /* ------------------------------------------------------------------ */ + /* Solve Lx=b: x = L \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + flops = UMF_lsolve (Numeric, X, Pattern) ; + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_Lt_P) + { + + /* ------------------------------------------------------------------ */ + /* Solve L'Px=b: x = P' (L' \ b) */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [i] ; */ + ASSIGN (W [i], Bx [i], Bz [i]) ; + } + flops = UMF_lhsolve (Numeric, W, Pattern) ; + for (i = 0 ; i < n ; i++) + { + X [Rperm [i]] = W [i] ; + } + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_Lat_P) + { + + /* ------------------------------------------------------------------ */ + /* Solve L.'Px=b: x = P' (L.' \ b) */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [i] ; */ + ASSIGN (W [i], Bx [i], Bz [i]) ; + } + flops = UMF_ltsolve (Numeric, W, Pattern) ; + for (i = 0 ; i < n ; i++) + { + X [Rperm [i]] = W [i] ; + } + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_Lt) + { + + /* ------------------------------------------------------------------ */ + /* Solve L'x=b: x = L' \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + flops = UMF_lhsolve (Numeric, X, Pattern) ; + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_Lat) + { + + /* ------------------------------------------------------------------ */ + /* Solve L.'x=b: x = L.' \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + flops = UMF_ltsolve (Numeric, X, Pattern) ; + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_U_Qt) + { + + /* ------------------------------------------------------------------ */ + /* Solve UQ'x=b: x = Q (U \ b) */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [i] ; */ + ASSIGN (W [i], Bx [i], Bz [i]) ; + } + flops = UMF_usolve (Numeric, W, Pattern) ; + for (i = 0 ; i < n ; i++) + { + X [Cperm [i]] = W [i] ; + } + + } + else if (sys == UMFPACK_U) + { + + /* ------------------------------------------------------------------ */ + /* Solve Ux=b: x = U \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + flops = UMF_usolve (Numeric, X, Pattern) ; + + } + else if (sys == UMFPACK_Q_Ut) + { + + /* ------------------------------------------------------------------ */ + /* Solve QU'x=b: x = U' \ Q'b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [Cperm [i]] ; */ + ASSIGN (X [i], Bx [Cperm [i]], Bz [Cperm [i]]) ; + } + flops = UMF_uhsolve (Numeric, X, Pattern) ; + + } + else if (sys == UMFPACK_Q_Uat) + { + + /* ------------------------------------------------------------------ */ + /* Solve QU.'x=b: x = U.' \ Q'b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [Cperm [i]] ; */ + ASSIGN (X [i], Bx [Cperm [i]], Bz [Cperm [i]]) ; + } + flops = UMF_utsolve (Numeric, X, Pattern) ; + + } + else if (sys == UMFPACK_Ut) + { + + /* ------------------------------------------------------------------ */ + /* Solve U'x=b: x = U' \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + flops = UMF_uhsolve (Numeric, X, Pattern) ; + + } + else if (sys == UMFPACK_Uat) + { + + /* ------------------------------------------------------------------ */ + /* Solve U'x=b: x = U' \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + flops = UMF_utsolve (Numeric, X, Pattern) ; + + } + else + { + return (UMFPACK_ERROR_invalid_system) ; + } + +#ifdef COMPLEX + /* copy the solution back, from Entry X [ ] to double Xx [ ] and Xz [ ] */ + for (i = 0 ; i < n ; i++) + { + Xx [i] = REAL_COMPONENT (X [i]) ; + Xz [i] = IMAG_COMPONENT (X [i]) ; + } +#endif + + /* return UMFPACK_OK, or UMFPACK_WARNING_singular_matrix */ + /* Note that systems involving just L will return UMFPACK_OK */ + Info [UMFPACK_SOLVE_FLOPS] = flops ; + return (status) ; +} + + +/* ========================================================================== */ +/* === do_step ============================================================== */ +/* ========================================================================== */ + +/* Perform one step of iterative refinement, for A x = b or A' x = b */ + +PRIVATE Int do_step /* return TRUE if iterative refinement done */ +( + double omega [3], + Int step, /* which step of iterative refinement to do */ + const double B2 [ ], /* abs (B) */ + Entry X [ ], + const Entry W [ ], + const double Y [ ], + const double Z2 [ ], + Entry S [ ], + Int n, + double Info [UMFPACK_INFO] +) +{ + double last_omega [3], tau, nctau, d1, wd1, d2, wd2, xi, yix, wi, xnorm ; + Int i ; + + /* DBL_EPSILON is a standard ANSI C term defined in */ + /* It is the smallest positive x such that 1.0+x != 1.0 */ + + nctau = 1000 * n * DBL_EPSILON ; + DEBUG0 (("do_step start: nctau = %30.20e\n", nctau)) ; + ASSERT (UMF_report_vector (n, (double *) X, (double *) NULL, UMF_debug, + FALSE, FALSE) == UMFPACK_OK) ; + + /* for approximate flop count, assume d1 > tau is always true */ + /* flops += (2*ABS_FLOPS + 5) * n ; (done in UMF_solve, above) */ + + /* ---------------------------------------------------------------------- */ + /* save the last iteration in case we need to reinstate it */ + /* ---------------------------------------------------------------------- */ + + last_omega [0] = omega [0] ; + last_omega [1] = omega [1] ; + last_omega [2] = omega [2] ; + + /* ---------------------------------------------------------------------- */ + /* compute sparse backward errors: omega [1] and omega [2] */ + /* ---------------------------------------------------------------------- */ + + /* xnorm = ||x|| maxnorm */ + xnorm = 0.0 ; + for (i = 0 ; i < n ; i++) + { + /* xi = ABS (X [i]) ; */ + ABS (xi, X [i]) ; + if (SCALAR_IS_NAN (xi)) + { + xnorm = xi ; + break ; + } + /* no NaN's to consider here: */ + xnorm = MAX (xnorm, xi) ; + } + + omega [1] = 0. ; + omega [2] = 0. ; + for (i = 0 ; i < n ; i++) + { + yix = Y [i] * xnorm ; + tau = (yix + B2 [i]) * nctau ; + d1 = Z2 [i] + B2 [i] ; + /* wi = ABS (W [i]) ; */ + ABS (wi, W [i]) ; + if (SCALAR_IS_NAN (d1)) + { + omega [1] = d1 ; + omega [2] = d1 ; + break ; + } + if (SCALAR_IS_NAN (tau)) + { + omega [1] = tau ; + omega [2] = tau ; + break ; + } + if (d1 > tau) /* a double relop, but no NaN's here */ + { + wd1 = wi / d1 ; + omega [1] = MAX (omega [1], wd1) ; + } + else if (tau > 0.0) /* a double relop, but no NaN's here */ + { + d2 = Z2 [i] + yix ; + wd2 = wi / d2 ; + omega [2] = MAX (omega [2], wd2) ; + } + } + + omega [0] = omega [1] + omega [2] ; + Info [UMFPACK_OMEGA1] = omega [1] ; + Info [UMFPACK_OMEGA2] = omega [2] ; + + /* ---------------------------------------------------------------------- */ + /* stop the iterations if the backward error is small, or NaN */ + /* ---------------------------------------------------------------------- */ + + Info [UMFPACK_IR_TAKEN] = step ; + Info [UMFPACK_IR_ATTEMPTED] = step ; + + if (SCALAR_IS_NAN (omega [0])) + { + DEBUG0 (("omega[0] is NaN - done.\n")) ; + ASSERT (UMF_report_vector (n, (double *) X, (double *) NULL, UMF_debug, + FALSE, FALSE) == UMFPACK_OK) ; + return (TRUE) ; + } + + if (omega [0] < DBL_EPSILON) /* double relop, but no NaN case here */ + { + DEBUG0 (("omega[0] too small - done.\n")) ; + ASSERT (UMF_report_vector (n, (double *) X, (double *) NULL, UMF_debug, + FALSE, FALSE) == UMFPACK_OK) ; + return (TRUE) ; + } + + /* ---------------------------------------------------------------------- */ + /* stop if insufficient decrease in omega */ + /* ---------------------------------------------------------------------- */ + + /* double relop, but no NaN case here: */ + if (step > 0 && omega [0] > last_omega [0] / 2) + { + DEBUG0 (("stop refinement\n")) ; + if (omega [0] > last_omega [0]) + { + /* last iteration better than this one, reinstate it */ + DEBUG0 (("last iteration better\n")) ; + for (i = 0 ; i < n ; i++) + { + X [i] = S [i] ; + } + Info [UMFPACK_OMEGA1] = last_omega [1] ; + Info [UMFPACK_OMEGA2] = last_omega [2] ; + } + Info [UMFPACK_IR_TAKEN] = step - 1 ; + ASSERT (UMF_report_vector (n, (double *) X, (double *) NULL, UMF_debug, + FALSE, FALSE) == UMFPACK_OK) ; + return (TRUE) ; + } + + /* ---------------------------------------------------------------------- */ + /* save current solution in case we need to reinstate */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i < n ; i++) + { + S [i] = X [i] ; + } + + /* ---------------------------------------------------------------------- */ + /* iterative refinement continues */ + /* ---------------------------------------------------------------------- */ + + ASSERT (UMF_report_vector (n, (double *) X, (double *) NULL, UMF_debug, + FALSE, FALSE) == UMFPACK_OK) ; + return (FALSE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_solve.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_solve.h new file mode 100644 index 0000000000..35a4867594 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_solve.h @@ -0,0 +1,26 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_solve +( + Int sys, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], + double Xx [ ], + const double Bx [ ], +#ifdef COMPLEX + const double Az [ ], + double Xz [ ], + const double Bz [ ], +#endif + NumericType *Numeric, + Int irstep, + double Info [UMFPACK_INFO], + Int Pattern [ ], + double SolveWork [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_start_front.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_start_front.c new file mode 100644 index 0000000000..bb34a9385c --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_start_front.c @@ -0,0 +1,284 @@ +/* ========================================================================== */ +/* === UMF_start_front ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Allocate the initial frontal matrix working array for a single chain. The + * front does not have to be big enough, since if it's too small it will get + * reallocated. The size computed here is just an estimate. */ + +#include "umf_internal.h" +#include "umf_grow_front.h" + +GLOBAL Int UMF_start_front /* returns TRUE if successful, FALSE otherwise */ +( + Int chain, + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) +{ + Int fnrows_max, fncols_max, fnr2, fnc2, fsize, fcurr_size, maxfrsize, + overflow, nb, f, cdeg ; + double maxbytes ; + + nb = Symbolic->nb ; + fnrows_max = Symbolic->Chain_maxrows [chain] ; + fncols_max = Symbolic->Chain_maxcols [chain] ; + + DEBUGm2 (("Start Front for chain "ID". fnrows_max "ID" fncols_max "ID"\n", + chain, fnrows_max, fncols_max)) ; + + Work->fnrows_max = fnrows_max ; + Work->fncols_max = fncols_max ; + Work->any_skip = FALSE ; + + maxbytes = sizeof (Entry) * + (double) (fnrows_max + nb) * (double) (fncols_max + nb) ; + fcurr_size = Work->fcurr_size ; + + if (Symbolic->prefer_diagonal) + { + /* Get a rough upper bound on the degree of the first pivot column in + * this front. Note that Col_degree is not maintained if diagonal + * pivoting is preferred. For most matrices, the first pivot column + * of the first frontal matrix of a new chain has only one tuple in + * it anyway, so this bound is exact in that case. */ + Int col, tpi, e, *E, *Col_tuples, *Col_tlen, *Cols ; + Tuple *tp, *tpend ; + Unit *Memory, *p ; + Element *ep ; + E = Work->E ; + Memory = Numeric->Memory ; + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + col = Work->nextcand ; + tpi = Col_tuples [col] ; + tp = (Tuple *) Memory + tpi ; + tpend = tp + Col_tlen [col] ; + cdeg = 0 ; + DEBUGm3 (("\n=============== start front: col "ID" tlen "ID"\n", + col, Col_tlen [col])) ; + for ( ; tp < tpend ; tp++) + { + DEBUG1 (("Tuple ("ID","ID")\n", tp->e, tp->f)) ; + e = tp->e ; + if (!E [e]) continue ; + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + if (Cols [f] == EMPTY) continue ; + DEBUG1 ((" nrowsleft "ID"\n", ep->nrowsleft)) ; + cdeg += ep->nrowsleft ; + } +#ifndef NDEBUG + DEBUGm3 (("start front cdeg: "ID" col "ID"\n", cdeg, col)) ; + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; +#endif + + /* cdeg is now the rough upper bound on the degree of the next pivot + * column. */ + + /* If AMD was called, we know the maximum number of nonzeros in any + * column of L. Use this as an upper bound for cdeg, but add 2 to + * account for a small amount of off-diagonal pivoting. */ + if (Symbolic->amd_dmax > 0) + { + cdeg = MIN (cdeg, Symbolic->amd_dmax) ; + } + + /* Increase it to account for larger columns later on. + * Also ensure that it's larger than zero. */ + cdeg += 2 ; + + /* cdeg cannot be larger than fnrows_max */ + cdeg = MIN (cdeg, fnrows_max) ; + + } + else + { + /* don't do the above cdeg computation */ + cdeg = 0 ; + } + + DEBUGm2 (("fnrows max "ID" fncols_max "ID"\n", fnrows_max, fncols_max)) ; + + /* the current frontal matrix is empty */ + ASSERT (Work->fnrows == 0 && Work->fncols == 0 && Work->fnpiv == 0) ; + + /* maximum row dimension is always odd, to avoid bad cache effects */ + ASSERT (fnrows_max >= 0) ; + ASSERT (fnrows_max % 2 == 1) ; + + /* ---------------------------------------------------------------------- + * allocate working array for current frontal matrix: + * minimum size: 1-by-1 + * maximum size: fnrows_max-by-fncols_max + * desired size: + * + * if Numeric->front_alloc_init >= 0: + * + * for unsymmetric matrices: + * Numeric->front_alloc_init * (fnrows_max-by-fncols_max) + * + * for symmetric matrices (diagonal pivoting preference, actually): + * Numeric->front_alloc_init * (fnrows_max-by-fncols_max), or + * cdeg*cdeg, whichever is smaller. + * + * if Numeric->front_alloc_init < 0: + * allocate a front of size -Numeric->front_alloc_init. + * + * Allocate the whole thing if it's small (less than 2*nb^2). Make sure the + * leading dimension of the frontal matrix is odd. + * + * Also allocate the nb-by-nb LU block, the dr-by-nb L block, and the + * nb-by-dc U block. + * ---------------------------------------------------------------------- */ + + /* get the maximum front size, avoiding integer overflow */ + overflow = INT_OVERFLOW (maxbytes) ; + if (overflow) + { + /* :: int overflow, max front size :: */ + maxfrsize = Int_MAX / sizeof (Entry) ; + } + else + { + maxfrsize = (fnrows_max + nb) * (fncols_max + nb) ; + } + ASSERT (!INT_OVERFLOW ((double) maxfrsize * sizeof (Entry))) ; + + if (Numeric->front_alloc_init < 0) + { + /* allocate a front of -Numeric->front_alloc_init entries */ + fsize = -Numeric->front_alloc_init ; + fsize = MAX (1, fsize) ; + } + else + { + if (INT_OVERFLOW (Numeric->front_alloc_init * maxbytes)) + { + /* :: int overflow, requested front size :: */ + fsize = Int_MAX / sizeof (Entry) ; + } + else + { + fsize = Numeric->front_alloc_init * maxfrsize ; + } + + if (cdeg > 0) + { + /* diagonal pivoting is in use. cdeg was computed above */ + Int fsize2 ; + + /* add the L and U blocks */ + cdeg += nb ; + + if (INT_OVERFLOW (((double) cdeg * (double) cdeg) * sizeof (Entry))) + { + /* :: int overflow, symmetric front size :: */ + fsize2 = Int_MAX / sizeof (Entry) ; + } + else + { + fsize2 = MAX (cdeg * cdeg, fcurr_size) ; + } + fsize = MIN (fsize, fsize2) ; + } + } + + fsize = MAX (fsize, 2*nb*nb) ; + + /* fsize and maxfrsize are now safe from integer overflow. They both + * include the size of the pivot blocks. */ + ASSERT (!INT_OVERFLOW ((double) fsize * sizeof (Entry))) ; + + Work->fnrows_new = 0 ; + Work->fncols_new = 0 ; + + /* desired size is fnr2-by-fnc2 (includes L and U blocks): */ + DEBUGm2 ((" fsize "ID" fcurr_size "ID"\n", fsize, fcurr_size)) ; + DEBUGm2 ((" maxfrsize "ID" fnr_curr "ID" fnc_curr "ID"\n", maxfrsize, + Work->fnr_curr, Work->fnc_curr)) ; + + if (fsize >= maxfrsize && !overflow) + { + /* max working array is small, allocate all of it */ + fnr2 = fnrows_max + nb ; + fnc2 = fncols_max + nb ; + fsize = maxfrsize ; + DEBUGm1 ((" sufficient for ("ID"+"ID")-by-("ID"+"ID")\n", + fnrows_max, nb, fncols_max, nb)) ; + } + else + { + /* allocate a smaller working array */ + if (fnrows_max <= fncols_max) + { + fnr2 = (Int) sqrt ((double) fsize) ; + /* make sure fnr2 is odd */ + fnr2 = MAX (fnr2, 1) ; + if (fnr2 % 2 == 0) fnr2++ ; + fnr2 = MIN (fnr2, fnrows_max + nb) ; + fnc2 = fsize / fnr2 ; + } + else + { + fnc2 = (Int) sqrt ((double) fsize) ; + fnc2 = MIN (fnc2, fncols_max + nb) ; + fnr2 = fsize / fnc2 ; + /* make sure fnr2 is odd */ + fnr2 = MAX (fnr2, 1) ; + if (fnr2 % 2 == 0) + { + fnr2++ ; + fnc2 = fsize / fnr2 ; + } + } + DEBUGm1 ((" smaller "ID"-by-"ID"\n", fnr2, fnc2)) ; + } + fnr2 = MIN (fnr2, fnrows_max + nb) ; + fnc2 = MIN (fnc2, fncols_max + nb) ; + ASSERT (fnr2 % 2 == 1) ; + ASSERT (fnr2 * fnc2 <= fsize) ; + + fnr2 -= nb ; + fnc2 -= nb ; + ASSERT (fnr2 >= 0) ; + ASSERT (fnc2 >= 0) ; + + if (fsize > fcurr_size) + { + DEBUGm1 ((" Grow front \n")) ; + Work->do_grow = TRUE ; + if (!UMF_grow_front (Numeric, fnr2, fnc2, Work, -1)) + { + /* since the minimum front size is 1-by-1, it would be nearly + * impossible to run out of memory here. */ + DEBUGm4 (("out of memory: start front\n")) ; + return (FALSE) ; + } + } + else + { + /* use the existing front */ + DEBUGm1 ((" existing front ok\n")) ; + Work->fnr_curr = fnr2 ; + Work->fnc_curr = fnc2 ; + Work->Flblock = Work->Flublock + nb * nb ; + Work->Fublock = Work->Flblock + nb * fnr2 ; + Work->Fcblock = Work->Fublock + nb * fnc2 ; + } + ASSERT (Work->Flblock == Work->Flublock + Work->nb*Work->nb) ; + ASSERT (Work->Fublock == Work->Flblock + Work->fnr_curr*Work->nb) ; + ASSERT (Work->Fcblock == Work->Fublock + Work->nb*Work->fnc_curr) ; + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_start_front.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_start_front.h new file mode 100644 index 0000000000..5992e73351 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_start_front.h @@ -0,0 +1,14 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_start_front +( + Int chain, + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_store_lu.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_store_lu.c new file mode 100644 index 0000000000..df0c63e094 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_store_lu.c @@ -0,0 +1,1035 @@ +/* ========================================================================== */ +/* === UMF_store_lu ========================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Store the LU factors. Called by the kernel. + Returns TRUE if successful, FALSE if out of memory. +*/ + +#include "umf_internal.h" +#include "umf_mem_alloc_head_block.h" +#include "umf_mem_free_tail_block.h" +#include "umf_get_memory.h" + +/* ========================================================================== */ + +#ifdef DROP +GLOBAL Int UMF_store_lu_drop +#else +GLOBAL Int UMF_store_lu +#endif +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int i, k, fnr_curr, fnrows, fncols, row, col, pivrow, pivcol, *Frows, + *Fcols, *Lpattern, *Upattern, *Lpos, *Upos, llen, ulen, fnc_curr, fnpiv, + uilen, lnz, unz, nb, *Lilen, + *Uilen, *Lip, *Uip, *Li, *Ui, pivcol_position, newLchain, newUchain, + pivrow_position, p, size, lip, uip, lnzi, lnzx, unzx, lnz2i, lnz2x, + unz2i, unz2x, zero_pivot, *Pivrow, *Pivcol, kk, + Lnz [MAXNB] ; + Entry *D, pivot_value, *Lval, *Uval, *Fl1, *Fl2, *Fu1, *Fu2, + *Flublock, *Flblock, *Fublock ; + +#ifndef NDEBUG + Int *Col_degree, *Row_degree ; +#endif + +#ifdef DROP + Int all_lnz, all_unz ; + double droptol ; + droptol = Numeric->droptol ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + fnrows = Work->fnrows ; + fncols = Work->fncols ; + fnpiv = Work->fnpiv ; + + Lpos = Numeric->Lpos ; + Upos = Numeric->Upos ; + Lilen = Numeric->Lilen ; + Uilen = Numeric->Uilen ; + + Lip = Numeric->Lip ; + Uip = Numeric->Uip ; + D = Numeric->D ; + + Flublock = Work->Flublock ; + Flblock = Work->Flblock ; + Fublock = Work->Fublock ; + + fnr_curr = Work->fnr_curr ; + fnc_curr = Work->fnc_curr ; + Frows = Work->Frows ; + Fcols = Work->Fcols ; + +#ifndef NDEBUG + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ +#endif + + Lpattern = Work->Lpattern ; + llen = Work->llen ; + Upattern = Work->Upattern ; + ulen = Work->ulen ; + + nb = Work->nb ; + +#ifndef NDEBUG + DEBUG1 (("\n##################################### STORE LU: fnrows "ID + " fncols "ID"\n", fnrows, fncols)) ; + + DEBUG2 (("\nFrontal matrix, including all space:\n" + "fnr_curr "ID" fnc_curr "ID" nb "ID"\n" + "fnrows "ID" fncols "ID" fnpiv "ID"\n", + fnr_curr, fnc_curr, nb, fnrows, fncols, fnpiv)) ; + + DEBUG2 (("\nJust the active part:\n")) ; + DEBUG7 (("C block: ")) ; + UMF_dump_dense (Work->Fcblock, fnr_curr, fnrows, fncols) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (Work->Flblock, fnr_curr, fnrows, fnpiv); + DEBUG7 (("U' block: ")) ; + UMF_dump_dense (Work->Fublock, fnc_curr, fncols, fnpiv) ; + DEBUG7 (("LU block: ")) ; + UMF_dump_dense (Work->Flublock, nb, fnpiv, fnpiv) ; + DEBUG7 (("Current frontal matrix: (prior to store LU)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + Pivrow = Work->Pivrow ; + Pivcol = Work->Pivcol ; + + /* ---------------------------------------------------------------------- */ + /* store the columns of L */ + /* ---------------------------------------------------------------------- */ + + for (kk = 0 ; kk < fnpiv ; kk++) + { + + /* ------------------------------------------------------------------ */ + /* one more pivot row and column is being stored into L and U */ + /* ------------------------------------------------------------------ */ + + k = Work->npiv + kk ; + + /* ------------------------------------------------------------------ */ + /* find the kth pivot row and pivot column */ + /* ------------------------------------------------------------------ */ + + pivrow = Pivrow [kk] ; + pivcol = Pivcol [kk] ; + +#ifndef NDEBUG + ASSERT (pivrow >= 0 && pivrow < Work->n_row) ; + ASSERT (pivcol >= 0 && pivcol < Work->n_col) ; + + DEBUGm4 (( + "\n -------------------------------------------------------------" + "Store LU: step " ID"\n", k)) ; + ASSERT (k < MIN (Work->n_row, Work->n_col)) ; + DEBUG2 (("Store column of L, k = "ID", llen "ID"\n", k, llen)) ; + for (i = 0 ; i < llen ; i++) + { + row = Lpattern [i] ; + ASSERT (row >= 0 && row < Work->n_row) ; + DEBUG2 ((" Lpattern["ID"] "ID" Lpos "ID, i, row, Lpos [row])) ; + if (row == pivrow) DEBUG2 ((" <- pivot row")) ; + DEBUG2 (("\n")) ; + ASSERT (i == Lpos [row]) ; + } +#endif + + /* ------------------------------------------------------------------ */ + /* remove pivot row from L */ + /* ------------------------------------------------------------------ */ + + /* remove pivot row index from current column of L */ + /* if a new Lchain starts, then all entries are removed later */ + DEBUG2 (("Removing pivrow from Lpattern, k = "ID"\n", k)) ; + ASSERT (!NON_PIVOTAL_ROW (pivrow)) ; + pivrow_position = Lpos [pivrow] ; + if (pivrow_position != EMPTY) + { + /* place the last entry in the column in the */ + /* position of the pivot row index */ + ASSERT (pivrow == Lpattern [pivrow_position]) ; + row = Lpattern [--llen] ; + /* ASSERT (NON_PIVOTAL_ROW (row)) ; */ + Lpattern [pivrow_position] = row ; + Lpos [row] = pivrow_position ; + Lpos [pivrow] = EMPTY ; + } + + /* ------------------------------------------------------------------ */ + /* store the pivot value, for the diagonal matrix D */ + /* ------------------------------------------------------------------ */ + + /* kk-th column of LU block */ + Fl1 = Flublock + kk * nb ; + + /* kk-th column of L in the L block */ + Fl2 = Flblock + kk * fnr_curr ; + + /* kk-th pivot in frontal matrix located in Flublock [kk, kk] */ + pivot_value = Fl1 [kk] ; + + D [k] = pivot_value ; + zero_pivot = IS_ZERO (pivot_value) ; + + DEBUG4 (("Pivot D["ID"]=", k)) ; + EDEBUG4 (pivot_value) ; + DEBUG4 (("\n")) ; + + /* ------------------------------------------------------------------ */ + /* count nonzeros in kth column of L */ + /* ------------------------------------------------------------------ */ + + lnz = 0 ; + lnz2i = 0 ; + lnz2x = llen ; + +#ifdef DROP + all_lnz = 0 ; + + for (i = kk + 1 ; i < fnpiv ; i++) + { + double s ; + Entry x = Fl1 [i] ; + if (IS_ZERO (x)) continue ; + all_lnz++ ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + lnz++ ; + if (Lpos [Pivrow [i]] == EMPTY) lnz2i++ ; + } + + for (i = 0 ; i < fnrows ; i++) + { + double s ; + Entry x = Fl2 [i] ; + if (IS_ZERO (x)) continue ; + all_lnz++ ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + lnz++ ; + if (Lpos [Frows [i]] == EMPTY) lnz2i++ ; + } + +#else + + for (i = kk + 1 ; i < fnpiv ; i++) + { + if (IS_ZERO (Fl1 [i])) continue ; + lnz++ ; + if (Lpos [Pivrow [i]] == EMPTY) lnz2i++ ; + } + + for (i = 0 ; i < fnrows ; i++) + { + if (IS_ZERO (Fl2 [i])) continue ; + lnz++ ; + if (Lpos [Frows [i]] == EMPTY) lnz2i++ ; + } + +#endif + + lnz2x += lnz2i ; + + /* determine if we start a new Lchain or continue the old one */ + if (llen == 0 || zero_pivot) + { + /* llen == 0 means there is no prior Lchain */ + /* D [k] == 0 means the pivot column is empty */ + newLchain = TRUE ; + } + else + { + newLchain = + /* storage for starting a new Lchain */ + UNITS (Entry, lnz) + UNITS (Int, lnz) + <= + /* storage for continuing a prior Lchain */ + UNITS (Entry, lnz2x) + UNITS (Int, lnz2i) ; + } + + if (newLchain) + { + /* start a new chain for column k of L */ + DEBUG2 (("Start new Lchain, k = "ID"\n", k)) ; + + pivrow_position = EMPTY ; + + /* clear the prior Lpattern */ + for (i = 0 ; i < llen ; i++) + { + row = Lpattern [i] ; + Lpos [row] = EMPTY ; + } + llen = 0 ; + + lnzi = lnz ; + lnzx = lnz ; + } + else + { + /* continue the prior Lchain */ + DEBUG2 (("Continue Lchain, k = "ID"\n", k)) ; + lnzi = lnz2i ; + lnzx = lnz2x ; + } + + /* ------------------------------------------------------------------ */ + /* allocate space for the column of L */ + /* ------------------------------------------------------------------ */ + + size = UNITS (Int, lnzi) + UNITS (Entry, lnzx) ; + +#ifndef NDEBUG + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) + { + double rrr = ((double) (rand ( ))) / (((double) RAND_MAX) + 1) ; + DEBUG4 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; + if (UMF_allocfail) DEBUGm2 (("Random garbage coll. (store LU)\n")); + } +#endif + + p = UMF_mem_alloc_head_block (Numeric, size) ; + if (!p) + { + Int r2, c2 ; + /* Do garbage collection, realloc, and try again. */ + /* Note that there are pivot rows/columns in current front. */ + if (Work->do_grow) + { + /* full compaction of current frontal matrix, since + * UMF_grow_front will be called next anyway. */ + r2 = fnrows ; + c2 = fncols ; + } + else + { + /* partial compaction. */ + r2 = MAX (fnrows, Work->fnrows_new + 1) ; + c2 = MAX (fncols, Work->fncols_new + 1) ; + } + DEBUGm3 (("get_memory from umf_store_lu:\n")) ; + if (!UMF_get_memory (Numeric, Work, size, r2, c2, TRUE)) + { + DEBUGm4 (("out of memory: store LU (1)\n")) ; + return (FALSE) ; /* out of memory */ + } + p = UMF_mem_alloc_head_block (Numeric, size) ; + if (!p) + { + DEBUGm4 (("out of memory: store LU (2)\n")) ; + return (FALSE) ; /* out of memory */ + } + /* garbage collection may have moved the current front */ + fnc_curr = Work->fnc_curr ; + fnr_curr = Work->fnr_curr ; + Flublock = Work->Flublock ; + Flblock = Work->Flblock ; + Fublock = Work->Fublock ; + Fl1 = Flublock + kk * nb ; + Fl2 = Flblock + kk * fnr_curr ; + } + + /* ------------------------------------------------------------------ */ + /* store the column of L */ + /* ------------------------------------------------------------------ */ + + lip = p ; + + Li = (Int *) (Numeric->Memory + p) ; + p += UNITS (Int, lnzi) ; + Lval = (Entry *) (Numeric->Memory + p) ; + p += UNITS (Entry, lnzx) ; + + for (i = 0 ; i < lnzx ; i++) + { + CLEAR (Lval [i]) ; + } + + /* store the numerical entries */ + + if (newLchain) + { + /* flag the first column in the Lchain by negating Lip [k] */ + lip = -lip ; + + ASSERT (llen == 0) ; + +#ifdef DROP + + for (i = kk + 1 ; i < fnpiv ; i++) + { + double s ; + Int row2, pos ; + Entry x = Fl1 [i] ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + row2 = Pivrow [i] ; + pos = llen++ ; + Lpattern [pos] = row2 ; + Lpos [row2] = pos ; + Li [pos] = row2 ; + Lval [pos] = x ; + } + + for (i = 0 ; i < fnrows ; i++) + { + double s ; + Int row2, pos ; + Entry x = Fl2 [i] ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + row2 = Frows [i] ; + pos = llen++ ; + Lpattern [pos] = row2 ; + Lpos [row2] = pos ; + Li [pos] = row2 ; + Lval [pos] = x ; + } + +#else + + for (i = kk + 1 ; i < fnpiv ; i++) + { + Int row2, pos ; + Entry x = Fl1 [i] ; + if (IS_ZERO (x)) continue ; + row2 = Pivrow [i] ; + pos = llen++ ; + Lpattern [pos] = row2 ; + Lpos [row2] = pos ; + Li [pos] = row2 ; + Lval [pos] = x ; + } + + for (i = 0 ; i < fnrows ; i++) + { + Int row2, pos ; + Entry x = Fl2 [i] ; + if (IS_ZERO (x)) continue ; + row2 = Frows [i] ; + pos = llen++ ; + Lpattern [pos] = row2 ; + Lpos [row2] = pos ; + Li [pos] = row2 ; + Lval [pos] = x ; + } + +#endif + + } + else + { + ASSERT (llen > 0) ; + +#ifdef DROP + + for (i = kk + 1 ; i < fnpiv ; i++) + { + double s ; + Int row2, pos ; + Entry x = Fl1 [i] ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + row2 = Pivrow [i] ; + pos = Lpos [row2] ; + if (pos == EMPTY) + { + pos = llen++ ; + Lpattern [pos] = row2 ; + Lpos [row2] = pos ; + *Li++ = row2 ; + } + Lval [pos] = x ; + } + + for (i = 0 ; i < fnrows ; i++) + { + double s ; + Int row2, pos ; + Entry x = Fl2 [i] ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + row2 = Frows [i] ; + pos = Lpos [row2] ; + if (pos == EMPTY) + { + pos = llen++ ; + Lpattern [pos] = row2 ; + Lpos [row2] = pos ; + *Li++ = row2 ; + } + Lval [pos] = x ; + } + +#else + + for (i = kk + 1 ; i < fnpiv ; i++) + { + Int row2, pos ; + Entry x = Fl1 [i] ; + if (IS_ZERO (x)) continue ; + row2 = Pivrow [i] ; + pos = Lpos [row2] ; + if (pos == EMPTY) + { + pos = llen++ ; + Lpattern [pos] = row2 ; + Lpos [row2] = pos ; + *Li++ = row2 ; + } + Lval [pos] = x ; + } + + for (i = 0 ; i < fnrows ; i++) + { + Int row2, pos ; + Entry x = Fl2 [i] ; + if (IS_ZERO (x)) continue ; + row2 = Frows [i] ; + pos = Lpos [row2] ; + if (pos == EMPTY) + { + pos = llen++ ; + Lpattern [pos] = row2 ; + Lpos [row2] = pos ; + *Li++ = row2 ; + } + Lval [pos] = x ; + } + +#endif + + } + DEBUG4 (("llen "ID" lnzx "ID"\n", llen, lnzx)) ; + ASSERT (llen == lnzx) ; + ASSERT (lnz <= llen) ; + DEBUG4 (("lnz "ID" \n", lnz)) ; + +#ifdef DROP + + DEBUG4 (("all_lnz "ID" \n", all_lnz)) ; + ASSERT (lnz <= all_lnz) ; + Numeric->lnz += lnz ; + Numeric->all_lnz += all_lnz ; + Lnz [kk] = all_lnz ; + +#else + + Numeric->lnz += lnz ; + Numeric->all_lnz += lnz ; + Lnz [kk] = lnz ; +#endif + + Numeric->nLentries += lnzx ; + Work->llen = llen ; + Numeric->isize += lnzi ; + + /* ------------------------------------------------------------------ */ + /* the pivot column is fully assembled and scaled, and is now the */ + /* k-th column of L */ + /* ------------------------------------------------------------------ */ + + Lpos [pivrow] = pivrow_position ; /* not aliased */ + Lip [pivcol] = lip ; /* aliased with Col_tuples */ + Lilen [pivcol] = lnzi ; /* aliased with Col_tlen */ + + } + + /* ---------------------------------------------------------------------- */ + /* store the rows of U */ + /* ---------------------------------------------------------------------- */ + + for (kk = 0 ; kk < fnpiv ; kk++) + { + + /* ------------------------------------------------------------------ */ + /* one more pivot row and column is being stored into L and U */ + /* ------------------------------------------------------------------ */ + + k = Work->npiv + kk ; + + /* ------------------------------------------------------------------ */ + /* find the kth pivot row and pivot column */ + /* ------------------------------------------------------------------ */ + + pivrow = Pivrow [kk] ; + pivcol = Pivcol [kk] ; + +#ifndef NDEBUG + ASSERT (pivrow >= 0 && pivrow < Work->n_row) ; + ASSERT (pivcol >= 0 && pivcol < Work->n_col) ; + + DEBUG2 (("Store row of U, k = "ID", ulen "ID"\n", k, ulen)) ; + for (i = 0 ; i < ulen ; i++) + { + col = Upattern [i] ; + DEBUG2 ((" Upattern["ID"] "ID, i, col)) ; + if (col == pivcol) DEBUG2 ((" <- pivot col")) ; + DEBUG2 (("\n")) ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (i == Upos [col]) ; + } +#endif + + /* ------------------------------------------------------------------ */ + /* get the pivot value, for the diagonal matrix D */ + /* ------------------------------------------------------------------ */ + + zero_pivot = IS_ZERO (D [k]) ; + + /* ------------------------------------------------------------------ */ + /* count the nonzeros in the row of U */ + /* ------------------------------------------------------------------ */ + + /* kk-th row of U in the LU block */ + Fu1 = Flublock + kk ; + + /* kk-th row of U in the U block */ + Fu2 = Fublock + kk * fnc_curr ; + + unz = 0 ; + unz2i = 0 ; + unz2x = ulen ; + DEBUG2 (("unz2x is "ID", lnzx "ID"\n", unz2x, lnzx)) ; + + /* if row k does not end a Uchain, pivcol not included in ulen */ + ASSERT (!NON_PIVOTAL_COL (pivcol)) ; + pivcol_position = Upos [pivcol] ; + if (pivcol_position != EMPTY) + { + unz2x-- ; + DEBUG2 (("(exclude pivcol) unz2x is now "ID"\n", unz2x)) ; + } + + ASSERT (unz2x >= 0) ; + +#ifdef DROP + all_unz = 0 ; + + for (i = kk + 1 ; i < fnpiv ; i++) + { + double s ; + Entry x = Fu1 [i*nb] ; + if (IS_ZERO (x)) continue ; + all_unz++ ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + unz++ ; + if (Upos [Pivcol [i]] == EMPTY) unz2i++ ; + } + + for (i = 0 ; i < fncols ; i++) + { + double s ; + Entry x = Fu2 [i] ; + if (IS_ZERO (x)) continue ; + all_unz++ ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + unz++ ; + if (Upos [Fcols [i]] == EMPTY) unz2i++ ; + } + +#else + + for (i = kk + 1 ; i < fnpiv ; i++) + { + if (IS_ZERO (Fu1 [i*nb])) continue ; + unz++ ; + if (Upos [Pivcol [i]] == EMPTY) unz2i++ ; + } + + for (i = 0 ; i < fncols ; i++) + { + if (IS_ZERO (Fu2 [i])) continue ; + unz++ ; + if (Upos [Fcols [i]] == EMPTY) unz2i++ ; + } + +#endif + + unz2x += unz2i ; + + ASSERT (IMPLIES (k == 0, ulen == 0)) ; + + /* determine if we start a new Uchain or continue the old one */ + if (ulen == 0 || zero_pivot) + { + /* ulen == 0 means there is no prior Uchain */ + /* D [k] == 0 means the matrix is singular (pivot row might */ + /* not be empty, however, but start a new Uchain to prune zero */ + /* entries for the deg > 0 test in UMF_u*solve) */ + newUchain = TRUE ; + } + else + { + newUchain = + /* approximate storage for starting a new Uchain */ + UNITS (Entry, unz) + UNITS (Int, unz) + <= + /* approximate storage for continuing a prior Uchain */ + UNITS (Entry, unz2x) + UNITS (Int, unz2i) ; + + /* this would be exact, except for the Int to Unit rounding, */ + /* because the Upattern is stored only at the end of the Uchain */ + } + + /* ------------------------------------------------------------------ */ + /* allocate space for the row of U */ + /* ------------------------------------------------------------------ */ + + size = 0 ; + if (newUchain) + { + /* store the pattern of the last row in the prior Uchain */ + size += UNITS (Int, ulen) ; + unzx = unz ; + } + else + { + unzx = unz2x ; + } + size += UNITS (Entry, unzx) ; + +#ifndef NDEBUG + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) + { + double rrr = ((double) (rand ( ))) / (((double) RAND_MAX) + 1) ; + DEBUG4 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; + if (UMF_allocfail) DEBUGm2 (("Random garbage coll. (store LU)\n")); + } +#endif + + p = UMF_mem_alloc_head_block (Numeric, size) ; + if (!p) + { + Int r2, c2 ; + /* Do garbage collection, realloc, and try again. */ + /* Note that there are pivot rows/columns in current front. */ + if (Work->do_grow) + { + /* full compaction of current frontal matrix, since + * UMF_grow_front will be called next anyway. */ + r2 = fnrows ; + c2 = fncols ; + } + else + { + /* partial compaction. */ + r2 = MAX (fnrows, Work->fnrows_new + 1) ; + c2 = MAX (fncols, Work->fncols_new + 1) ; + } + DEBUGm3 (("get_memory from umf_store_lu:\n")) ; + if (!UMF_get_memory (Numeric, Work, size, r2, c2, TRUE)) + { + /* :: get memory, column of L :: */ + DEBUGm4 (("out of memory: store LU (1)\n")) ; + return (FALSE) ; /* out of memory */ + } + p = UMF_mem_alloc_head_block (Numeric, size) ; + if (!p) + { + /* :: out of memory, column of U :: */ + DEBUGm4 (("out of memory: store LU (2)\n")) ; + return (FALSE) ; /* out of memory */ + } + /* garbage collection may have moved the current front */ + fnc_curr = Work->fnc_curr ; + fnr_curr = Work->fnr_curr ; + Flublock = Work->Flublock ; + Flblock = Work->Flblock ; + Fublock = Work->Fublock ; + Fu1 = Flublock + kk ; + Fu2 = Fublock + kk * fnc_curr ; + } + + /* ------------------------------------------------------------------ */ + /* store the row of U */ + /* ------------------------------------------------------------------ */ + + uip = p ; + + if (newUchain) + { + /* starting a new Uchain - flag this by negating Uip [k] */ + uip = -uip ; + DEBUG2 (("Start new Uchain, k = "ID"\n", k)) ; + + pivcol_position = EMPTY ; + + /* end the prior Uchain */ + /* save the current Upattern, and then */ + /* clear it and start a new Upattern */ + DEBUG2 (("Ending prior chain, k-1 = "ID"\n", k-1)) ; + uilen = ulen ; + Ui = (Int *) (Numeric->Memory + p) ; + Numeric->isize += ulen ; + p += UNITS (Int, ulen) ; + for (i = 0 ; i < ulen ; i++) + { + col = Upattern [i] ; + ASSERT (col >= 0 && col < Work->n_col) ; + Upos [col] = EMPTY ; + Ui [i] = col ; + } + + ulen = 0 ; + + } + else + { + /* continue the prior Uchain */ + DEBUG2 (("Continue Uchain, k = "ID"\n", k)) ; + ASSERT (k > 0) ; + + /* remove pivot col index from current row of U */ + /* if a new Uchain starts, then all entries are removed later */ + DEBUG2 (("Removing pivcol from Upattern, k = "ID"\n", k)) ; + + if (pivcol_position != EMPTY) + { + /* place the last entry in the row in the */ + /* position of the pivot col index */ + ASSERT (pivcol == Upattern [pivcol_position]) ; + col = Upattern [--ulen] ; + ASSERT (col >= 0 && col < Work->n_col) ; + Upattern [pivcol_position] = col ; + Upos [col] = pivcol_position ; + Upos [pivcol] = EMPTY ; + } + + /* this row continues the Uchain. Keep track of how much */ + /* to trim from the k-th length to get the length of the */ + /* (k-1)st row of U */ + uilen = unz2i ; + + } + + Uval = (Entry *) (Numeric->Memory + p) ; + /* p += UNITS (Entry, unzx), no need to increment p */ + + for (i = 0 ; i < unzx ; i++) + { + CLEAR (Uval [i]) ; + } + + if (newUchain) + { + ASSERT (ulen == 0) ; + +#ifdef DROP + + for (i = kk + 1 ; i < fnpiv ; i++) + { + double s ; + Int col2, pos ; + Entry x = Fu1 [i*nb] ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + col2 = Pivcol [i] ; + pos = ulen++ ; + Upattern [pos] = col2 ; + Upos [col2] = pos ; + Uval [pos] = x ; + } + + for (i = 0 ; i < fncols ; i++) + { + double s ; + Int col2, pos ; + Entry x = Fu2 [i] ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + col2 = Fcols [i] ; + pos = ulen++ ; + Upattern [pos] = col2 ; + Upos [col2] = pos ; + Uval [pos] = x ; + } + +#else + + for (i = kk + 1 ; i < fnpiv ; i++) + { + Int col2, pos ; + Entry x = Fu1 [i*nb] ; + if (IS_ZERO (x)) continue ; + col2 = Pivcol [i] ; + pos = ulen++ ; + Upattern [pos] = col2 ; + Upos [col2] = pos ; + Uval [pos] = x ; + } + + for (i = 0 ; i < fncols ; i++) + { + Int col2, pos ; + Entry x = Fu2 [i] ; + if (IS_ZERO (x)) continue ; + col2 = Fcols [i] ; + pos = ulen++ ; + Upattern [pos] = col2 ; + Upos [col2] = pos ; + Uval [pos] = x ; + } + +#endif + + } + else + { + + ASSERT (ulen > 0) ; + + /* store the numerical entries and find new nonzeros */ + +#ifdef DROP + + for (i = kk + 1 ; i < fnpiv ; i++) + { + double s ; + Int col2, pos ; + Entry x = Fu1 [i*nb] ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + col2 = Pivcol [i] ; + pos = Upos [col2] ; + if (pos == EMPTY) + { + pos = ulen++ ; + Upattern [pos] = col2 ; + Upos [col2] = pos ; + } + Uval [pos] = x ; + } + + for (i = 0 ; i < fncols ; i++) + { + double s ; + Int col2, pos ; + Entry x = Fu2 [i] ; + APPROX_ABS (s, x) ; + if (s <= droptol) continue ; + col2 = Fcols [i] ; + pos = Upos [col2] ; + if (pos == EMPTY) + { + pos = ulen++ ; + Upattern [pos] = col2 ; + Upos [col2] = pos ; + } + Uval [pos] = x ; + } + +#else + + for (i = kk + 1 ; i < fnpiv ; i++) + { + Int col2, pos ; + Entry x = Fu1 [i*nb] ; + if (IS_ZERO (x)) continue ; + col2 = Pivcol [i] ; + pos = Upos [col2] ; + if (pos == EMPTY) + { + pos = ulen++ ; + Upattern [pos] = col2 ; + Upos [col2] = pos ; + } + Uval [pos] = x ; + } + + for (i = 0 ; i < fncols ; i++) + { + Int col2, pos ; + Entry x = Fu2 [i] ; + if (IS_ZERO (x)) continue ; + col2 = Fcols [i] ; + pos = Upos [col2] ; + if (pos == EMPTY) + { + pos = ulen++ ; + Upattern [pos] = col2 ; + Upos [col2] = pos ; + } + Uval [pos] = x ; + } + +#endif + + } + + ASSERT (ulen == unzx) ; + ASSERT (unz <= ulen) ; + DEBUG4 (("unz "ID" \n", unz)) ; + +#ifdef DROP + + DEBUG4 (("all_unz "ID" \n", all_unz)) ; + ASSERT (unz <= all_unz) ; + Numeric->unz += unz ; + Numeric->all_unz += all_unz ; + /* count the "true" flops, based on LU pattern only */ + Numeric->flops += DIV_FLOPS * Lnz [kk] /* scale pivot column */ + + MULTSUB_FLOPS * (Lnz [kk] * all_unz) ; /* outer product */ + +#else + + Numeric->unz += unz ; + Numeric->all_unz += unz ; + /* count the "true" flops, based on LU pattern only */ + Numeric->flops += DIV_FLOPS * Lnz [kk] /* scale pivot column */ + + MULTSUB_FLOPS * (Lnz [kk] * unz) ; /* outer product */ +#endif + + Numeric->nUentries += unzx ; + Work->ulen = ulen ; + DEBUG1 (("Work->ulen = "ID" at end of pivot step, k: "ID"\n", ulen, k)); + + /* ------------------------------------------------------------------ */ + /* the pivot row is the k-th row of U */ + /* ------------------------------------------------------------------ */ + + Upos [pivcol] = pivcol_position ; /* not aliased */ + Uip [pivrow] = uip ; /* aliased with Row_tuples */ + Uilen [pivrow] = uilen ; /* aliased with Row_tlen */ + + } + + /* ---------------------------------------------------------------------- */ + /* no more pivots in frontal working array */ + /* ---------------------------------------------------------------------- */ + + Work->npiv += fnpiv ; + Work->fnpiv = 0 ; + Work->fnzeros = 0 ; + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_store_lu.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_store_lu.h new file mode 100644 index 0000000000..142ee5b71d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_store_lu.h @@ -0,0 +1,18 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_store_lu +( + NumericType *Numeric, + WorkType *Work +) ; + +GLOBAL Int UMF_store_lu_drop +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_symbolic_usage.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_symbolic_usage.c new file mode 100644 index 0000000000..cc8b94a298 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_symbolic_usage.c @@ -0,0 +1,46 @@ +/* ========================================================================== */ +/* === UMF_symbolic_usage =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Returns the final size of the Symbolic object, in Units */ + +#include "umf_internal.h" + +GLOBAL double UMF_symbolic_usage +( + Int n_row, + Int n_col, + Int nchains, + Int nfr, + Int esize, /* zero if no dense rows. Otherwise, equal to the + * number of non-singleton, non-empty columns */ + Int prefer_diagonal +) +{ + double units ; + + units = + DUNITS (SymbolicType, 1) /* Symbolic structure */ + + 2 * DUNITS (Int, n_col+1) /* Cperm_init, Cdeg */ + + 2 * DUNITS (Int, n_row+1) /* Rperm_init, Rdeg */ + + 3 * DUNITS (Int, nchains+1) /* Chain_ */ + + 4 * DUNITS (Int, nfr+1) ; /* Front_ */ + + /* if dense rows are present */ + units += DUNITS (Int, esize) ; /* Esize */ + + /* for diagonal pivoting */ + if (prefer_diagonal) + { + units += DUNITS (Int, n_col+1) ; /* Diagonal_map */ + } + + return (units) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_symbolic_usage.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_symbolic_usage.h new file mode 100644 index 0000000000..1767c2ce06 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_symbolic_usage.h @@ -0,0 +1,16 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL double UMF_symbolic_usage +( + Int n_row, + Int n_col, + Int nchains, + Int nfr, + Int esize, + Int prefer_diagonal +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_transpose.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_transpose.c new file mode 100644 index 0000000000..074911939d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_transpose.c @@ -0,0 +1,332 @@ +/* ========================================================================== */ +/* === UMF_transpose ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Not user-callable. Computes a permuted transpose, R = (A (P,Q(1:nq)))' in + MATLAB notation, where R is in column-form. A is n_row-by-n_col, the + row-form matrix R is n_row-by-nq, where nq <= n_col. A may be singular. + The complex version can do transpose (') or array transpose (.'). + + Uses Gustavson's method (Two Fast Algorithms for Sparse Matrices: + Multiplication and Permuted Transposition, ACM Trans. on Math. Softw., + vol 4, no 3, pp. 250-269). +*/ + +#include "umf_internal.h" +#include "umf_is_permutation.h" + +GLOBAL Int UMF_transpose +( + Int n_row, /* A is n_row-by-n_col */ + Int n_col, + const Int Ap [ ], /* size n_col+1 */ + const Int Ai [ ], /* size nz = Ap [n_col] */ + const double Ax [ ], /* size nz if present */ + + const Int P [ ], /* P [k] = i means original row i is kth row in A(P,Q)*/ + /* P is identity if not present */ + /* size n_row, if present */ + + const Int Q [ ], /* Q [k] = j means original col j is kth col in A(P,Q)*/ + /* Q is identity if not present */ + /* size nq, if present */ + Int nq, /* size of Q, ignored if Q is (Int *) NULL */ + + /* output matrix: Rp, Ri, Rx, and Rz: */ + Int Rp [ ], /* size n_row+1 */ + Int Ri [ ], /* size nz */ + double Rx [ ], /* size nz, if present */ + + Int W [ ], /* size max (n_row,n_col) workspace */ + + Int check /* if true, then check inputs */ +#ifdef COMPLEX + , const double Az [ ] /* size nz */ + , double Rz [ ] /* size nz */ + , Int do_conjugate /* if true, then do conjugate transpose */ + /* otherwise, do array transpose */ +#endif +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int i, j, k, p, bp, newj, do_values ; + + /* ---------------------------------------------------------------------- */ + /* check inputs */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + Int nz ; + ASSERT (n_col >= 0) ; + nz = (Ap != (Int *) NULL) ? Ap [n_col] : 0 ; + DEBUG2 (("UMF_transpose: "ID"-by-"ID" nz "ID"\n", n_row, n_col, nz)) ; +#endif + + if (check) + { + /* UMFPACK_symbolic skips this check */ + /* UMFPACK_transpose always does this check */ + if (!Ai || !Ap || !Ri || !Rp || !W) + { + return (UMFPACK_ERROR_argument_missing) ; + } + if (n_row <= 0 || n_col <= 0) /* n_row,n_col must be > 0 */ + { + return (UMFPACK_ERROR_n_nonpositive) ; + } + if (!UMF_is_permutation (P, W, n_row, n_row) || + !UMF_is_permutation (Q, W, nq, nq)) + { + return (UMFPACK_ERROR_invalid_permutation) ; + } + if (!AMD_valid (n_row, n_col, Ap, Ai)) + { + return (UMFPACK_ERROR_invalid_matrix) ; + } + } + +#ifndef NDEBUG + DEBUG2 (("UMF_transpose, input matrix:\n")) ; + UMF_dump_col_matrix (Ax, +#ifdef COMPLEX + Az, +#endif + Ai, Ap, n_row, n_col, nz) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* count the entries in each row of A */ + /* ---------------------------------------------------------------------- */ + + /* use W as workspace for RowCount */ + + for (i = 0 ; i < n_row ; i++) + { + W [i] = 0 ; + Rp [i] = 0 ; + } + + if (Q != (Int *) NULL) + { + for (newj = 0 ; newj < nq ; newj++) + { + j = Q [newj] ; + ASSERT (j >= 0 && j < n_col) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + ASSERT (i >= 0 && i < n_row) ; + W [i]++ ; + } + } + } + else + { + for (j = 0 ; j < n_col ; j++) + { + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + ASSERT (i >= 0 && i < n_row) ; + W [i]++ ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* compute the row pointers for R = A (P,Q) */ + /* ---------------------------------------------------------------------- */ + + if (P != (Int *) NULL) + { + Rp [0] = 0 ; + for (k = 0 ; k < n_row ; k++) + { + i = P [k] ; + ASSERT (i >= 0 && i < n_row) ; + Rp [k+1] = Rp [k] + W [i] ; + } + for (k = 0 ; k < n_row ; k++) + { + i = P [k] ; + ASSERT (i >= 0 && i < n_row) ; + W [i] = Rp [k] ; + } + } + else + { + Rp [0] = 0 ; + for (i = 0 ; i < n_row ; i++) + { + Rp [i+1] = Rp [i] + W [i] ; + } + for (i = 0 ; i < n_row ; i++) + { + W [i] = Rp [i] ; + } + } + ASSERT (Rp [n_row] <= Ap [n_col]) ; + + /* at this point, W holds the permuted row pointers */ + + /* ---------------------------------------------------------------------- */ + /* construct the row form of B */ + /* ---------------------------------------------------------------------- */ + + do_values = Ax && Rx ; +#ifdef COMPLEX + do_values = do_values && Az && Rz ; +#endif + +#ifdef COMPLEX + if (do_conjugate && do_values) + { + if (Q != (Int *) NULL) + { + + /* R = A (P,Q)' */ + for (newj = 0 ; newj < nq ; newj++) + { + j = Q [newj] ; + ASSERT (j >= 0 && j < n_col) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + bp = W [Ai [p]]++ ; + Ri [bp] = newj ; + Rx [bp] = Ax [p] ; + Rz [bp] = -Az [p] ; + } + } + + } + else + { + + /* R = A (P,:)' */ + for (j = 0 ; j < n_col ; j++) + { + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + bp = W [Ai [p]]++ ; + Ri [bp] = j ; + Rx [bp] = Ax [p] ; + Rz [bp] = -Az [p] ; + } + } + + } + } + else +#endif + { + if (Q != (Int *) NULL) + { + if (do_values) + { + + /* R = A (P,Q).' */ + for (newj = 0 ; newj < nq ; newj++) + { + j = Q [newj] ; + ASSERT (j >= 0 && j < n_col) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + bp = W [Ai [p]]++ ; + Ri [bp] = newj ; + Rx [bp] = Ax [p] ; +#ifdef COMPLEX + Rz [bp] = Az [p] ; +#endif + } + } + + } + else + { + + /* R = pattern of A (P,Q).' */ + for (newj = 0 ; newj < nq ; newj++) + { + j = Q [newj] ; + ASSERT (j >= 0 && j < n_col) ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + Ri [W [Ai [p]]++] = newj ; + } + } + + } + } + else + { + if (do_values) + { + + /* R = A (P,:).' */ + for (j = 0 ; j < n_col ; j++) + { + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + bp = W [Ai [p]]++ ; + Ri [bp] = j ; + Rx [bp] = Ax [p] ; +#ifdef COMPLEX + Rz [bp] = Az [p] ; +#endif + } + } + + } + else + { + + /* R = pattern of A (P,:).' */ + for (j = 0 ; j < n_col ; j++) + { + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + Ri [W [Ai [p]]++] = j ; + } + } + + } + } + + } + +#ifndef NDEBUG + for (k = 0 ; k < n_row ; k++) + { + if (P != (Int *) NULL) + { + i = P [k] ; + } + else + { + i = k ; + } + DEBUG3 ((ID": W[i] "ID" Rp[k+1] "ID"\n", i, W [i], Rp [k+1])) ; + ASSERT (W [i] == Rp [k+1]) ; + } + DEBUG2 (("UMF_transpose, output matrix:\n")) ; + UMF_dump_col_matrix (Rx, +#ifdef COMPLEX + Rz, +#endif + Ri, Rp, n_col, n_row, Rp [n_row]) ; + ASSERT (AMD_valid (n_col, n_row, Rp, Ri)) ; +#endif + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_transpose.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_transpose.h new file mode 100644 index 0000000000..0e1aac9c22 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_transpose.h @@ -0,0 +1,28 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_transpose +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], + const Int P [ ], + const Int Q [ ], + Int nq, + Int Rp [ ], + Int Ri [ ], + double Rx [ ], + Int W [ ], + Int check +#ifdef COMPLEX + , const double Az [ ] + , double Rz [ ] + , Int do_conjugate +#endif +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_triplet.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_triplet.c new file mode 100644 index 0000000000..8192af7f2c --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_triplet.c @@ -0,0 +1,386 @@ +/* ========================================================================== */ +/* === UMF_triplet ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Not user callable. Converts triplet input to column-oriented form. + Duplicate entries may exist (they are summed in the output). The columns + of the column-oriented form are in sorted order. The input is not modified. + Returns 1 if OK, 0 if an error occured. + + Compiled into four different routines for each version (di, dl, zi, zl), + for a total of 16 different routines. +*/ + +#include "umf_internal.h" +#include "umf_malloc.h" +#include "umf_free.h" + +#ifdef DO_MAP +#ifdef DO_VALUES +GLOBAL Int UMF_triplet_map_x +#else +GLOBAL Int UMF_triplet_map_nox +#endif +#else +#ifdef DO_VALUES +GLOBAL Int UMF_triplet_nomap_x +#else +GLOBAL Int UMF_triplet_nomap_nox +#endif +#endif +( + Int n_row, + Int n_col, + Int nz, + const Int Ti [ ], /* size nz */ + const Int Tj [ ], /* size nz */ + Int Ap [ ], /* size n_col + 1 */ + Int Ai [ ], /* size nz */ + Int Rp [ ], /* size n_row + 1 */ + Int Rj [ ], /* size nz */ + Int W [ ], /* size max (n_row, n_col) */ + Int RowCount [ ] /* size n_row */ +#ifdef DO_VALUES + , const double Tx [ ] /* size nz */ + , double Ax [ ] /* size nz */ + , double Rx [ ] /* size nz */ +#ifdef COMPLEX + , const double Tz [ ] /* size nz */ + , double Az [ ] /* size nz */ + , double Rz [ ] /* size nz */ +#endif +#endif +#ifdef DO_MAP + , Int Map [ ] /* size nz */ + , Int Map2 [ ] /* size nz */ +#endif +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int i, j, k, p, cp, p1, p2, pdest, pj ; +#ifdef DO_MAP + Int duplicates ; +#endif + + /* ---------------------------------------------------------------------- */ + /* count the entries in each row (also counting duplicates) */ + /* ---------------------------------------------------------------------- */ + + /* use W as workspace for row counts (including duplicates) */ + for (i = 0 ; i < n_row ; i++) + { + W [i] = 0 ; + } + + for (k = 0 ; k < nz ; k++) + { + i = Ti [k] ; + j = Tj [k] ; + if (i < 0 || i >= n_row || j < 0 || j >= n_col) + { + return (UMFPACK_ERROR_invalid_matrix) ; + } + W [i]++ ; +#ifndef NDEBUG + DEBUG1 ((ID " triplet: "ID" "ID" ", k, i, j)) ; +#ifdef DO_VALUES + { + Entry tt ; + ASSIGN (tt, Tx [k], Tz [k]) ; + EDEBUG2 (tt) ; + DEBUG1 (("\n")) ; + } +#endif +#endif + } + + /* ---------------------------------------------------------------------- */ + /* compute the row pointers */ + /* ---------------------------------------------------------------------- */ + + Rp [0] = 0 ; + for (i = 0 ; i < n_row ; i++) + { + Rp [i+1] = Rp [i] + W [i] ; + W [i] = Rp [i] ; + } + + /* W is now equal to the row pointers */ + + /* ---------------------------------------------------------------------- */ + /* construct the row form */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < nz ; k++) + { + p = W [Ti [k]]++ ; +#ifdef DO_MAP + Map [k] = p ; +#endif + Rj [p] = Tj [k] ; +#ifdef DO_VALUES + Rx [p] = Tx [k] ; +#ifdef COMPLEX + Rz [p] = Tz [k] ; +#endif +#endif + } + + /* Rp stays the same, but W [i] is advanced to the start of row i+1 */ + +#ifndef NDEBUG + for (i = 0 ; i < n_row ; i++) + { + ASSERT (W [i] == Rp [i+1]) ; + } +#ifdef DO_MAP + for (k = 0 ; k < nz ; k++) + { + /* make sure that kth triplet is mapped correctly */ + p = Map [k] ; + DEBUG1 (("First row map: Map ["ID"] = "ID"\n", k, p)) ; + i = Ti [k] ; + j = Tj [k] ; + ASSERT (j == Rj [p]) ; + ASSERT (Rp [i] <= p && p < Rp [i+1]) ; + } +#endif +#endif + + /* ---------------------------------------------------------------------- */ + /* sum up duplicates */ + /* ---------------------------------------------------------------------- */ + + /* use W [j] to hold position in Ri/Rx/Rz of a_ij, for row i [ */ + + for (j = 0 ; j < n_col ; j++) + { + W [j] = EMPTY ; + } + +#ifdef DO_MAP + duplicates = FALSE ; +#endif + + for (i = 0 ; i < n_row ; i++) + { + p1 = Rp [i] ; + p2 = Rp [i+1] ; + pdest = p1 ; + /* At this point, W [j] < p1 holds true for all columns j, */ + /* because Ri/Rx/Rz is stored in row oriented order. */ +#ifndef NDEBUG + if (UMF_debug >= -2) + { + for (j = 0 ; j < n_col ; j++) + { + ASSERT (W [j] < p1) ; + } + } +#endif + for (p = p1 ; p < p2 ; p++) + { + j = Rj [p] ; + ASSERT (j >= 0 && j < n_col) ; + pj = W [j] ; + if (pj >= p1) + { + /* this column index, j, is already in row i, at position pj */ + ASSERT (pj < p) ; + ASSERT (Rj [pj] == j) ; +#ifdef DO_MAP + Map2 [p] = pj ; + duplicates = TRUE ; +#endif +#ifdef DO_VALUES + /* sum the entry */ + Rx [pj] += Rx [p] ; +#ifdef COMPLEX + Rz [pj] += Rz [p] ; +#endif +#endif + } + else + { + /* keep the entry */ + /* also keep track in W[j] of position of a_ij for case above */ + W [j] = pdest ; +#ifdef DO_MAP + Map2 [p] = pdest ; +#endif + /* no need to move the entry if pdest is equal to p */ + if (pdest != p) + { + Rj [pdest] = j ; +#ifdef DO_VALUES + Rx [pdest] = Rx [p] ; +#ifdef COMPLEX + Rz [pdest] = Rz [p] ; +#endif +#endif + } + pdest++ ; + } + } + RowCount [i] = pdest - p1 ; + } + + /* done using W for position of a_ij ] */ + + /* ---------------------------------------------------------------------- */ + /* merge Map and Map2 into a single Map */ + /* ---------------------------------------------------------------------- */ + +#ifdef DO_MAP + if (duplicates) + { + for (k = 0 ; k < nz ; k++) + { + Map [k] = Map2 [Map [k]] ; + } + } +#ifndef NDEBUG + else + { + /* no duplicates, so no need to recompute Map */ + for (k = 0 ; k < nz ; k++) + { + ASSERT (Map2 [k] == k) ; + } + } + for (k = 0 ; k < nz ; k++) + { + /* make sure that kth triplet is mapped correctly */ + p = Map [k] ; + DEBUG1 (("Second row map: Map ["ID"] = "ID"\n", k, p)) ; + i = Ti [k] ; + j = Tj [k] ; + ASSERT (j == Rj [p]) ; + ASSERT (Rp [i] <= p && p < Rp [i+1]) ; + } +#endif +#endif + + /* now the kth triplet maps to p = Map [k], and thus to Rj/Rx [p] */ + + /* ---------------------------------------------------------------------- */ + /* count the entries in each column */ + /* ---------------------------------------------------------------------- */ + + /* [ use W as work space for column counts of A */ + for (j = 0 ; j < n_col ; j++) + { + W [j] = 0 ; + } + + for (i = 0 ; i < n_row ; i++) + { + for (p = Rp [i] ; p < Rp [i] + RowCount [i] ; p++) + { + j = Rj [p] ; + ASSERT (j >= 0 && j < n_col) ; + W [j]++ ; + } + } + + /* ---------------------------------------------------------------------- */ + /* create the column pointers */ + /* ---------------------------------------------------------------------- */ + + Ap [0] = 0 ; + for (j = 0 ; j < n_col ; j++) + { + Ap [j+1] = Ap [j] + W [j] ; + } + /* done using W as workspace for column counts of A ] */ + + for (j = 0 ; j < n_col ; j++) + { + W [j] = Ap [j] ; + } + + /* ---------------------------------------------------------------------- */ + /* construct the column form */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i < n_row ; i++) + { + for (p = Rp [i] ; p < Rp [i] + RowCount [i] ; p++) + { + cp = W [Rj [p]]++ ; +#ifdef DO_MAP + Map2 [p] = cp ; +#endif + Ai [cp] = i ; +#ifdef DO_VALUES + Ax [cp] = Rx [p] ; +#ifdef COMPLEX + Az [cp] = Rz [p] ; +#endif +#endif + } + } + + /* ---------------------------------------------------------------------- */ + /* merge Map and Map2 into a single Map */ + /* ---------------------------------------------------------------------- */ + +#ifdef DO_MAP + for (k = 0 ; k < nz ; k++) + { + Map [k] = Map2 [Map [k]] ; + } +#endif + + /* now the kth triplet maps to p = Map [k], and thus to Ai/Ax [p] */ + +#ifndef NDEBUG + for (j = 0 ; j < n_col ; j++) + { + ASSERT (W [j] == Ap [j+1]) ; + } + + UMF_dump_col_matrix ( +#ifdef DO_VALUES + Ax, +#ifdef COMPLEX + Az, +#endif +#else + (double *) NULL, +#ifdef COMPLEX + (double *) NULL, +#endif +#endif + Ai, Ap, n_row, n_col, nz) ; + +#ifdef DO_MAP + for (k = 0 ; k < nz ; k++) + { + /* make sure that kth triplet is mapped correctly */ + p = Map [k] ; + DEBUG1 (("Col map: Map ["ID"] = "ID"\t", k, p)) ; + i = Ti [k] ; + j = Tj [k] ; + ASSERT (i == Ai [p]) ; + DEBUG1 ((" i "ID" j "ID" Ap[j] "ID" p "ID" Ap[j+1] "ID"\n", + i, j, Ap [j], p, Ap [j+1])) ; + ASSERT (Ap [j] <= p && p < Ap [j+1]) ; + } +#endif +#endif + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_triplet.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_triplet.h new file mode 100644 index 0000000000..73704e6593 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_triplet.h @@ -0,0 +1,86 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_triplet_map_x +( + Int n_row, + Int n_col, + Int nz, + const Int Ti [ ], + const Int Tj [ ], + Int Ap [ ], + Int Ai [ ], + Int Rp [ ], + Int Rj [ ], + Int W [ ], + Int RowCount [ ] + , const double Tx [ ] + , double Ax [ ] + , double Rx [ ] +#ifdef COMPLEX + , const double Tz [ ] + , double Az [ ] + , double Rz [ ] +#endif + , Int Map [ ] + , Int Map2 [ ] +) ; + +GLOBAL Int UMF_triplet_map_nox +( + Int n_row, + Int n_col, + Int nz, + const Int Ti [ ], + const Int Tj [ ], + Int Ap [ ], + Int Ai [ ], + Int Rp [ ], + Int Rj [ ], + Int W [ ], + Int RowCount [ ] + , Int Map [ ] + , Int Map2 [ ] +) ; + +GLOBAL Int UMF_triplet_nomap_x +( + Int n_row, + Int n_col, + Int nz, + const Int Ti [ ], + const Int Tj [ ], + Int Ap [ ], + Int Ai [ ], + Int Rp [ ], + Int Rj [ ], + Int W [ ], + Int RowCount [ ] + , const double Tx [ ] + , double Ax [ ] + , double Rx [ ] +#ifdef COMPLEX + , const double Tz [ ] + , double Az [ ] + , double Rz [ ] +#endif +) ; + +GLOBAL Int UMF_triplet_nomap_nox +( + Int n_row, + Int n_col, + Int nz, + const Int Ti [ ], + const Int Tj [ ], + Int Ap [ ], + Int Ai [ ], + Int Rp [ ], + Int Rj [ ], + Int W [ ], + Int RowCount [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_tuple_lengths.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_tuple_lengths.c new file mode 100644 index 0000000000..6843428d1d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_tuple_lengths.c @@ -0,0 +1,136 @@ +/* ========================================================================== */ +/* === UMF_tuple_lengths ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Determine the tuple list lengths, and the amount of memory required for */ +/* them. Return the amount of memory needed to store all the tuples. */ +/* This routine assumes that the tuple lists themselves are either already */ +/* deallocated, or will be shortly (so Row[ ].tlen and Col[ ].tlen are */ +/* overwritten) */ + +#include "umf_internal.h" + +GLOBAL Int UMF_tuple_lengths /* return memory usage */ +( + NumericType *Numeric, + WorkType *Work, + double *p_dusage /* output argument */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int e, nrows, ncols, nel, i, *Rows, *Cols, row, col, n_row, n_col, *E, + *Row_degree, *Row_tlen, *Col_degree, *Col_tlen, usage, n1 ; + double dusage ; + Element *ep ; + Unit *p ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + E = Work->E ; + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro only */ + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro only */ + Row_tlen = Numeric->Uilen ; + Col_tlen = Numeric->Lilen ; + n_row = Work->n_row ; + n_col = Work->n_col ; + n1 = Work->n1 ; + nel = Work->nel ; + + DEBUG3 (("TUPLE_LENGTHS: n_row "ID" n_col "ID" nel "ID"\n", + n_row, n_col, nel)) ; + ASSERT (nel < Work->elen) ; + + /* tuple list lengths already initialized to zero */ + + /* ---------------------------------------------------------------------- */ + /* scan each element: count tuple list lengths (include element 0) */ + /* ---------------------------------------------------------------------- */ + + for (e = 1 ; e <= nel ; e++) /* for all elements, in any order */ + { + if (E [e]) + { +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + p = Numeric->Memory + E [e] ; + GET_ELEMENT_PATTERN (ep, p, Cols, Rows, ncols) ; + nrows = ep->nrows ; + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + ASSERT (row == EMPTY || (row >= n1 && row < n_row)) ; + if (row >= n1) + { + ASSERT (NON_PIVOTAL_ROW (row)) ; + Row_tlen [row] ++ ; + } + } + for (i = 0 ; i < ncols ; i++) + { + col = Cols [i] ; + ASSERT (col == EMPTY || (col >= n1 && col < n_col)) ; + if (col >= n1) + { + ASSERT (NON_PIVOTAL_COL (col)) ; + Col_tlen [col] ++ ; + } + } + } + } + + /* note: tuple lengths are now modified, but the tuple lists are not */ + /* updated to reflect that fact. */ + + /* ---------------------------------------------------------------------- */ + /* determine the required memory to hold all the tuple lists */ + /* ---------------------------------------------------------------------- */ + + DEBUG0 (("UMF_build_tuples_usage\n")) ; + + usage = 0 ; + dusage = 0 ; + + ASSERT (Col_tlen && Col_degree) ; + + for (col = n1 ; col < n_col ; col++) + { + if (NON_PIVOTAL_COL (col)) + { + usage += 1 + UNITS (Tuple, TUPLES (Col_tlen [col])) ; + dusage += 1 + DUNITS (Tuple, TUPLES (Col_tlen [col])) ; + DEBUG0 ((" col: "ID" tlen "ID" usage so far: "ID"\n", + col, Col_tlen [col], usage)) ; + } + } + + ASSERT (Row_tlen && Row_degree) ; + + for (row = n1 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row)) + { + usage += 1 + UNITS (Tuple, TUPLES (Row_tlen [row])) ; + dusage += 1 + DUNITS (Tuple, TUPLES (Row_tlen [row])) ; + DEBUG0 ((" row: "ID" tlen "ID" usage so far: "ID"\n", + row, Row_tlen [row], usage)) ; + } + } + + DEBUG0 (("UMF_build_tuples_usage "ID" %g\n", usage, dusage)) ; + + *p_dusage = dusage ; + return (usage) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_tuple_lengths.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_tuple_lengths.h new file mode 100644 index 0000000000..6d1560b9b2 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_tuple_lengths.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_tuple_lengths +( + NumericType *Numeric, + WorkType *Work, + double *dusage +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_usolve.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_usolve.c new file mode 100644 index 0000000000..c292eb4d25 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_usolve.c @@ -0,0 +1,226 @@ +/* ========================================================================== */ +/* === UMF_usolve =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* solves Ux = b, where U is the upper triangular factor of a matrix. */ +/* B is overwritten with the solution X. */ +/* Returns the floating point operation count */ + +#include "umf_internal.h" + +GLOBAL double UMF_usolve +( + NumericType *Numeric, + Entry X [ ], /* b on input, solution x on output */ + Int Pattern [ ] /* a work array of size n */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int k, deg, j, *ip, col, *Upos, *Uilen, pos, + *Uip, n, ulen, up, newUchain, npiv, n1, *Ui ; + Entry *xp, xk, *D, *Uval ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + if (Numeric->n_row != Numeric->n_col) return (0.) ; + n = Numeric->n_row ; + npiv = Numeric->npiv ; + Upos = Numeric->Upos ; + Uilen = Numeric->Uilen ; + Uip = Numeric->Uip ; + D = Numeric->D ; + n1 = Numeric->n1 ; + +#ifndef NDEBUG + DEBUG4 (("Usolve start: npiv = "ID" n = "ID"\n", npiv, n)) ; + for (j = 0 ; j < n ; j++) + { + DEBUG4 (("Usolve start "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* singular case */ + /* ---------------------------------------------------------------------- */ + +#ifndef NO_DIVIDE_BY_ZERO + /* handle the singular part of D, up to just before the last pivot */ + for (k = n-1 ; k >= npiv ; k--) + { + /* This is an *** intentional *** divide-by-zero, to get Inf or Nan, + * as appropriate. It is not a bug. */ + ASSERT (IS_ZERO (D [k])) ; + xk = X [k] ; + /* X [k] = xk / D [k] ; */ + DIV (X [k], xk, D [k]) ; + } +#else + /* Do not divide by zero */ +#endif + + deg = Numeric->ulen ; + if (deg > 0) + { + /* :: make last pivot row of U (singular matrices only) :: */ + for (j = 0 ; j < deg ; j++) + { + DEBUG1 (("Last row of U: j="ID"\n", j)) ; + DEBUG1 (("Last row of U: Upattern[j]="ID"\n", + Numeric->Upattern [j]) ); + Pattern [j] = Numeric->Upattern [j] ; + } + } + + /* ---------------------------------------------------------------------- */ + /* nonsingletons */ + /* ---------------------------------------------------------------------- */ + + for (k = npiv-1 ; k >= n1 ; k--) + { + + /* ------------------------------------------------------------------ */ + /* use row k of U */ + /* ------------------------------------------------------------------ */ + + up = Uip [k] ; + ulen = Uilen [k] ; + newUchain = (up < 0) ; + if (newUchain) + { + up = -up ; + xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ; + } + else + { + xp = (Entry *) (Numeric->Memory + up) ; + } + + xk = X [k] ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" k "ID" col "ID" value", k, Pattern [j])) ; + EDEBUG4 (*xp) ; + DEBUG4 (("\n")) ; + /* xk -= X [Pattern [j]] * (*xp) ; */ + MULT_SUB (xk, X [Pattern [j]], *xp) ; + xp++ ; + } + +#ifndef NO_DIVIDE_BY_ZERO + /* Go ahead and divide by zero if D [k] is zero */ + /* X [k] = xk / D [k] ; */ + DIV (X [k], xk, D [k]) ; +#else + /* Do not divide by zero */ + if (IS_NONZERO (D [k])) + { + /* X [k] = xk / D [k] ; */ + DIV (X [k], xk, D [k]) ; + } +#endif + + /* ------------------------------------------------------------------ */ + /* make row k-1 of U in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + if (k == n1) break ; + + if (newUchain) + { + /* next row is a new Uchain */ + deg = ulen ; + ASSERT (IMPLIES (k == 0, deg == 0)) ; + DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ; + ip = (Int *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = *ip++ ; + DEBUG4 ((" k "ID" col "ID"\n", k-1, col)) ; + ASSERT (k <= col) ; + Pattern [j] = col ; + } + } + else + { + deg -= ulen ; + DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k, deg)) ; + ASSERT (deg >= 0) ; + pos = Upos [k] ; + if (pos != EMPTY) + { + /* add the pivot column */ + DEBUG4 (("k "ID" add pivot entry at pos "ID"\n", k, pos)) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* singletons */ + /* ---------------------------------------------------------------------- */ + + for (k = n1 - 1 ; k >= 0 ; k--) + { + deg = Uilen [k] ; + xk = X [k] ; + DEBUG4 (("Singleton k "ID"\n", k)) ; + if (deg > 0) + { + up = Uip [k] ; + Ui = (Int *) (Numeric->Memory + up) ; + up += UNITS (Int, deg) ; + Uval = (Entry *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" k "ID" col "ID" value", k, Ui [j])) ; + EDEBUG4 (Uval [j]) ; + DEBUG4 (("\n")) ; + /* xk -= X [Ui [j]] * Uval [j] ; */ + ASSERT (Ui [j] >= 0 && Ui [j] < n) ; + MULT_SUB (xk, X [Ui [j]], Uval [j]) ; + } + } + +#ifndef NO_DIVIDE_BY_ZERO + /* Go ahead and divide by zero if D [k] is zero */ + /* X [k] = xk / D [k] ; */ + DIV (X [k], xk, D [k]) ; +#else + /* Do not divide by zero */ + if (IS_NONZERO (D [k])) + { + /* X [k] = xk / D [k] ; */ + DIV (X [k], xk, D [k]) ; + } +#endif + + } + +#ifndef NDEBUG + for (j = 0 ; j < n ; j++) + { + DEBUG4 (("Usolve done "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } + DEBUG4 (("Usolve done.\n")) ; +#endif + + return (DIV_FLOPS * ((double) n) + MULTSUB_FLOPS * ((double) Numeric->unz)); +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_usolve.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_usolve.h new file mode 100644 index 0000000000..c546111380 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_usolve.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL double UMF_usolve +( + NumericType *Numeric, + Entry X [ ], + Int Pattern [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_utsolve.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_utsolve.c new file mode 100644 index 0000000000..bd7d08e57d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_utsolve.c @@ -0,0 +1,331 @@ +/* ========================================================================== */ +/* === UMF_utsolve ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* solves U'x = b or U.'x=b, where U is the upper triangular factor of a */ +/* matrix. B is overwritten with the solution X. */ +/* Returns the floating point operation count */ + +#include "umf_internal.h" + +GLOBAL double +#ifdef CONJUGATE_SOLVE +UMF_uhsolve /* solve U'x=b (complex conjugate transpose) */ +#else +UMF_utsolve /* solve U.'x=b (array transpose) */ +#endif +( + NumericType *Numeric, + Entry X [ ], /* b on input, solution x on output */ + Int Pattern [ ] /* a work array of size n */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int k, deg, j, *ip, col, *Upos, *Uilen, kstart, kend, up, + *Uip, n, uhead, ulen, pos, npiv, n1, *Ui ; + Entry *xp, xk, *D, *Uval ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + if (Numeric->n_row != Numeric->n_col) return (0.) ; + n = Numeric->n_row ; + npiv = Numeric->npiv ; + Upos = Numeric->Upos ; + Uilen = Numeric->Uilen ; + Uip = Numeric->Uip ; + D = Numeric->D ; + kend = 0 ; + n1 = Numeric->n1 ; + +#ifndef NDEBUG + DEBUG4 (("Utsolve start: npiv "ID" n "ID"\n", npiv, n)) ; + for (j = 0 ; j < n ; j++) + { + DEBUG4 (("Utsolve start "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* singletons */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < n1 ; k++) + { + DEBUG4 (("Singleton k "ID"\n", k)) ; + +#ifndef NO_DIVIDE_BY_ZERO + /* Go ahead and divide by zero if D [k] is zero. */ +#ifdef CONJUGATE_SOLVE + /* xk = X [k] / conjugate (D [k]) ; */ + DIV_CONJ (xk, X [k], D [k]) ; +#else + /* xk = X [k] / D [k] ; */ + DIV (xk, X [k], D [k]) ; +#endif +#else + /* Do not divide by zero */ + if (IS_NONZERO (D [k])) + { +#ifdef CONJUGATE_SOLVE + /* xk = X [k] / conjugate (D [k]) ; */ + DIV_CONJ (xk, X [k], D [k]) ; +#else + /* xk = X [k] / D [k] ; */ + DIV (xk, X [k], D [k]) ; +#endif + } +#endif + + X [k] = xk ; + deg = Uilen [k] ; + if (deg > 0 && IS_NONZERO (xk)) + { + up = Uip [k] ; + Ui = (Int *) (Numeric->Memory + up) ; + up += UNITS (Int, deg) ; + Uval = (Entry *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" k "ID" col "ID" value", k, Ui [j])) ; + EDEBUG4 (Uval [j]) ; + DEBUG4 (("\n")) ; +#ifdef CONJUGATE_SOLVE + /* X [Ui [j]] -= xk * conjugate (Uval [j]) ; */ + MULT_SUB_CONJ (X [Ui [j]], xk, Uval [j]) ; +#else + /* X [Ui [j]] -= xk * Uval [j] ; */ + MULT_SUB (X [Ui [j]], xk, Uval [j]) ; +#endif + } + } + } + + /* ---------------------------------------------------------------------- */ + /* nonsingletons */ + /* ---------------------------------------------------------------------- */ + + for (kstart = n1 ; kstart < npiv ; kstart = kend + 1) + { + + /* ------------------------------------------------------------------ */ + /* find the end of this Uchain */ + /* ------------------------------------------------------------------ */ + + DEBUG4 (("kstart "ID" kend "ID"\n", kstart, kend)) ; + /* for (kend = kstart ; kend < npiv && Uip [kend+1] > 0 ; kend++) ; */ + kend = kstart ; + while (kend < npiv && Uip [kend+1] > 0) + { + kend++ ; + } + + /* ------------------------------------------------------------------ */ + /* scan the whole Uchain to find the pattern of the first row of U */ + /* ------------------------------------------------------------------ */ + + k = kend+1 ; + DEBUG4 (("\nKend "ID" K "ID"\n", kend, k)) ; + + /* ------------------------------------------------------------------ */ + /* start with last row in Uchain of U in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + if (k == npiv) + { + deg = Numeric->ulen ; + if (deg > 0) + { + /* :: make last pivot row of U (singular matrices only) :: */ + for (j = 0 ; j < deg ; j++) + { + Pattern [j] = Numeric->Upattern [j] ; + } + } + } + else + { + ASSERT (k >= 0 && k < npiv) ; + up = -Uip [k] ; + ASSERT (up > 0) ; + deg = Uilen [k] ; + DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ; + ip = (Int *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = *ip++ ; + DEBUG4 ((" k "ID" col "ID"\n", k-1, col)) ; + ASSERT (k <= col) ; + Pattern [j] = col ; + } + } + + /* empty the stack at the bottom of Pattern */ + uhead = n ; + + for (k = kend ; k > kstart ; k--) + { + /* Pattern [0..deg-1] is the pattern of row k of U */ + + /* -------------------------------------------------------------- */ + /* make row k-1 of U in Pattern [0..deg-1] */ + /* -------------------------------------------------------------- */ + + ASSERT (k >= 0 && k < npiv) ; + ulen = Uilen [k] ; + /* delete, and push on the stack */ + for (j = 0 ; j < ulen ; j++) + { + ASSERT (uhead >= deg) ; + Pattern [--uhead] = Pattern [--deg] ; + } + DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k, deg)) ; + ASSERT (deg >= 0) ; + + pos = Upos [k] ; + if (pos != EMPTY) + { + /* add the pivot column */ + DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + + /* Pattern [0..deg-1] is now the pattern of the first row in Uchain */ + + /* ------------------------------------------------------------------ */ + /* solve using this Uchain, in reverse order */ + /* ------------------------------------------------------------------ */ + + DEBUG4 (("Unwinding Uchain\n")) ; + for (k = kstart ; k <= kend ; k++) + { + + /* -------------------------------------------------------------- */ + /* construct row k */ + /* -------------------------------------------------------------- */ + + ASSERT (k >= 0 && k < npiv) ; + pos = Upos [k] ; + if (pos != EMPTY) + { + /* remove the pivot column */ + DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ; + ASSERT (k > kstart) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + up = Uip [k] ; + ulen = Uilen [k] ; + if (k > kstart) + { + /* concatenate the deleted pattern; pop from the stack */ + for (j = 0 ; j < ulen ; j++) + { + ASSERT (deg <= uhead && uhead < n) ; + Pattern [deg++] = Pattern [uhead++] ; + } + DEBUG4 (("middle of chain, row of U "ID" deg "ID"\n", k, deg)) ; + ASSERT (deg >= 0) ; + } + + /* -------------------------------------------------------------- */ + /* use row k of U */ + /* -------------------------------------------------------------- */ + +#ifndef NO_DIVIDE_BY_ZERO + /* Go ahead and divide by zero if D [k] is zero. */ +#ifdef CONJUGATE_SOLVE + /* xk = X [k] / conjugate (D [k]) ; */ + DIV_CONJ (xk, X [k], D [k]) ; +#else + /* xk = X [k] / D [k] ; */ + DIV (xk, X [k], D [k]) ; +#endif +#else + /* Do not divide by zero */ + if (IS_NONZERO (D [k])) + { +#ifdef CONJUGATE_SOLVE + /* xk = X [k] / conjugate (D [k]) ; */ + DIV_CONJ (xk, X [k], D [k]) ; +#else + /* xk = X [k] / D [k] ; */ + DIV (xk, X [k], D [k]) ; +#endif + } +#endif + + X [k] = xk ; + if (IS_NONZERO (xk)) + { + if (k == kstart) + { + up = -up ; + xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ; + } + else + { + xp = (Entry *) (Numeric->Memory + up) ; + } + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" k "ID" col "ID" value", k, Pattern [j])) ; + EDEBUG4 (*xp) ; + DEBUG4 (("\n")) ; +#ifdef CONJUGATE_SOLVE + /* X [Pattern [j]] -= xk * conjugate (*xp) ; */ + MULT_SUB_CONJ (X [Pattern [j]], xk, *xp) ; +#else + /* X [Pattern [j]] -= xk * (*xp) ; */ + MULT_SUB (X [Pattern [j]], xk, *xp) ; +#endif + xp++ ; + } + } + } + ASSERT (uhead == n) ; + } + +#ifndef NO_DIVIDE_BY_ZERO + for (k = npiv ; k < n ; k++) + { + /* This is an *** intentional *** divide-by-zero, to get Inf or Nan, + * as appropriate. It is not a bug. */ + ASSERT (IS_ZERO (D [k])) ; + /* For conjugate solve, D [k] == conjugate (D [k]), in this case */ + /* xk = X [k] / D [k] ; */ + DIV (xk, X [k], D [k]) ; + X [k] = xk ; + } +#endif + +#ifndef NDEBUG + for (j = 0 ; j < n ; j++) + { + DEBUG4 (("Utsolve done "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } + DEBUG4 (("Utsolve done.\n")) ; +#endif + + return (DIV_FLOPS * ((double) n) + MULTSUB_FLOPS * ((double) Numeric->unz)); +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_utsolve.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_utsolve.h new file mode 100644 index 0000000000..86ccb40140 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_utsolve.h @@ -0,0 +1,21 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL double UMF_utsolve +( + NumericType *Numeric, + Entry X [ ], + Int Pattern [ ] +) ; + + +GLOBAL double UMF_uhsolve +( + NumericType *Numeric, + Entry X [ ], + Int Pattern [ ] +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_numeric.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_numeric.c new file mode 100644 index 0000000000..58da8ac94b --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_numeric.c @@ -0,0 +1,47 @@ +/* ========================================================================== */ +/* === UMF_valid_numeric ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Returns TRUE if the Numeric object is valid, FALSE otherwise. */ +/* Does not check everything. UMFPACK_report_numeric checks more. */ + +#include "umf_internal.h" + +GLOBAL Int UMF_valid_numeric +( + NumericType *Numeric +) +{ + /* This routine does not check the contents of the individual arrays, so */ + /* it can miss some errors. All it checks for is the presence of the */ + /* arrays, and the Numeric "valid" entry. */ + + if (!Numeric) + { + return (FALSE) ; + } + + if (Numeric->valid != NUMERIC_VALID) + { + /* Numeric does not point to a NumericType object */ + return (FALSE) ; + } + + if (Numeric->n_row <= 0 || Numeric->n_col <= 0 || !Numeric->D || + !Numeric->Rperm || !Numeric->Cperm || + !Numeric->Lpos || !Numeric->Upos || + !Numeric->Lilen || !Numeric->Uilen || !Numeric->Lip || !Numeric->Uip || + !Numeric->Memory || (Numeric->ulen > 0 && !Numeric->Upattern)) + { + return (FALSE) ; + } + + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_numeric.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_numeric.h new file mode 100644 index 0000000000..11895fd12e --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_numeric.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_valid_numeric +( + NumericType *Numeric +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_symbolic.c b/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_symbolic.c new file mode 100644 index 0000000000..1acc6ac5f1 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_symbolic.c @@ -0,0 +1,48 @@ +/* ========================================================================== */ +/* === UMF_valid_symbolic =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" + +/* Returns TRUE if the Symbolic object is valid, FALSE otherwise. */ +/* The UMFPACK_report_symbolic routine does a more thorough check. */ + +GLOBAL Int UMF_valid_symbolic +( + SymbolicType *Symbolic +) +{ + /* This routine does not check the contents of the individual arrays, so */ + /* it can miss some errors. All it checks for is the presence of the */ + /* arrays, and the Symbolic "valid" entry. */ + + if (!Symbolic) + { + return (FALSE) ; + } + + if (Symbolic->valid != SYMBOLIC_VALID) + { + /* Symbolic does not point to a SymbolicType object */ + return (FALSE) ; + } + + if (!Symbolic->Cperm_init || !Symbolic->Rperm_init || + !Symbolic->Front_npivcol || !Symbolic->Front_1strow || + !Symbolic->Front_leftmostdesc || + !Symbolic->Front_parent || !Symbolic->Chain_start || + !Symbolic->Chain_maxrows || !Symbolic->Chain_maxcols || + Symbolic->n_row <= 0 || Symbolic->n_col <= 0) + { + return (FALSE) ; + } + + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_symbolic.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_symbolic.h new file mode 100644 index 0000000000..5cada3c146 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_valid_symbolic.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_valid_symbolic +( + SymbolicType *Symbolic +) ; diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umf_version.h b/deal.II/contrib/umfpack/UMFPACK/Source/umf_version.h new file mode 100644 index 0000000000..b3c4df09ec --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umf_version.h @@ -0,0 +1,989 @@ +/* ========================================================================== */ +/* === umf_version.h ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Define routine names, depending on version being compiled. + + DINT: double precision, int's as integers + DLONG: double precision, long's as integers + ZLONG: complex double precision, long's as integers + ZINT: complex double precision, int's as integers +*/ + +/* Set DINT as the default, if nothing is defined */ +#if !defined (DLONG) && !defined (DINT) && !defined (ZLONG) && !defined (ZINT) +#define DINT +#endif + +/* Determine if this is a real or complex version */ +#if defined (ZLONG) || defined (ZINT) +#define COMPLEX +#endif + +/* -------------------------------------------------------------------------- */ +/* integer type (Int is int or long) now defined in amd_internal.h */ +/* -------------------------------------------------------------------------- */ + +#if defined (DLONG) || defined (ZLONG) +#define LONG_INTEGER +#endif + +/* -------------------------------------------------------------------------- */ +/* Numerical relop macros for correctly handling the NaN case */ +/* -------------------------------------------------------------------------- */ + +/* +SCALAR_IS_NAN(x): + True if x is NaN. False otherwise. The commonly-existing isnan(x) + function could be used, but it's not in Kernighan & Ritchie 2nd edition + (ANSI C). It may appear in , but I'm not certain about + portability. The expression x != x is true if and only if x is NaN, + according to the IEEE 754 floating-point standard. + +SCALAR_IS_ZERO(x): + True if x is zero. False if x is nonzero, NaN, or +/- Inf. + This is (x == 0) if the compiler is IEEE 754 compliant. + +SCALAR_IS_NONZERO(x): + True if x is nonzero, NaN, or +/- Inf. False if x zero. + This is (x != 0) if the compiler is IEEE 754 compliant. + +SCALAR_IS_LTZERO(x): + True if x is < zero or -Inf. False if x is >= 0, NaN, or +Inf. + This is (x < 0) if the compiler is IEEE 754 compliant. +*/ + +#if defined (MATHWORKS) + +/* The MathWorks has their own macros in util.h that handle NaN's properly. */ +#define SCALAR_IS_NAN(x) (utIsNaN (x)) +#define SCALAR_IS_ZERO(x) (utEQZero (x)) +#define SCALAR_IS_NONZERO(x) (utNEZero (x)) +#define SCALAR_IS_LTZERO(x) (utLTZero (x)) + +#elif defined (UMF_WINDOWS) + +/* Yes, this is exceedingly ugly. Blame Microsoft, which hopelessly */ +/* violates the IEEE 754 floating-point standard in a bizarre way. */ +/* If you're using an IEEE 754-compliant compiler, then x != x is true */ +/* iff x is NaN. For Microsoft, (x < x) is true iff x is NaN. */ +/* So either way, this macro safely detects a NaN. */ +#define SCALAR_IS_NAN(x) (((x) != (x)) || (((x) < (x)))) +#define SCALAR_IS_ZERO(x) (((x) == 0.) && !SCALAR_IS_NAN(x)) +#define SCALAR_IS_NONZERO(x) (((x) != 0.) || SCALAR_IS_NAN(x)) +#define SCALAR_IS_LTZERO(x) (((x) < 0.) && !SCALAR_IS_NAN(x)) + +#else + +/* These all work properly, according to the IEEE 754 standard ... except on */ +/* a PC with windows. Works fine in Linux on the same PC... */ +#define SCALAR_IS_NAN(x) ((x) != (x)) +#define SCALAR_IS_ZERO(x) ((x) == 0.) +#define SCALAR_IS_NONZERO(x) ((x) != 0.) +#define SCALAR_IS_LTZERO(x) ((x) < 0.) + +#endif + +/* scalar absolute value macro. If x is NaN, the result is NaN: */ +#define SCALAR_ABS(x) ((SCALAR_IS_LTZERO (x)) ? -(x) : (x)) + +/* true if an integer (stored in double x) would overflow (or if x is NaN) */ +#define INT_OVERFLOW(x) ((!((x) * (1.0+1e-8) <= (double) Int_MAX)) \ + || SCALAR_IS_NAN (x)) + +/* print a scalar (avoid printing "-0" for negative zero). */ +#define PRINT_SCALAR(a) \ +{ \ + if (SCALAR_IS_NONZERO (a)) \ + { \ + PRINTF ((" (%g)", (a))) ; \ + } \ + else \ + { \ + PRINTF ((" (0)")) ; \ + } \ +} + +/* -------------------------------------------------------------------------- */ +/* Real floating-point arithmetic */ +/* -------------------------------------------------------------------------- */ + +#ifndef COMPLEX + +#define Entry double + +#define REAL_COMPONENT(c) (c) +#define IMAG_COMPONENT(c) (0.) +#define ASSIGN(c,s1,s2) { (c) = (s1) ; } +#define CLEAR(c) { (c) = 0. ; } +#define CLEAR_AND_INCREMENT(p) { *p++ = 0. ; } +#define IS_NAN(a) SCALAR_IS_NAN (a) +#define IS_ZERO(a) SCALAR_IS_ZERO (a) +#define IS_NONZERO(a) SCALAR_IS_NONZERO (a) +#define SCALE_DIV(c,s) { (c) /= (s) ; } +#ifndef NRECIPROCAL +#define SCALE_RECIP(c,s) { (c) *= (s) ; } +#endif +#define ASSEMBLE(c,a) { (c) += (a) ; } +#define ASSEMBLE_AND_INCREMENT(c,p) { (c) += *p++ ; } +#define DECREMENT(c,a) { (c) -= (a) ; } +#define MULT(c,a,b) { (c) = (a) * (b) ; } +#define MULT_CONJ(c,a,b) { (c) = (a) * (b) ; } +#define MULT_SUB(c,a,b) { (c) -= (a) * (b) ; } +#define MULT_SUB_CONJ(c,a,b) { (c) -= (a) * (b) ; } +#define DIV(c,a,b) { (c) = (a) / (b) ; } +#define RECIPROCAL(c) { (c) = 1.0 / (c) ; } +#define DIV_CONJ(c,a,b) { (c) = (a) / (b) ; } +#define APPROX_ABS(s,a) { (s) = SCALAR_ABS (a) ; } +#define ABS(s,a) { (s) = SCALAR_ABS (a) ; } +#define PRINT_ENTRY(a) PRINT_SCALAR (a) + +/* for flop counts */ +#define MULTSUB_FLOPS 2. /* c -= a*b */ +#define DIV_FLOPS 1. /* c = a/b */ +#define ABS_FLOPS 0. /* c = abs (a) */ +#define ASSEMBLE_FLOPS 1. /* c += a */ +#define DECREMENT_FLOPS 1. /* c -= a */ +#define MULT_FLOPS 1. /* c = a*b */ +#define SCALE_FLOPS 1. /* c = a/s */ + +#else + +/* -------------------------------------------------------------------------- */ +/* Complex floating-point arithmetic */ +/* -------------------------------------------------------------------------- */ + +/* + Note: An alternative to this DoubleComplex type would be to use a + struct { double r ; double i ; }. The problem with that method + (used by the Sun Performance Library, for example) is that ANSI C provides + no guarantee about the layout of a struct. It is possible that the sizeof + the struct above would be greater than 2 * sizeof (double). This would + mean that the complex BLAS could not be used. The method used here avoids + that possibility. ANSI C *does* guarantee that an array of structs has + the same size as n times the size of one struct. + + The ANSI C99 version of the C language includes a "double _Complex" type. + It should be possible in that case to do the following: + + #define Entry double _Complex + + and remove the DoubleComplex struct. The macros, below, could then be + replaced with instrinsic operators. Note that the #define Real and + #define Imag should also be removed (they only appear in this file). + + For the MULT, MULT_SUB, MULT_SUB_CONJ, and MULT_CONJ macros, + the output argument c cannot be the same as any input argument. + +*/ + +typedef struct +{ + double component [2] ; /* real and imaginary parts */ + +} DoubleComplex ; + +#define Entry DoubleComplex +#define Real component [0] +#define Imag component [1] + +/* for flop counts */ +#define MULTSUB_FLOPS 8. /* c -= a*b */ +#define DIV_FLOPS 9. /* c = a/b */ +#define ABS_FLOPS 6. /* c = abs (a), count sqrt as one flop */ +#define ASSEMBLE_FLOPS 2. /* c += a */ +#define DECREMENT_FLOPS 2. /* c -= a */ +#define MULT_FLOPS 6. /* c = a*b */ +#define SCALE_FLOPS 2. /* c = a/s or c = a*s */ + +/* -------------------------------------------------------------------------- */ + +/* real part of c */ +#define REAL_COMPONENT(c) ((c).Real) + +/* -------------------------------------------------------------------------- */ + +/* imag part of c */ +#define IMAG_COMPONENT(c) ((c).Imag) + +/* -------------------------------------------------------------------------- */ + +/* c = (s1) + (s2)i */ +#define ASSIGN(c,s1,s2) \ +{ \ + (c).Real = (s1) ; \ + (c).Imag = (s2) ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c = 0 */ +#define CLEAR(c) \ +{ \ + (c).Real = 0. ; \ + (c).Imag = 0. ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* *p++ = 0 */ +#define CLEAR_AND_INCREMENT(p) \ +{ \ + p->Real = 0. ; \ + p->Imag = 0. ; \ + p++ ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* True if a == 0 */ +#define IS_ZERO(a) \ + (SCALAR_IS_ZERO ((a).Real) && SCALAR_IS_ZERO ((a).Imag)) + +/* -------------------------------------------------------------------------- */ + +/* True if a is NaN */ +#define IS_NAN(a) \ + (SCALAR_IS_NAN ((a).Real) || SCALAR_IS_NAN ((a).Imag)) + +/* -------------------------------------------------------------------------- */ + +/* True if a != 0 */ +#define IS_NONZERO(a) \ + (SCALAR_IS_NONZERO ((a).Real) || SCALAR_IS_NONZERO ((a).Imag)) + +/* -------------------------------------------------------------------------- */ + +/* c /= s */ +#define SCALE_DIV(c,s) \ +{ \ + (c).Real /= (s) ; \ + (c).Imag /= (s) ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c *= s, where s is the reciprocal scale factor. Not used if + * NRECIPROCAL is defined at compile time. */ +#ifndef NRECIPROCAL +#define SCALE_RECIP(c,s) \ +{ \ + (c).Real *= (s) ; \ + (c).Imag *= (s) ; \ +} +#endif + +/* -------------------------------------------------------------------------- */ + +/* c += a */ +#define ASSEMBLE(c,a) \ +{ \ + (c).Real += (a).Real ; \ + (c).Imag += (a).Imag ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c += *p++ */ +#define ASSEMBLE_AND_INCREMENT(c,p) \ +{ \ + (c).Real += p->Real ; \ + (c).Imag += p->Imag ; \ + p++ ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c -= a */ +#define DECREMENT(c,a) \ +{ \ + (c).Real -= (a).Real ; \ + (c).Imag -= (a).Imag ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c = a*b, assert because c cannot be the same as a or b */ +#define MULT(c,a,b) \ +{ \ + ASSERT (&(c) != &(a) && &(c) != &(b)) ; \ + (c).Real = (a).Real * (b).Real - (a).Imag * (b).Imag ; \ + (c).Imag = (a).Imag * (b).Real + (a).Real * (b).Imag ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c = a*conjugate(b), assert because c cannot be the same as a or b */ +#define MULT_CONJ(c,a,b) \ +{ \ + ASSERT (&(c) != &(a) && &(c) != &(b)) ; \ + (c).Real = (a).Real * (b).Real + (a).Imag * (b).Imag ; \ + (c).Imag = (a).Imag * (b).Real - (a).Real * (b).Imag ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c -= a*b, assert because c cannot be the same as a or b */ +#define MULT_SUB(c,a,b) \ +{ \ + ASSERT (&(c) != &(a) && &(c) != &(b)) ; \ + (c).Real -= (a).Real * (b).Real - (a).Imag * (b).Imag ; \ + (c).Imag -= (a).Imag * (b).Real + (a).Real * (b).Imag ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c -= a*conjugate(b), assert because c cannot be the same as a or b */ +#define MULT_SUB_CONJ(c,a,b) \ +{ \ + ASSERT (&(c) != &(a) && &(c) != &(b)) ; \ + (c).Real -= (a).Real * (b).Real + (a).Imag * (b).Imag ; \ + (c).Imag -= (a).Imag * (b).Real - (a).Real * (b).Imag ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c = a/b, be careful to avoid underflow and overflow */ +#ifdef MATHWORKS +#define DIV(c,a,b) \ +{ \ + (void) utDivideComplex ((a).Real, (a).Imag, (b).Real, (b).Imag, \ + &((c).Real), &((c).Imag)) ; \ +} +#else +/* This uses ACM Algo 116, by R. L. Smith, 1962. */ +/* c can be the same variable as a or b. */ +/* Ignore NaN case for double relop br>=bi. */ +#define DIV(c,a,b) \ +{ \ + double r, den, ar, ai, br, bi ; \ + br = (b).Real ; \ + bi = (b).Imag ; \ + ar = (a).Real ; \ + ai = (a).Imag ; \ + if (SCALAR_ABS (br) >= SCALAR_ABS (bi)) \ + { \ + r = bi / br ; \ + den = br + r * bi ; \ + (c).Real = (ar + ai * r) / den ; \ + (c).Imag = (ai - ar * r) / den ; \ + } \ + else \ + { \ + r = br / bi ; \ + den = r * br + bi ; \ + (c).Real = (ar * r + ai) / den ; \ + (c).Imag = (ai * r - ar) / den ; \ + } \ +} +#endif + +/* -------------------------------------------------------------------------- */ + +/* c = 1/c, be careful to avoid underflow and overflow */ +/* Not used if MATHWORKS is defined. */ +/* This uses ACM Algo 116, by R. L. Smith, 1962. */ +/* Ignore NaN case for double relop cr>=ci. */ +#define RECIPROCAL(c) \ +{ \ + double r, den, cr, ci ; \ + cr = (c).Real ; \ + ci = (c).Imag ; \ + if (SCALAR_ABS (cr) >= SCALAR_ABS (ci)) \ + { \ + r = ci / cr ; \ + den = cr + r * ci ; \ + (c).Real = 1.0 / den ; \ + (c).Imag = - r / den ; \ + } \ + else \ + { \ + r = cr / ci ; \ + den = r * cr + ci ; \ + (c).Real = r / den ; \ + (c).Imag = - 1.0 / den ; \ + } \ +} + + +/* -------------------------------------------------------------------------- */ + +/* c = a/conjugate(b), be careful to avoid underflow and overflow */ +#ifdef MATHWORKS +#define DIV_CONJ(c,a,b) \ +{ \ + (void) utDivideComplex ((a).Real, (a).Imag, (b).Real, (-(b).Imag), \ + &((c).Real), &((c).Imag)) ; \ +} +#else +/* This uses ACM Algo 116, by R. L. Smith, 1962. */ +/* c can be the same variable as a or b. */ +/* Ignore NaN case for double relop br>=bi. */ +#define DIV_CONJ(c,a,b) \ +{ \ + double r, den, ar, ai, br, bi ; \ + br = (b).Real ; \ + bi = (b).Imag ; \ + ar = (a).Real ; \ + ai = (a).Imag ; \ + if (SCALAR_ABS (br) >= SCALAR_ABS (bi)) \ + { \ + r = (-bi) / br ; \ + den = br - r * bi ; \ + (c).Real = (ar + ai * r) / den ; \ + (c).Imag = (ai - ar * r) / den ; \ + } \ + else \ + { \ + r = br / (-bi) ; \ + den = r * br - bi; \ + (c).Real = (ar * r + ai) / den ; \ + (c).Imag = (ai * r - ar) / den ; \ + } \ +} +#endif + +/* -------------------------------------------------------------------------- */ + +/* approximate absolute value, s = |r|+|i| */ +#define APPROX_ABS(s,a) \ +{ \ + (s) = SCALAR_ABS ((a).Real) + SCALAR_ABS ((a).Imag) ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* exact absolute value, s = sqrt (a.real^2 + amag^2) */ +#ifdef MATHWORKS +#define ABS(s,a) \ +{ \ + (s) = utFdlibm_hypot ((a).Real, (a).Imag) ; \ +} +#else +/* Ignore NaN case for the double relops ar>=ai and ar+ai==ar. */ +#define ABS(s,a) \ +{ \ + double r, ar, ai ; \ + ar = SCALAR_ABS ((a).Real) ; \ + ai = SCALAR_ABS ((a).Imag) ; \ + if (ar >= ai) \ + { \ + if (ar + ai == ar) \ + { \ + (s) = ar ; \ + } \ + else \ + { \ + r = ai / ar ; \ + (s) = ar * sqrt (1.0 + r*r) ; \ + } \ + } \ + else \ + { \ + if (ai + ar == ai) \ + { \ + (s) = ai ; \ + } \ + else \ + { \ + r = ar / ai ; \ + (s) = ai * sqrt (1.0 + r*r) ; \ + } \ + } \ +} +#endif + +/* -------------------------------------------------------------------------- */ + +/* print an entry (avoid printing "-0" for negative zero). */ +#define PRINT_ENTRY(a) \ +{ \ + if (SCALAR_IS_NONZERO ((a).Real)) \ + { \ + PRINTF ((" (%g", (a).Real)) ; \ + } \ + else \ + { \ + PRINTF ((" (0")) ; \ + } \ + if (SCALAR_IS_LTZERO ((a).Imag)) \ + { \ + PRINTF ((" - %gi)", -(a).Imag)) ; \ + } \ + else if (SCALAR_IS_ZERO ((a).Imag)) \ + { \ + PRINTF ((" + 0i)")) ; \ + } \ + else \ + { \ + PRINTF ((" + %gi)", (a).Imag)) ; \ + } \ +} + +/* -------------------------------------------------------------------------- */ + +#endif /* #ifndef COMPLEX */ + +/* -------------------------------------------------------------------------- */ +/* Double precision, with int's as integers */ +/* -------------------------------------------------------------------------- */ + +#ifdef DINT + +#define UMF_analyze umf_i_analyze +#define UMF_apply_order umf_i_apply_order +#define UMF_assemble umfdi_assemble +#define UMF_assemble_fixq umfdi_assemble_fixq +#define UMF_blas3_update umfdi_blas3_update +#define UMF_build_tuples umfdi_build_tuples +#define UMF_build_tuples_usage umfdi_build_tuples_usage +#define UMF_colamd umf_i_colamd +#define UMF_colamd_set_defaults umf_i_colamd_set_defaults +#define UMF_create_element umfdi_create_element +#define UMF_extend_front umfdi_extend_front +#define UMF_free umf_i_free +#define UMF_fsize umf_i_fsize +#define UMF_garbage_collection umfdi_garbage_collection +#define UMF_get_memory umfdi_get_memory +#define UMF_grow_front umfdi_grow_front +#define UMF_init_front umfdi_init_front +#define UMF_is_permutation umf_i_is_permutation +#define UMF_kernel umfdi_kernel +#define UMF_kernel_init umfdi_kernel_init +#define UMF_kernel_init_usage umfdi_kernel_init_usage +#define UMF_kernel_wrapup umfdi_kernel_wrapup +#define UMF_local_search umfdi_local_search +#define UMF_lsolve umfdi_lsolve +#define UMF_ltsolve umfdi_ltsolve +#define UMF_lhsolve umfdi_lhsolve +#define UMF_malloc umf_i_malloc +#define UMF_mem_alloc_element umfdi_mem_alloc_element +#define UMF_mem_alloc_head_block umfdi_mem_alloc_head_block +#define UMF_mem_alloc_tail_block umfdi_mem_alloc_tail_block +#define UMF_mem_free_tail_block umfdi_mem_free_tail_block +#define UMF_mem_init_memoryspace umfdi_mem_init_memoryspace +#define UMF_realloc umf_i_realloc +#define UMF_report_perm umf_i_report_perm +#define UMF_report_vector umfdi_report_vector +#define UMF_row_search umfdi_row_search +#define UMF_scale umfdi_scale +#define UMF_scale_column umfdi_scale_column +#define UMF_set_stats umf_i_set_stats +#define UMF_singletons umf_i_singletons +#define UMF_solve umfdi_solve +#define UMF_start_front umfdi_start_front +#define UMF_store_lu umfdi_store_lu +#define UMF_store_lu_drop umfdi_store_lu_drop +#define UMF_symbolic_usage umfdi_symbolic_usage +#define UMF_transpose umfdi_transpose +#define UMF_tuple_lengths umfdi_tuple_lengths +#define UMF_usolve umfdi_usolve +#define UMF_utsolve umfdi_utsolve +#define UMF_uhsolve umfdi_uhsolve +#define UMF_valid_numeric umfdi_valid_numeric +#define UMF_valid_symbolic umfdi_valid_symbolic +#define UMF_triplet_map_x umfdi_triplet_map_x +#define UMF_triplet_map_nox umfdi_triplet_map_nox +#define UMF_triplet_nomap_x umfdi_triplet_nomap_x +#define UMF_triplet_nomap_nox umfdi_triplet_nomap_nox +#define UMF_2by2 umfdi_2by2 + +#define UMFPACK_col_to_triplet umfpack_di_col_to_triplet +#define UMFPACK_defaults umfpack_di_defaults +#define UMFPACK_free_numeric umfpack_di_free_numeric +#define UMFPACK_free_symbolic umfpack_di_free_symbolic +#define UMFPACK_get_lunz umfpack_di_get_lunz +#define UMFPACK_get_numeric umfpack_di_get_numeric +#define UMFPACK_get_symbolic umfpack_di_get_symbolic +#define UMFPACK_numeric umfpack_di_numeric +#define UMFPACK_qsymbolic umfpack_di_qsymbolic +#define UMFPACK_report_control umfpack_di_report_control +#define UMFPACK_report_info umfpack_di_report_info +#define UMFPACK_report_matrix umfpack_di_report_matrix +#define UMFPACK_report_numeric umfpack_di_report_numeric +#define UMFPACK_report_perm umfpack_di_report_perm +#define UMFPACK_report_status umfpack_di_report_status +#define UMFPACK_report_symbolic umfpack_di_report_symbolic +#define UMFPACK_report_triplet umfpack_di_report_triplet +#define UMFPACK_report_vector umfpack_di_report_vector +#define UMFPACK_save_numeric umfpack_di_save_numeric +#define UMFPACK_save_symbolic umfpack_di_save_symbolic +#define UMFPACK_load_numeric umfpack_di_load_numeric +#define UMFPACK_load_symbolic umfpack_di_load_symbolic +#define UMFPACK_scale umfpack_di_scale +#define UMFPACK_solve umfpack_di_solve +#define UMFPACK_symbolic umfpack_di_symbolic +#define UMFPACK_transpose umfpack_di_transpose +#define UMFPACK_triplet_to_col umfpack_di_triplet_to_col +#define UMFPACK_wsolve umfpack_di_wsolve + +/* for debugging only: */ +#define UMF_malloc_count umf_i_malloc_count +#define UMF_debug umfdi_debug +#define UMF_allocfail umfdi_allocfail +#define UMF_gprob umfdi_gprob +#define UMF_dump_dense umfdi_dump_dense +#define UMF_dump_element umfdi_dump_element +#define UMF_dump_rowcol umfdi_dump_rowcol +#define UMF_dump_matrix umfdi_dump_matrix +#define UMF_dump_current_front umfdi_dump_current_front +#define UMF_dump_lu umfdi_dump_lu +#define UMF_dump_memory umfdi_dump_memory +#define UMF_dump_packed_memory umfdi_dump_packed_memory +#define UMF_dump_col_matrix umfdi_dump_col_matrix +#define UMF_dump_chain umfdi_dump_chain +#define UMF_dump_start umfdi_dump_start +#define UMF_dump_rowmerge umfdi_dump_rowmerge +#define UMF_dump_diagonal_map umfdi_dump_diagonal_map + +#endif + +/* -------------------------------------------------------------------------- */ +/* Double precision, with long's as integers */ +/* -------------------------------------------------------------------------- */ + +#ifdef DLONG + +#define UMF_analyze umf_l_analyze +#define UMF_apply_order umf_l_apply_order +#define UMF_assemble umfdl_assemble +#define UMF_assemble_fixq umfdl_assemble_fixq +#define UMF_blas3_update umfdl_blas3_update +#define UMF_build_tuples umfdl_build_tuples +#define UMF_build_tuples_usage umfdl_build_tuples_usage +#define UMF_colamd umf_l_colamd +#define UMF_colamd_set_defaults umf_l_colamd_set_defaults +#define UMF_create_element umfdl_create_element +#define UMF_extend_front umfdl_extend_front +#define UMF_free umf_l_free +#define UMF_fsize umf_l_fsize +#define UMF_garbage_collection umfdl_garbage_collection +#define UMF_get_memory umfdl_get_memory +#define UMF_grow_front umfdl_grow_front +#define UMF_init_front umfdl_init_front +#define UMF_is_permutation umf_l_is_permutation +#define UMF_kernel umfdl_kernel +#define UMF_kernel_init umfdl_kernel_init +#define UMF_kernel_init_usage umfdl_kernel_init_usage +#define UMF_kernel_wrapup umfdl_kernel_wrapup +#define UMF_local_search umfdl_local_search +#define UMF_lsolve umfdl_lsolve +#define UMF_ltsolve umfdl_ltsolve +#define UMF_lhsolve umfdl_lhsolve +#define UMF_malloc umf_l_malloc +#define UMF_mem_alloc_element umfdl_mem_alloc_element +#define UMF_mem_alloc_head_block umfdl_mem_alloc_head_block +#define UMF_mem_alloc_tail_block umfdl_mem_alloc_tail_block +#define UMF_mem_free_tail_block umfdl_mem_free_tail_block +#define UMF_mem_init_memoryspace umfdl_mem_init_memoryspace +#define UMF_realloc umf_l_realloc +#define UMF_report_perm umf_l_report_perm +#define UMF_report_vector umfdl_report_vector +#define UMF_row_search umfdl_row_search +#define UMF_scale umfdl_scale +#define UMF_scale_column umfdl_scale_column +#define UMF_set_stats umf_l_set_stats +#define UMF_singletons umf_l_singletons +#define UMF_solve umfdl_solve +#define UMF_start_front umfdl_start_front +#define UMF_store_lu umfdl_store_lu +#define UMF_store_lu_drop umfdl_store_lu_drop +#define UMF_symbolic_usage umfdl_symbolic_usage +#define UMF_transpose umfdl_transpose +#define UMF_tuple_lengths umfdl_tuple_lengths +#define UMF_usolve umfdl_usolve +#define UMF_utsolve umfdl_utsolve +#define UMF_uhsolve umfdl_uhsolve +#define UMF_valid_numeric umfdl_valid_numeric +#define UMF_valid_symbolic umfdl_valid_symbolic +#define UMF_triplet_map_x umfdl_triplet_map_x +#define UMF_triplet_map_nox umfdl_triplet_map_nox +#define UMF_triplet_nomap_x umfdl_triplet_nomap_x +#define UMF_triplet_nomap_nox umfdl_triplet_nomap_nox +#define UMF_2by2 umfdl_2by2 + +#define UMFPACK_col_to_triplet umfpack_dl_col_to_triplet +#define UMFPACK_defaults umfpack_dl_defaults +#define UMFPACK_free_numeric umfpack_dl_free_numeric +#define UMFPACK_free_symbolic umfpack_dl_free_symbolic +#define UMFPACK_get_lunz umfpack_dl_get_lunz +#define UMFPACK_get_numeric umfpack_dl_get_numeric +#define UMFPACK_get_symbolic umfpack_dl_get_symbolic +#define UMFPACK_numeric umfpack_dl_numeric +#define UMFPACK_qsymbolic umfpack_dl_qsymbolic +#define UMFPACK_report_control umfpack_dl_report_control +#define UMFPACK_report_info umfpack_dl_report_info +#define UMFPACK_report_matrix umfpack_dl_report_matrix +#define UMFPACK_report_numeric umfpack_dl_report_numeric +#define UMFPACK_report_perm umfpack_dl_report_perm +#define UMFPACK_report_status umfpack_dl_report_status +#define UMFPACK_report_symbolic umfpack_dl_report_symbolic +#define UMFPACK_report_triplet umfpack_dl_report_triplet +#define UMFPACK_report_vector umfpack_dl_report_vector +#define UMFPACK_save_numeric umfpack_dl_save_numeric +#define UMFPACK_save_symbolic umfpack_dl_save_symbolic +#define UMFPACK_load_numeric umfpack_dl_load_numeric +#define UMFPACK_load_symbolic umfpack_dl_load_symbolic +#define UMFPACK_scale umfpack_dl_scale +#define UMFPACK_solve umfpack_dl_solve +#define UMFPACK_symbolic umfpack_dl_symbolic +#define UMFPACK_transpose umfpack_dl_transpose +#define UMFPACK_triplet_to_col umfpack_dl_triplet_to_col +#define UMFPACK_wsolve umfpack_dl_wsolve + +/* for debugging only: */ +#define UMF_malloc_count umf_l_malloc_count +#define UMF_debug umfdl_debug +#define UMF_allocfail umfdl_allocfail +#define UMF_gprob umfdl_gprob +#define UMF_dump_dense umfdl_dump_dense +#define UMF_dump_element umfdl_dump_element +#define UMF_dump_rowcol umfdl_dump_rowcol +#define UMF_dump_matrix umfdl_dump_matrix +#define UMF_dump_current_front umfdl_dump_current_front +#define UMF_dump_lu umfdl_dump_lu +#define UMF_dump_memory umfdl_dump_memory +#define UMF_dump_packed_memory umfdl_dump_packed_memory +#define UMF_dump_col_matrix umfdl_dump_col_matrix +#define UMF_dump_chain umfdl_dump_chain +#define UMF_dump_start umfdl_dump_start +#define UMF_dump_rowmerge umfdl_dump_rowmerge +#define UMF_dump_diagonal_map umfdl_dump_diagonal_map + +#endif + +/* -------------------------------------------------------------------------- */ +/* Complex double precision, with int's as integers */ +/* -------------------------------------------------------------------------- */ + +#ifdef ZINT + +#define UMF_analyze umf_i_analyze +#define UMF_apply_order umf_i_apply_order +#define UMF_assemble umfzi_assemble +#define UMF_assemble_fixq umfzi_assemble_fixq +#define UMF_blas3_update umfzi_blas3_update +#define UMF_build_tuples umfzi_build_tuples +#define UMF_build_tuples_usage umfzi_build_tuples_usage +#define UMF_colamd umf_i_colamd +#define UMF_colamd_set_defaults umf_i_colamd_set_defaults +#define UMF_create_element umfzi_create_element +#define UMF_extend_front umfzi_extend_front +#define UMF_free umf_i_free +#define UMF_fsize umf_i_fsize +#define UMF_garbage_collection umfzi_garbage_collection +#define UMF_get_memory umfzi_get_memory +#define UMF_grow_front umfzi_grow_front +#define UMF_init_front umfzi_init_front +#define UMF_is_permutation umf_i_is_permutation +#define UMF_kernel umfzi_kernel +#define UMF_kernel_init umfzi_kernel_init +#define UMF_kernel_init_usage umfzi_kernel_init_usage +#define UMF_kernel_wrapup umfzi_kernel_wrapup +#define UMF_local_search umfzi_local_search +#define UMF_lsolve umfzi_lsolve +#define UMF_ltsolve umfzi_ltsolve +#define UMF_lhsolve umfzi_lhsolve +#define UMF_malloc umf_i_malloc +#define UMF_mem_alloc_element umfzi_mem_alloc_element +#define UMF_mem_alloc_head_block umfzi_mem_alloc_head_block +#define UMF_mem_alloc_tail_block umfzi_mem_alloc_tail_block +#define UMF_mem_free_tail_block umfzi_mem_free_tail_block +#define UMF_mem_init_memoryspace umfzi_mem_init_memoryspace +#define UMF_realloc umf_i_realloc +#define UMF_report_perm umf_i_report_perm +#define UMF_report_vector umfzi_report_vector +#define UMF_row_search umfzi_row_search +#define UMF_scale umfzi_scale +#define UMF_scale_column umfzi_scale_column +#define UMF_set_stats umfzi_set_stats +#define UMF_singletons umf_i_singletons +#define UMF_solve umfzi_solve +#define UMF_start_front umfzi_start_front +#define UMF_store_lu umfzi_store_lu +#define UMF_store_lu_drop umfzi_store_lu_drop +#define UMF_symbolic_usage umfzi_symbolic_usage +#define UMF_transpose umfzi_transpose +#define UMF_tuple_lengths umfzi_tuple_lengths +#define UMF_usolve umfzi_usolve +#define UMF_utsolve umfzi_utsolve +#define UMF_uhsolve umfzi_uhsolve +#define UMF_valid_numeric umfzi_valid_numeric +#define UMF_valid_symbolic umfzi_valid_symbolic +#define UMF_triplet_map_x umfzi_triplet_map_x +#define UMF_triplet_map_nox umfzi_triplet_map_nox +#define UMF_triplet_nomap_x umfzi_triplet_nomap_x +#define UMF_triplet_nomap_nox umfzi_triplet_nomap_nox +#define UMF_2by2 umfzi_2by2 + +#define UMFPACK_col_to_triplet umfpack_zi_col_to_triplet +#define UMFPACK_defaults umfpack_zi_defaults +#define UMFPACK_free_numeric umfpack_zi_free_numeric +#define UMFPACK_free_symbolic umfpack_zi_free_symbolic +#define UMFPACK_get_lunz umfpack_zi_get_lunz +#define UMFPACK_get_numeric umfpack_zi_get_numeric +#define UMFPACK_get_symbolic umfpack_zi_get_symbolic +#define UMFPACK_numeric umfpack_zi_numeric +#define UMFPACK_qsymbolic umfpack_zi_qsymbolic +#define UMFPACK_report_control umfpack_zi_report_control +#define UMFPACK_report_info umfpack_zi_report_info +#define UMFPACK_report_matrix umfpack_zi_report_matrix +#define UMFPACK_report_numeric umfpack_zi_report_numeric +#define UMFPACK_report_perm umfpack_zi_report_perm +#define UMFPACK_report_status umfpack_zi_report_status +#define UMFPACK_report_symbolic umfpack_zi_report_symbolic +#define UMFPACK_report_triplet umfpack_zi_report_triplet +#define UMFPACK_report_vector umfpack_zi_report_vector +#define UMFPACK_save_numeric umfpack_zi_save_numeric +#define UMFPACK_save_symbolic umfpack_zi_save_symbolic +#define UMFPACK_load_numeric umfpack_zi_load_numeric +#define UMFPACK_load_symbolic umfpack_zi_load_symbolic +#define UMFPACK_scale umfpack_zi_scale +#define UMFPACK_solve umfpack_zi_solve +#define UMFPACK_symbolic umfpack_zi_symbolic +#define UMFPACK_transpose umfpack_zi_transpose +#define UMFPACK_triplet_to_col umfpack_zi_triplet_to_col +#define UMFPACK_wsolve umfpack_zi_wsolve + +/* for debugging only: */ +#define UMF_malloc_count umf_i_malloc_count +#define UMF_debug umfzi_debug +#define UMF_allocfail umfzi_allocfail +#define UMF_gprob umfzi_gprob +#define UMF_dump_dense umfzi_dump_dense +#define UMF_dump_element umfzi_dump_element +#define UMF_dump_rowcol umfzi_dump_rowcol +#define UMF_dump_matrix umfzi_dump_matrix +#define UMF_dump_current_front umfzi_dump_current_front +#define UMF_dump_lu umfzi_dump_lu +#define UMF_dump_memory umfzi_dump_memory +#define UMF_dump_packed_memory umfzi_dump_packed_memory +#define UMF_dump_col_matrix umfzi_dump_col_matrix +#define UMF_dump_chain umfzi_dump_chain +#define UMF_dump_start umfzi_dump_start +#define UMF_dump_rowmerge umfzi_dump_rowmerge +#define UMF_dump_diagonal_map umfzi_dump_diagonal_map + +#endif + +/* -------------------------------------------------------------------------- */ +/* Complex double precision, with long's as integers */ +/* -------------------------------------------------------------------------- */ + +#ifdef ZLONG + +#define UMF_analyze umf_l_analyze +#define UMF_apply_order umf_l_apply_order +#define UMF_assemble umfzl_assemble +#define UMF_assemble_fixq umfzl_assemble_fixq +#define UMF_blas3_update umfzl_blas3_update +#define UMF_build_tuples umfzl_build_tuples +#define UMF_build_tuples_usage umfzl_build_tuples_usage +#define UMF_colamd umf_l_colamd +#define UMF_colamd_set_defaults umf_l_colamd_set_defaults +#define UMF_create_element umfzl_create_element +#define UMF_extend_front umfzl_extend_front +#define UMF_free umf_l_free +#define UMF_fsize umf_l_fsize +#define UMF_garbage_collection umfzl_garbage_collection +#define UMF_get_memory umfzl_get_memory +#define UMF_grow_front umfzl_grow_front +#define UMF_init_front umfzl_init_front +#define UMF_is_permutation umf_l_is_permutation +#define UMF_kernel umfzl_kernel +#define UMF_kernel_init umfzl_kernel_init +#define UMF_kernel_init_usage umfzl_kernel_init_usage +#define UMF_kernel_wrapup umfzl_kernel_wrapup +#define UMF_local_search umfzl_local_search +#define UMF_lsolve umfzl_lsolve +#define UMF_ltsolve umfzl_ltsolve +#define UMF_lhsolve umfzl_lhsolve +#define UMF_malloc umf_l_malloc +#define UMF_mem_alloc_element umfzl_mem_alloc_element +#define UMF_mem_alloc_head_block umfzl_mem_alloc_head_block +#define UMF_mem_alloc_tail_block umfzl_mem_alloc_tail_block +#define UMF_mem_free_tail_block umfzl_mem_free_tail_block +#define UMF_mem_init_memoryspace umfzl_mem_init_memoryspace +#define UMF_realloc umf_l_realloc +#define UMF_report_perm umf_l_report_perm +#define UMF_report_vector umfzl_report_vector +#define UMF_row_search umfzl_row_search +#define UMF_scale umfzl_scale +#define UMF_scale_column umfzl_scale_column +#define UMF_set_stats umfzl_set_stats +#define UMF_singletons umf_l_singletons +#define UMF_solve umfzl_solve +#define UMF_start_front umfzl_start_front +#define UMF_store_lu umfzl_store_lu +#define UMF_store_lu_drop umfzl_store_lu_drop +#define UMF_symbolic_usage umfzl_symbolic_usage +#define UMF_transpose umfzl_transpose +#define UMF_tuple_lengths umfzl_tuple_lengths +#define UMF_usolve umfzl_usolve +#define UMF_utsolve umfzl_utsolve +#define UMF_uhsolve umfzl_uhsolve +#define UMF_valid_numeric umfzl_valid_numeric +#define UMF_valid_symbolic umfzl_valid_symbolic +#define UMF_triplet_map_x umfzl_triplet_map_x +#define UMF_triplet_map_nox umfzl_triplet_map_nox +#define UMF_triplet_nomap_x umfzl_triplet_nomap_x +#define UMF_triplet_nomap_nox umfzl_triplet_nomap_nox +#define UMF_2by2 umfzl_2by2 + +#define UMFPACK_col_to_triplet umfpack_zl_col_to_triplet +#define UMFPACK_defaults umfpack_zl_defaults +#define UMFPACK_free_numeric umfpack_zl_free_numeric +#define UMFPACK_free_symbolic umfpack_zl_free_symbolic +#define UMFPACK_get_lunz umfpack_zl_get_lunz +#define UMFPACK_get_numeric umfpack_zl_get_numeric +#define UMFPACK_get_symbolic umfpack_zl_get_symbolic +#define UMFPACK_numeric umfpack_zl_numeric +#define UMFPACK_qsymbolic umfpack_zl_qsymbolic +#define UMFPACK_report_control umfpack_zl_report_control +#define UMFPACK_report_info umfpack_zl_report_info +#define UMFPACK_report_matrix umfpack_zl_report_matrix +#define UMFPACK_report_numeric umfpack_zl_report_numeric +#define UMFPACK_report_perm umfpack_zl_report_perm +#define UMFPACK_report_status umfpack_zl_report_status +#define UMFPACK_report_symbolic umfpack_zl_report_symbolic +#define UMFPACK_report_triplet umfpack_zl_report_triplet +#define UMFPACK_report_vector umfpack_zl_report_vector +#define UMFPACK_save_numeric umfpack_zl_save_numeric +#define UMFPACK_save_symbolic umfpack_zl_save_symbolic +#define UMFPACK_load_numeric umfpack_zl_load_numeric +#define UMFPACK_load_symbolic umfpack_zl_load_symbolic +#define UMFPACK_scale umfpack_zl_scale +#define UMFPACK_solve umfpack_zl_solve +#define UMFPACK_symbolic umfpack_zl_symbolic +#define UMFPACK_transpose umfpack_zl_transpose +#define UMFPACK_triplet_to_col umfpack_zl_triplet_to_col +#define UMFPACK_wsolve umfpack_zl_wsolve + +/* for debugging only: */ +#define UMF_malloc_count umf_l_malloc_count +#define UMF_debug umfzl_debug +#define UMF_allocfail umfzl_allocfail +#define UMF_gprob umfzl_gprob +#define UMF_dump_dense umfzl_dump_dense +#define UMF_dump_element umfzl_dump_element +#define UMF_dump_rowcol umfzl_dump_rowcol +#define UMF_dump_matrix umfzl_dump_matrix +#define UMF_dump_current_front umfzl_dump_current_front +#define UMF_dump_lu umfzl_dump_lu +#define UMF_dump_memory umfzl_dump_memory +#define UMF_dump_packed_memory umfzl_dump_packed_memory +#define UMF_dump_col_matrix umfzl_dump_col_matrix +#define UMF_dump_chain umfzl_dump_chain +#define UMF_dump_start umfzl_dump_start +#define UMF_dump_rowmerge umfzl_dump_rowmerge +#define UMF_dump_diagonal_map umfzl_dump_diagonal_map + +#endif diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_col_to_triplet.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_col_to_triplet.c new file mode 100644 index 0000000000..ef27bc2e8d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_col_to_triplet.c @@ -0,0 +1,73 @@ +/* ========================================================================== */ +/* === UMFPACK_col_to_triplet =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User callable. Converts a column-oriented input matrix to triplet form by + constructing the column indices Tj from the column pointers Ap. The matrix + may be singular. See umfpack_col_to_triplet.h for details. + +*/ + +#include "umf_internal.h" + +GLOBAL Int UMFPACK_col_to_triplet +( + Int n_col, + const Int Ap [ ], + Int Tj [ ] +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int nz, j, p, p1, p2, length ; + + /* ---------------------------------------------------------------------- */ + /* construct the column indices */ + /* ---------------------------------------------------------------------- */ + + if (!Ap || !Tj) + { + return (UMFPACK_ERROR_argument_missing) ; + } + if (n_col <= 0) + { + return (UMFPACK_ERROR_n_nonpositive) ; + } + if (Ap [0] != 0) + { + return (UMFPACK_ERROR_invalid_matrix) ; + } + nz = Ap [n_col] ; + if (nz < 0) + { + return (UMFPACK_ERROR_invalid_matrix) ; + } + + for (j = 0 ; j < n_col ; j++) + { + p1 = Ap [j] ; + p2 = Ap [j+1] ; + length = p2 - p1 ; + if (length < 0 || p2 > nz) + { + return (UMFPACK_ERROR_invalid_matrix) ; + } + for (p = p1 ; p < p2 ; p++) + { + Tj [p] = j ; + } + } + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_defaults.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_defaults.c new file mode 100644 index 0000000000..8de4c1b0d0 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_defaults.c @@ -0,0 +1,119 @@ +/* ========================================================================== */ +/* === UMFPACK_defaults ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Sets default control parameters. See umfpack_defaults.h + for details. +*/ + +#include "umf_internal.h" + +GLOBAL void UMFPACK_defaults +( + double Control [UMFPACK_CONTROL] +) +{ + Int i ; + + if (!Control) + { + /* silently return if no Control array */ + return ; + } + + for (i = 0 ; i < UMFPACK_CONTROL ; i++) + { + Control [i] = 0 ; + } + + /* ---------------------------------------------------------------------- */ + /* default control settings: can be modified at run-time */ + /* ---------------------------------------------------------------------- */ + + /* used in UMFPACK_report_* routines: */ + Control [UMFPACK_PRL] = UMFPACK_DEFAULT_PRL ; + + /* used in UMFPACK_*symbolic: */ + Control [UMFPACK_DENSE_ROW] = UMFPACK_DEFAULT_DENSE_ROW ; + Control [UMFPACK_DENSE_COL] = UMFPACK_DEFAULT_DENSE_COL ; + Control [UMFPACK_AMD_DENSE] = UMFPACK_DEFAULT_AMD_DENSE ; + Control [UMFPACK_STRATEGY] = UMFPACK_DEFAULT_STRATEGY ; + Control [UMFPACK_2BY2_TOLERANCE] = UMFPACK_DEFAULT_2BY2_TOLERANCE ; + Control [UMFPACK_AGGRESSIVE] = UMFPACK_DEFAULT_AGGRESSIVE ; + + /* used in UMFPACK_numeric: */ + Control [UMFPACK_PIVOT_TOLERANCE] = UMFPACK_DEFAULT_PIVOT_TOLERANCE ; + Control [UMFPACK_SYM_PIVOT_TOLERANCE] = UMFPACK_DEFAULT_SYM_PIVOT_TOLERANCE; + Control [UMFPACK_BLOCK_SIZE] = UMFPACK_DEFAULT_BLOCK_SIZE ; + Control [UMFPACK_ALLOC_INIT] = UMFPACK_DEFAULT_ALLOC_INIT ; + Control [UMFPACK_FRONT_ALLOC_INIT] = UMFPACK_DEFAULT_FRONT_ALLOC_INIT ; + Control [UMFPACK_SCALE] = UMFPACK_DEFAULT_SCALE ; + + /* used in UMFPACK_*solve: */ + Control [UMFPACK_IRSTEP] = UMFPACK_DEFAULT_IRSTEP ; + + /* ---------------------------------------------------------------------- */ + /* compile-time settings: cannot be modified at run-time */ + /* ---------------------------------------------------------------------- */ + +#ifdef USE_NO_BLAS + /* do not use the BLAS - use in-line C code instead */ + Control [UMFPACK_COMPILED_WITH_BLAS] = 0 ; +#else + /* use externally-provided BLAS (dgemm, dger, dgemv, zgemm, zgeru, zgemv) */ + Control [UMFPACK_COMPILED_WITH_BLAS] = 1 ; +#endif + +#ifdef MATLAB_MEX_FILE + /* use mxMalloc, mxFree, mxRealloc, and mexPrintf */ + /* use mxAssert if debugging is enabled */ + Control [UMFPACK_COMPILED_FOR_MATLAB] = 1 ; +#else +#ifdef MATHWORKS + /* use internal utMalloc, utFree, utRealloc, and utPrintf routines. */ + /* use utDivideComplex and utFdlibm_hypot for complex version. */ + /* use utAssert if debugging is enabled. */ + Control [UMFPACK_COMPILED_FOR_MATLAB] = 2 ; +#else + /* use ANSI C malloc, free, realloc, and print */ + /* use ANSI C assert if debugging is enabled */ + Control [UMFPACK_COMPILED_FOR_MATLAB] = 0 ; +#endif +#endif + +#ifdef NO_TIMER + /* no timer used */ + Control [UMFPACK_COMPILED_WITH_GETRUSAGE] = 3 ; +#ifndef NPOSIX + /* uses the POSIX sysconf ( ) and times ( ) routines in UMFPACK_tic, toc */ + Control [UMFPACK_COMPILED_WITH_GETRUSAGE] = 2 ; +#else +#ifdef GETRUSAGE + /* uses the non-standard getrusage to get CPU time (Solaris) */ + Control [UMFPACK_COMPILED_WITH_GETRUSAGE] = 1 ; +#else + /* uses the ANSI standard clock routine to get CPU time */ + /* this may wrap around */ + Control [UMFPACK_COMPILED_WITH_GETRUSAGE] = 0 ; +#endif +#endif +#endif + +#ifndef NDEBUG + /* UMFPACK is compiled in debug mode. */ + /* This is exceedingly slow. */ + DEBUG0 (("UMFPACK is running in debug mode. This is very slow!\n")) ; + Control [UMFPACK_COMPILED_IN_DEBUG_MODE] = 1 ; +#else + /* UMFPACK is compiled in normal (non-debug) mode */ + Control [UMFPACK_COMPILED_IN_DEBUG_MODE] = 0 ; +#endif +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_free_numeric.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_free_numeric.c new file mode 100644 index 0000000000..ec19adfa9d --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_free_numeric.c @@ -0,0 +1,58 @@ +/* ========================================================================== */ +/* === UMFPACK_free_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* User-callable. Free the entire Numeric object (consists of 11 to 13 + * malloc'd objects. See UMFPACK_free_numeric.h for details. + */ + +#include "umf_internal.h" +#include "umf_free.h" + +GLOBAL void UMFPACK_free_numeric +( + void **NumericHandle +) +{ + + NumericType *Numeric ; + if (!NumericHandle) + { + return ; + } + Numeric = *((NumericType **) NumericHandle) ; + if (!Numeric) + { + return ; + } + + /* these 9 objects always exist */ + (void) UMF_free ((void *) Numeric->D) ; + (void) UMF_free ((void *) Numeric->Rperm) ; + (void) UMF_free ((void *) Numeric->Cperm) ; + (void) UMF_free ((void *) Numeric->Lpos) ; + (void) UMF_free ((void *) Numeric->Lilen) ; + (void) UMF_free ((void *) Numeric->Lip) ; + (void) UMF_free ((void *) Numeric->Upos) ; + (void) UMF_free ((void *) Numeric->Uilen) ; + (void) UMF_free ((void *) Numeric->Uip) ; + + /* Rs does not exist if scaling was not performed */ + (void) UMF_free ((void *) Numeric->Rs) ; + + /* Upattern can only exist for singular or rectangular matrices */ + (void) UMF_free ((void *) Numeric->Upattern) ; + + /* these 2 objects always exist */ + (void) UMF_free ((void *) Numeric->Memory) ; + (void) UMF_free ((void *) Numeric) ; + + *NumericHandle = (void *) NULL ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_free_symbolic.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_free_symbolic.c new file mode 100644 index 0000000000..347727f85f --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_free_symbolic.c @@ -0,0 +1,57 @@ +/* ========================================================================== */ +/* === UMFPACK_free_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. See umfpack_free_symbolic.h for details. + All 10 objects comprising the Symbolic object are free'd via UMF_free. +*/ + +#include "umf_internal.h" +#include "umf_free.h" + +GLOBAL void UMFPACK_free_symbolic +( + void **SymbolicHandle +) +{ + + SymbolicType *Symbolic ; + if (!SymbolicHandle) + { + return ; + } + Symbolic = *((SymbolicType **) SymbolicHandle) ; + if (!Symbolic) + { + return ; + } + + (void) UMF_free ((void *) Symbolic->Cperm_init) ; + (void) UMF_free ((void *) Symbolic->Rperm_init) ; + (void) UMF_free ((void *) Symbolic->Front_npivcol) ; + (void) UMF_free ((void *) Symbolic->Front_parent) ; + (void) UMF_free ((void *) Symbolic->Front_1strow) ; + (void) UMF_free ((void *) Symbolic->Front_leftmostdesc) ; + (void) UMF_free ((void *) Symbolic->Chain_start) ; + (void) UMF_free ((void *) Symbolic->Chain_maxrows) ; + (void) UMF_free ((void *) Symbolic->Chain_maxcols) ; + (void) UMF_free ((void *) Symbolic->Cdeg) ; + (void) UMF_free ((void *) Symbolic->Rdeg) ; + + /* only when dense rows are present */ + (void) UMF_free ((void *) Symbolic->Esize) ; + + /* only when diagonal pivoting is prefered */ + (void) UMF_free ((void *) Symbolic->Diagonal_map) ; + + (void) UMF_free ((void *) Symbolic) ; + *SymbolicHandle = (void *) NULL ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_lunz.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_lunz.c new file mode 100644 index 0000000000..37ac8bffe6 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_lunz.c @@ -0,0 +1,56 @@ +/* ========================================================================== */ +/* === UMFPACK_get_lunz ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Determines the number of nonzeros in L and U, and the size + of L and U. +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" + +GLOBAL Int UMFPACK_get_lunz +( + Int *lnz, + Int *unz, + Int *n_row, + Int *n_col, + Int *nz_udiag, + void *NumericHandle +) +{ + NumericType *Numeric ; + + Numeric = (NumericType *) NumericHandle ; + + if (!UMF_valid_numeric (Numeric)) + { + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + if (!lnz || !unz || !n_row || !n_col || !nz_udiag) + { + return (UMFPACK_ERROR_argument_missing) ; + } + + *n_row = Numeric->n_row ; + *n_col = Numeric->n_col ; + + /* number of nz's in L below diagonal, plus the unit diagonal of L */ + *lnz = Numeric->lnz + MIN (Numeric->n_row, Numeric->n_col) ; + + /* number of nz's in U above diagonal, plus nz's on diagaonal of U */ + *unz = Numeric->unz + Numeric->nnzpiv ; + + /* number of nz's on the diagonal */ + *nz_udiag = Numeric->nnzpiv ; + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_numeric.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_numeric.c new file mode 100644 index 0000000000..e270b1e3a4 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_numeric.c @@ -0,0 +1,980 @@ +/* ========================================================================== */ +/* === UMFPACK_get_numeric ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Gets the LU factors and the permutation vectors held in the + Numeric object. L is returned in sparse row form with sorted rows, U is + returned in sparse column form with sorted columns, and P and Q are + returned as permutation vectors. See umfpack_get_numeric.h for a more + detailed description. + + Returns TRUE if successful, FALSE if the Numeric object is invalid or + if out of memory. + + Dynamic memory usage: calls UMF_malloc twice, for a total space of + 2*n integers, and then frees all of it via UMF_free when done. + +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" +#include "umf_malloc.h" +#include "umf_free.h" + +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif + +PRIVATE void get_L +( + Int Lp [ ], + Int Lj [ ], + double Lx [ ], +#ifdef COMPLEX + double Lz [ ], +#endif + NumericType *Numeric, + Int Pattern [ ], + Int Wi [ ] +) ; + +PRIVATE void get_U +( + Int Up [ ], + Int Ui [ ], + double Ux [ ], +#ifdef COMPLEX + double Uz [ ], +#endif + NumericType *Numeric, + Int Pattern [ ], + Int Wi [ ] +) ; + +/* ========================================================================== */ +/* === UMFPACK_get_numeric ================================================== */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_get_numeric +( + Int Lp [ ], + Int Lj [ ], + double Lx [ ], +#ifdef COMPLEX + double Lz [ ], +#endif + Int Up [ ], + Int Ui [ ], + double Ux [ ], +#ifdef COMPLEX + double Uz [ ], +#endif + Int P [ ], + Int Q [ ], + double Dx [ ], +#ifdef COMPLEX + double Dz [ ], +#endif + Int *p_do_recip, + double Rs [ ], + void *NumericHandle +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + NumericType *Numeric ; + Int getL, getU, *Rperm, *Cperm, k, nn, n_row, n_col, *Wi, *Pattern, + n_inner ; + double *Rs1 ; + Entry *D ; + +#ifndef NDEBUG + init_count = UMF_malloc_count ; +#endif + + Wi = (Int *) NULL ; + Pattern = (Int *) NULL ; + + /* ---------------------------------------------------------------------- */ + /* check input parameters */ + /* ---------------------------------------------------------------------- */ + + Numeric = (NumericType *) NumericHandle ; + if (!UMF_valid_numeric (Numeric)) + { + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + n_row = Numeric->n_row ; + n_col = Numeric->n_col ; + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + + /* ---------------------------------------------------------------------- */ + /* allocate workspace */ + /* ---------------------------------------------------------------------- */ + +#ifdef COMPLEX + getL = Lp && Lj && Lx && Lz ; + getU = Up && Ui && Ux && Uz ; +#else + getL = Lp && Lj && Lx ; + getU = Up && Ui && Ux ; +#endif + + if (getL || getU) + { + Wi = (Int *) UMF_malloc (nn, sizeof (Int)) ; + Pattern = (Int *) UMF_malloc (nn, sizeof (Int)) ; + if (!Wi || !Pattern) + { + (void) UMF_free ((void *) Wi) ; + (void) UMF_free ((void *) Pattern) ; + ASSERT (UMF_malloc_count == init_count) ; + DEBUGm4 (("out of memory: get numeric\n")) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + ASSERT (UMF_malloc_count == init_count + 2) ; + } + + /* ---------------------------------------------------------------------- */ + /* get contents of Numeric */ + /* ---------------------------------------------------------------------- */ + + if (P != (Int *) NULL) + { + Rperm = Numeric->Rperm ; + for (k = 0 ; k < n_row ; k++) + { + P [k] = Rperm [k] ; + } + } + + if (Q != (Int *) NULL) + { + Cperm = Numeric->Cperm ; + for (k = 0 ; k < n_col ; k++) + { + Q [k] = Cperm [k] ; + } + } + + if (getL) + { + get_L (Lp, Lj, Lx, +#ifdef COMPLEX + Lz, +#endif + Numeric, Pattern, Wi) ; + } + + if (getU) + { + get_U (Up, Ui, Ux, +#ifdef COMPLEX + Uz, +#endif + Numeric, Pattern, Wi) ; + } + + if (Dx != (double *) NULL) + { + D = Numeric->D ; + for (k = 0 ; k < n_inner ; k++) + { + Dx [k] = REAL_COMPONENT (D [k]) ; + } + } + +#ifdef COMPLEX + if (Dz != (double *) NULL) + { + D = Numeric->D ; + for (k = 0 ; k < n_inner ; k++) + { + Dz [k] = IMAG_COMPONENT (D [k]) ; + } + } +#endif + + /* return the flag stating whether the scale factors are to be multiplied, + * or divided. If do_recip is TRUE, multiply. Otherwise, divided. + * If NRECIPROCAL is defined at compile time, the scale factors are always + * to be used by dividing. + */ + if (p_do_recip != (Int *) NULL) + { +#ifndef NRECIPROCAL + *p_do_recip = Numeric->do_recip ; +#else + *p_do_recip = FALSE ; +#endif + } + + if (Rs != (double *) NULL) + { + Rs1 = Numeric->Rs ; + if (Rs1 == (double *) NULL) + { + /* R is the identity matrix. */ + for (k = 0 ; k < n_row ; k++) + { + Rs [k] = 1.0 ; + } + } + else + { + for (k = 0 ; k < n_row ; k++) + { + Rs [k] = Rs1 [k] ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* free the workspace */ + /* ---------------------------------------------------------------------- */ + + (void) UMF_free ((void *) Wi) ; + (void) UMF_free ((void *) Pattern) ; + ASSERT (UMF_malloc_count == init_count) ; + + return (UMFPACK_OK) ; +} + + +/* ========================================================================== */ +/* === get_L ================================================================ */ +/* ========================================================================== */ + +/* + The matrix L is stored in the following arrays in the Numeric object: + + Int Lpos [0..npiv] + Int Lip [0..npiv], index into Numeric->Memory + Int Lilen [0..npiv] + Unit *(Numeric->Memory), pointer to memory space holding row indices + and numerical values + + where npiv is the number of pivot entries found. If A is n_row-by-n_col, + then npiv <= MIN (n_row,n_col). + + Let L_k denote the pattern of entries in column k of L (excluding the + diagonal). + + An Lchain is a sequence of columns of L whose nonzero patterns are related. + The start of an Lchain is denoted by a negative value of Lip [k]. + + To obtain L_k: + + (1) If column k starts an Lchain, then L_k is stored in its entirety. + |Lip [k]| is an index into Numeric->Memory for the integer row indices + in L_k. The number of entries in the column is |L_k| = Lilen [k]. + This defines the pattern of the "leading" column of this chain. + Lpos [k] is not used for the first column in the chain. Column zero + is always a leading column. + + (2) If column k does not start an Lchain, then L_k is represented as a + superset of L_k-1. Define Lnew_k such that (L_k-1 - {k} union Lnew_k) + = L_k, where Lnew_k and (L_k-1)-{k} are disjoint. Lnew_k are the + entries in L_k that are not in L_k-1. Lpos [k] holds the position of + pivot row index k in the prior pattern L_k-1 (if it is present), so + that the set subtraction (L_k-1)-{k} can be computed quickly, when + computing the pattern of L_k from L_k-1. The number of new entries in + L_k is stored in Lilen [k] = |Lnew_k|. + + Note that this means we must have the pattern L_k-1 to compute L_k. + + In both cases (1) and (2), we obtain the pattern L_k. + + The numerical values are stored in Numeric->Memory, starting at the index + |Lip [k]| + Lilen [k]. It is stored in the same order as the entries + in L_k, after L_k is obtained from cases (1) or (2), above. + + The advantage of using this "packed" data structure is that it can + dramatically reduce the amount of storage needed for the pattern of L. + The disadvantage is that it can be difficult for the user to access, + and it does not match the sparse matrix data structure used in MATLAB. + Thus, this routine is provided to create a conventional sparse matrix + data structure for L, in sparse-row form. A row-form of L appears to + MATLAB to be a column-oriented from of the transpose of L. If you would + like a column-form of L, then use UMFPACK_transpose (an example of this + is in umfpackmex.c). + +*/ +/* ========================================================================== */ + +PRIVATE void get_L +( + Int Lp [ ], /* of size n_row+1 */ + Int Lj [ ], /* of size lnz, where lnz = Lp [n_row] */ + double Lx [ ], /* of size lnz */ +#ifdef COMPLEX + double Lz [ ], /* of size lnz */ +#endif + NumericType *Numeric, + Int Pattern [ ], /* workspace of size n_row */ + Int Wi [ ] /* workspace of size n_row */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int deg, *ip, j, row, n_row, n_col, n_inner, *Lpos, *Lilen, *Lip, p, llen, + lnz2, lp, newLchain, k, pos, npiv, *Li, n1 ; + Entry *xp, value, *Lval ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + DEBUG4 (("get_L start:\n")) ; + n_row = Numeric->n_row ; + n_col = Numeric->n_col ; + n_inner = MIN (n_row, n_col) ; + npiv = Numeric->npiv ; + n1 = Numeric->n1 ; + Lpos = Numeric->Lpos ; + Lilen = Numeric->Lilen ; + Lip = Numeric->Lip ; + deg = 0 ; + + /* ---------------------------------------------------------------------- */ + /* count the nonzeros in each row of L */ + /* ---------------------------------------------------------------------- */ + +#pragma ivdep + for (row = 0 ; row < n_inner ; row++) + { + /* include the diagonal entry in the row counts */ + Wi [row] = 1 ; + } +#pragma ivdep + for (row = n_inner ; row < n_row ; row++) + { + Wi [row] = 0 ; + } + + /* singletons */ + for (k = 0 ; k < n1 ; k++) + { + DEBUG4 (("Singleton k "ID"\n", k)) ; + deg = Lilen [k] ; + if (deg > 0) + { + lp = Lip [k] ; + Li = (Int *) (Numeric->Memory + lp) ; + lp += UNITS (Int, deg) ; + Lval = (Entry *) (Numeric->Memory + lp) ; + for (j = 0 ; j < deg ; j++) + { + row = Li [j] ; + value = Lval [j] ; + DEBUG4 ((" row "ID" k "ID" value", row, k)) ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + Wi [row]++ ; + } + } + } + } + + /* non-singletons */ + for (k = n1 ; k < npiv ; k++) + { + + /* ------------------------------------------------------------------ */ + /* make column of L in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + lp = Lip [k] ; + newLchain = (lp < 0) ; + if (newLchain) + { + lp = -lp ; + deg = 0 ; + DEBUG4 (("start of chain for column of L\n")) ; + } + + /* remove pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" removing row "ID" at position "ID"\n", + k, Pattern [pos], pos)) ; + ASSERT (!newLchain) ; + ASSERT (deg > 0) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + /* concatenate the pattern */ + ip = (Int *) (Numeric->Memory + lp) ; + llen = Lilen [k] ; + for (j = 0 ; j < llen ; j++) + { + row = *ip++ ; + DEBUG4 ((" row "ID" k "ID"\n", row, k)) ; + ASSERT (row > k && row < n_row) ; + Pattern [deg++] = row ; + } + + xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ; + + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Pattern [j], k)) ; + row = Pattern [j] ; + value = *xp++ ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + Wi [row]++ ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* construct the final row form of L */ + /* ---------------------------------------------------------------------- */ + + /* create the row pointers */ + lnz2 = 0 ; + for (row = 0 ; row < n_row ; row++) + { + Lp [row] = lnz2 ; + lnz2 += Wi [row] ; + Wi [row] = Lp [row] ; + } + Lp [n_row] = lnz2 ; + ASSERT (Numeric->lnz + n_inner == lnz2) ; + + /* add entries from the rows of L (singletons) */ + for (k = 0 ; k < n1 ; k++) + { + DEBUG4 (("Singleton k "ID"\n", k)) ; + deg = Lilen [k] ; + if (deg > 0) + { + lp = Lip [k] ; + Li = (Int *) (Numeric->Memory + lp) ; + lp += UNITS (Int, deg) ; + Lval = (Entry *) (Numeric->Memory + lp) ; + for (j = 0 ; j < deg ; j++) + { + row = Li [j] ; + value = Lval [j] ; + DEBUG4 ((" row "ID" k "ID" value", row, k)) ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + p = Wi [row]++ ; + Lj [p] = k ; + Lx [p] = REAL_COMPONENT (value) ; +#ifdef COMPLEX + Lz [p] = IMAG_COMPONENT (value) ; +#endif + } + } + } + } + + /* add entries from the rows of L (non-singletons) */ + for (k = n1 ; k < npiv ; k++) + { + + /* ------------------------------------------------------------------ */ + /* make column of L in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + lp = Lip [k] ; + newLchain = (lp < 0) ; + if (newLchain) + { + lp = -lp ; + deg = 0 ; + DEBUG4 (("start of chain for column of L\n")) ; + } + + /* remove pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" removing row "ID" at position "ID"\n", + k, Pattern [pos], pos)) ; + ASSERT (!newLchain) ; + ASSERT (deg > 0) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + /* concatenate the pattern */ + ip = (Int *) (Numeric->Memory + lp) ; + llen = Lilen [k] ; + for (j = 0 ; j < llen ; j++) + { + row = *ip++ ; + DEBUG4 ((" row "ID" k "ID"\n", row, k)) ; + ASSERT (row > k) ; + Pattern [deg++] = row ; + } + + xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ; + + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Pattern [j], k)) ; + row = Pattern [j] ; + value = *xp++ ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + p = Wi [row]++ ; + Lj [p] = k ; + Lx [p] = REAL_COMPONENT (value) ; +#ifdef COMPLEX + Lz [p] = IMAG_COMPONENT (value) ; +#endif + } + } + } + + /* add all of the diagonal entries (L is unit diagonal) */ + for (row = 0 ; row < n_inner ; row++) + { + p = Wi [row]++ ; + Lj [p] = row ; + Lx [p] = 1. ; +#ifdef COMPLEX + Lz [p] = 0. ; +#endif + ASSERT (Wi [row] == Lp [row+1]) ; + } + +#ifndef NDEBUG + DEBUG6 (("L matrix (stored by rows):")) ; + UMF_dump_col_matrix (Lx, +#ifdef COMPLEX + Lz, +#endif + Lj, Lp, n_inner, n_row, Numeric->lnz+n_inner) ; +#endif + + DEBUG4 (("get_L done:\n")) ; +} + + +/* ========================================================================== */ +/* === get_U ================================================================ */ +/* ========================================================================== */ + +/* + The matrix U is stored in the following arrays in the Numeric object: + + Int Upos [0..npiv] + Int Uip [0..npiv], index into Numeric->Memory + Int Uilen [0..npiv] + Unit *(Numeric->Memory), pointer to memory space holding column indices + and numerical values + + where npiv is the number of pivot entries found. If A is n_row-by-n_col, + then npiv <= MIN (n_row,n_col). + + Let U_k denote the pattern of entries in row k of U (excluding the + diagonal). + + A Uchain is a sequence of columns of U whose nonzero patterns are related. + The start of a Uchain is denoted by a negative value of Uip [k]. + + To obtain U_k-1: + + (1) If row k is the start of a Uchain then Uip [k] is negative and |Uip [k]| + is an index into Numeric->Memory for the integer column indices in + U_k-1. The number of entries in the row is |U_k-1| = Uilen [k]. This + defines the pattern of the "trailing" row of this chain that ends at + row k-1. + + + (2) If row k is not the start of a Uchain, then U_k-1 is a subset of U_k. + The indices in U_k are arranged so that last Uilen [k] entries of + U_k are those indices not in U_k-1. Next, the pivot column index k is + added if it appears in row U_k-1 (it never appears in U_k). Upos [k] + holds the position of pivot column index k in the pattern U_k-1 (if it + is present), so that the set union (U_k-1)+{k} can be computed quickly, + when computing the pattern of U_k-1 from U_k. + + Note that this means we must have the pattern U_k to compute L_k-1. + + In both cases (1) and (2), we obtain the pattern U_k. + + The numerical values are stored in Numeric->Memory. If k is the start of a + Uchain, then the offset is |Uip [k]| plus the size of the space needed to + store the pattern U_k-1. Otherwise, Uip [k] is the offset itself of the + numerical values, since in this case no pattern is stored. + The numerical values are stored in the same order as the entries in U_k, + after U_k is obtained from cases (1) or (2), above. + + The advantage of using this "packed" data structure is that it can + dramatically reduce the amount of storage needed for the pattern of U. + The disadvantage is that it can be difficult for the user to access, + and it does not match the sparse matrix data structure used in MATLAB. + Thus, this routine is provided to create a conventional sparse matrix + data structure for U, in sparse-column form. + +*/ +/* ========================================================================== */ + +PRIVATE void get_U +( + Int Up [ ], /* of size n_col+1 */ + Int Ui [ ], /* of size unz, where unz = Up [n_col] */ + double Ux [ ], /* of size unz */ +#ifdef COMPLEX + double Uz [ ], /* of size unz */ +#endif + NumericType *Numeric, + Int Pattern [ ], /* workspace of size n_col */ + Int Wi [ ] /* workspace of size n_col */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int deg, j, *ip, col, *Upos, *Uilen, *Uip, n_col, ulen, *Usi, + unz2, p, k, up, newUchain, pos, npiv, n1 ; + Entry *xp, *D, value, *Uval ; + +#ifndef NDEBUG + Int nnzpiv = 0 ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + DEBUG4 (("get_U start:\n")) ; + n_col = Numeric->n_col ; + n1 = Numeric->n1 ; + npiv = Numeric->npiv ; + Upos = Numeric->Upos ; + Uilen = Numeric->Uilen ; + Uip = Numeric->Uip ; + D = Numeric->D ; + + /* ---------------------------------------------------------------------- */ + /* count the nonzeros in each column of U */ + /* ---------------------------------------------------------------------- */ + + for (col = 0 ; col < npiv ; col++) + { + /* include the diagonal entry in the column counts */ + DEBUG4 (("D ["ID"] = ", col)) ; + EDEBUG4 (D [col]) ; + Wi [col] = IS_NONZERO (D [col]) ; + DEBUG4 ((" is nonzero: "ID"\n", Wi [col])) ; +#ifndef NDEBUG + nnzpiv += IS_NONZERO (D [col]) ; +#endif + } + DEBUG4 (("nnzpiv "ID" "ID"\n", nnzpiv, Numeric->nnzpiv)) ; + ASSERT (nnzpiv == Numeric->nnzpiv) ; + for (col = npiv ; col < n_col ; col++) + { + /* diagonal entries are zero for structurally singular part */ + Wi [col] = 0 ; + } + + deg = Numeric->ulen ; + if (deg > 0) + { + /* make last pivot row of U (singular matrices only) */ + DEBUG0 (("Last pivot row of U: ulen "ID"\n", deg)) ; + for (j = 0 ; j < deg ; j++) + { + Pattern [j] = Numeric->Upattern [j] ; + DEBUG0 ((" column "ID"\n", Pattern [j])) ; + } + } + + /* non-singletons */ + for (k = npiv-1 ; k >= n1 ; k--) + { + + /* ------------------------------------------------------------------ */ + /* use row k of U */ + /* ------------------------------------------------------------------ */ + + up = Uip [k] ; + ulen = Uilen [k] ; + newUchain = (up < 0) ; + if (newUchain) + { + up = -up ; + xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ; + } + else + { + xp = (Entry *) (Numeric->Memory + up) ; + } + + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" k "ID" col "ID" value\n", k, Pattern [j])) ; + col = Pattern [j] ; + ASSERT (col >= 0 && col < n_col) ; + value = *xp++ ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + Wi [col]++ ; + } + } + + /* ------------------------------------------------------------------ */ + /* make row k-1 of U in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + if (k == n1) break ; + + if (newUchain) + { + /* next row is a new Uchain */ + deg = ulen ; + DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ; + ip = (Int *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = *ip++ ; + DEBUG4 ((" k "ID" col "ID"\n", k-1, col)) ; + ASSERT (k <= col) ; + Pattern [j] = col ; + } + } + else + { + deg -= ulen ; + DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k-1, deg)); + ASSERT (deg >= 0) ; + pos = Upos [k] ; + if (pos != EMPTY) + { + /* add the pivot column */ + DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + } + + /* singletons */ + for (k = n1 - 1 ; k >= 0 ; k--) + { + deg = Uilen [k] ; + DEBUG4 (("Singleton k "ID"\n", k)) ; + if (deg > 0) + { + up = Uip [k] ; + Usi = (Int *) (Numeric->Memory + up) ; + up += UNITS (Int, deg) ; + Uval = (Entry *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = Usi [j] ; + value = Uval [j] ; + DEBUG4 ((" k "ID" col "ID" value", k, col)) ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + Wi [col]++ ; + } + } + } + } + + /* ---------------------------------------------------------------------- */ + /* construct the final column form of U */ + /* ---------------------------------------------------------------------- */ + + /* create the column pointers */ + unz2 = 0 ; + for (col = 0 ; col < n_col ; col++) + { + Up [col] = unz2 ; + unz2 += Wi [col] ; + } + Up [n_col] = unz2 ; + DEBUG1 (("Numeric->unz "ID" npiv "ID" nnzpiv "ID" unz2 "ID"\n", + Numeric->unz, npiv, Numeric->nnzpiv, unz2)) ; + ASSERT (Numeric->unz + Numeric->nnzpiv == unz2) ; + + for (col = 0 ; col < n_col ; col++) + { + Wi [col] = Up [col+1] ; + } + + /* add all of the diagonal entries */ + for (col = 0 ; col < npiv ; col++) + { + if (IS_NONZERO (D [col])) + { + p = --(Wi [col]) ; + Ui [p] = col ; + Ux [p] = REAL_COMPONENT (D [col]) ; +#ifdef COMPLEX + Uz [p] = IMAG_COMPONENT (D [col]) ; +#endif + } + } + + /* add all the entries from the rows of U */ + + deg = Numeric->ulen ; + if (deg > 0) + { + /* make last pivot row of U (singular matrices only) */ + for (j = 0 ; j < deg ; j++) + { + Pattern [j] = Numeric->Upattern [j] ; + } + } + + /* non-singletons */ + for (k = npiv-1 ; k >= n1 ; k--) + { + + /* ------------------------------------------------------------------ */ + /* use row k of U */ + /* ------------------------------------------------------------------ */ + + up = Uip [k] ; + ulen = Uilen [k] ; + newUchain = (up < 0) ; + if (newUchain) + { + up = -up ; + xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ; + } + else + { + xp = (Entry *) (Numeric->Memory + up) ; + } + + xp += deg ; + for (j = deg-1 ; j >= 0 ; j--) + { + DEBUG4 ((" k "ID" col "ID" value", k, Pattern [j])) ; + col = Pattern [j] ; + ASSERT (col >= 0 && col < n_col) ; + value = *(--xp) ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + p = --(Wi [col]) ; + Ui [p] = k ; + Ux [p] = REAL_COMPONENT (value) ; +#ifdef COMPLEX + Uz [p] = IMAG_COMPONENT (value) ; +#endif + } + } + + /* ------------------------------------------------------------------ */ + /* make row k-1 of U in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + if (newUchain) + { + /* next row is a new Uchain */ + deg = ulen ; + DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ; + ip = (Int *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = *ip++ ; + DEBUG4 ((" k "ID" col "ID"\n", k-1, col)) ; + ASSERT (k <= col) ; + Pattern [j] = col ; + } + } + else + { + deg -= ulen ; + DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k-1, deg)); + ASSERT (deg >= 0) ; + pos = Upos [k] ; + if (pos != EMPTY) + { + /* add the pivot column */ + DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + } + + /* singletons */ + for (k = n1 - 1 ; k >= 0 ; k--) + { + deg = Uilen [k] ; + DEBUG4 (("Singleton k "ID"\n", k)) ; + if (deg > 0) + { + up = Uip [k] ; + Usi = (Int *) (Numeric->Memory + up) ; + up += UNITS (Int, deg) ; + Uval = (Entry *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = Usi [j] ; + value = Uval [j] ; + DEBUG4 ((" k "ID" col "ID" value", k, col)) ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + p = --(Wi [col]) ; + Ui [p] = k ; + Ux [p] = REAL_COMPONENT (value) ; +#ifdef COMPLEX + Uz [p] = IMAG_COMPONENT (value) ; +#endif + } + } + } + } + +#ifndef NDEBUG + DEBUG6 (("U matrix:")) ; + UMF_dump_col_matrix (Ux, +#ifdef COMPLEX + Uz, +#endif + Ui, Up, Numeric->n_row, n_col, Numeric->unz + Numeric->nnzpiv) ; +#endif + +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_symbolic.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_symbolic.c new file mode 100644 index 0000000000..6a88f37b7e --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_get_symbolic.c @@ -0,0 +1,192 @@ +/* ========================================================================== */ +/* === UMFPACK_get_symbolic ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Gets the symbolic information held in the Symbolic object. + See umfpack_get_symbolic.h for a more detailed description. +*/ + +#include "umf_internal.h" +#include "umf_valid_symbolic.h" + +GLOBAL Int UMFPACK_get_symbolic +( + Int *p_n_row, + Int *p_n_col, + Int *p_n1, /* number of singletons */ + Int *p_nz, + Int *p_nfr, + Int *p_nchains, + Int P [ ], + Int Q [ ], + Int Front_npivcol [ ], + Int Front_parent [ ], + Int Front_1strow [ ], + Int Front_leftmostdesc [ ], + Int Chain_start [ ], + Int Chain_maxrows [ ], + Int Chain_maxcols [ ], + void *SymbolicHandle +) +{ + SymbolicType *Symbolic ; + Int k, n_row, n_col, n1, nfr, nchains, *p ; + + /* ---------------------------------------------------------------------- */ + /* check inputs */ + /* ---------------------------------------------------------------------- */ + + Symbolic = (SymbolicType *) SymbolicHandle ; + if (!UMF_valid_symbolic (Symbolic)) + { + return (UMFPACK_ERROR_invalid_Symbolic_object) ; + } + + /* ---------------------------------------------------------------------- */ + /* get contents of Symbolic */ + /* ---------------------------------------------------------------------- */ + + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + n1 = Symbolic->n1 ; + nfr = Symbolic->nfr ; + nchains = Symbolic->nchains ; + + if (p_n_row) + { + *p_n_row = n_row ; + } + + if (p_n_col) + { + *p_n_col = n_col ; + } + + if (p_n1) + { + *p_n1 = n1 ; + } + + if (p_nz) + { + *p_nz = Symbolic->nz ; + } + + if (p_nfr) + { + *p_nfr = nfr ; + } + + if (p_nchains) + { + *p_nchains = nchains ; + } + + if (P != (Int *) NULL) + { + Int *Rperm_init, *Diagonal_map ; + Rperm_init = Symbolic->Rperm_init ; + Diagonal_map = Symbolic->Diagonal_map ; + if (Diagonal_map != (Int *) NULL) + { + ASSERT (n_row == n_col) ; + /* next pivot rows are found in the diagonal map */ + for (k = 0 ; k < n_row ; k++) + { + P [k] = Rperm_init [Diagonal_map [k]] ; + } + } + else + { + /* there is no diagonal map. */ + for (k = 0 ; k < n_row ; k++) + { + P [k] = Rperm_init [k] ; + } + } + } + + if (Q != (Int *) NULL) + { + p = Symbolic->Cperm_init ; + for (k = 0 ; k < n_col ; k++) + { + Q [k] = p [k] ; + } + } + + if (Front_npivcol != (Int *) NULL) + { + p = Symbolic->Front_npivcol ; + for (k = 0 ; k <= nfr ; k++) + { + Front_npivcol [k] = p [k] ; + } + } + + if (Front_parent != (Int *) NULL) + { + p = Symbolic->Front_parent ; + for (k = 0 ; k <= nfr ; k++) + { + Front_parent [k] = p [k] ; + } + } + + if (Front_1strow != (Int *) NULL) + { + p = Symbolic->Front_1strow ; + for (k = 0 ; k <= nfr ; k++) + { + Front_1strow [k] = p [k] ; + } + } + + if (Front_leftmostdesc != (Int *) NULL) + { + p = Symbolic->Front_leftmostdesc ; + for (k = 0 ; k <= nfr ; k++) + { + Front_leftmostdesc [k] = p [k] ; + } + } + + if (Chain_start != (Int *) NULL) + { + p = Symbolic->Chain_start ; + for (k = 0 ; k <= nchains ; k++) + { + Chain_start [k] = p [k] ; + } + } + + if (Chain_maxrows != (Int *) NULL) + { + p = Symbolic->Chain_maxrows ; + for (k = 0 ; k < nchains ; k++) + { + Chain_maxrows [k] = p [k] ; + } + Chain_maxrows [nchains] = 0 ; + } + + if (Chain_maxcols != (Int *) NULL) + { + p = Symbolic->Chain_maxcols ; + for (k = 0 ; k < nchains ; k++) + { + Chain_maxcols [k] = p [k] ; + } + Chain_maxcols [nchains] = 0 ; + } + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_load_numeric.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_load_numeric.c new file mode 100644 index 0000000000..db98757cb7 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_load_numeric.c @@ -0,0 +1,162 @@ +/* ========================================================================== */ +/* === UMFPACK_load_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Loads a Numeric object from a file created by + umfpack_*_save_numeric. +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" +#include "umf_malloc.h" +#include "umf_free.h" + +#define READ(object,type,n) \ +{ \ + object = (type *) UMF_malloc (n, sizeof (type)) ; \ + if (object == (type *) NULL) \ + { \ + UMFPACK_free_numeric ((void **) &Numeric) ; \ + fclose (f) ; \ + return (UMFPACK_ERROR_out_of_memory) ; \ + } \ + if (fread (object, sizeof (type), n, f) != n) \ + { \ + UMFPACK_free_numeric ((void **) &Numeric) ; \ + fclose (f) ; \ + return (UMFPACK_ERROR_file_IO) ; \ + } \ + if (ferror (f)) \ + { \ + UMFPACK_free_numeric ((void **) &Numeric) ; \ + fclose (f) ; \ + return (UMFPACK_ERROR_file_IO) ; \ + } \ +} + +/* ========================================================================== */ +/* === UMFPACK_load_numeric ================================================= */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_load_numeric +( + void **NumericHandle, + char *user_filename +) +{ + NumericType *Numeric ; + char *filename ; + FILE *f ; + + *NumericHandle = (void *) NULL ; + + /* ---------------------------------------------------------------------- */ + /* get the filename, or use the default name if filename is NULL */ + /* ---------------------------------------------------------------------- */ + + if (user_filename == (char *) NULL) + { + filename = "numeric.umf" ; + } + else + { + filename = user_filename ; + } + f = fopen (filename, "rb") ; + if (!f) + { + return (UMFPACK_ERROR_file_IO) ; + } + + /* ---------------------------------------------------------------------- */ + /* read the Numeric header from the file, in binary */ + /* ---------------------------------------------------------------------- */ + + Numeric = (NumericType *) UMF_malloc (1, sizeof (NumericType)) ; + if (Numeric == (NumericType *) NULL) + { + fclose (f) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + if (fread (Numeric, sizeof (NumericType), 1, f) != 1) + { + (void) UMF_free ((void *) Numeric) ; + fclose (f) ; + return (UMFPACK_ERROR_file_IO) ; + } + if (ferror (f)) + { + (void) UMF_free ((void *) Numeric) ; + fclose (f) ; + return (UMFPACK_ERROR_file_IO) ; + } + + if (Numeric->valid != NUMERIC_VALID || Numeric->n_row <= 0 || + Numeric->n_col <= 0 || Numeric->npiv < 0 || Numeric->ulen < 0 || + Numeric->size <= 0) + { + /* Numeric does not point to a NumericType object */ + (void) UMF_free ((void *) Numeric) ; + fclose (f) ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + Numeric->D = (Entry *) NULL ; + Numeric->Rperm = (Int *) NULL ; + Numeric->Cperm = (Int *) NULL ; + Numeric->Lpos = (Int *) NULL ; + Numeric->Lilen = (Int *) NULL ; + Numeric->Lip = (Int *) NULL ; + Numeric->Upos = (Int *) NULL ; + Numeric->Uilen = (Int *) NULL ; + Numeric->Uip = (Int *) NULL ; + Numeric->Rs = (double *) NULL ; + Numeric->Memory = (Unit *) NULL ; + Numeric->Upattern = (Int *) NULL ; + + /* umfpack_free_numeric can now be safely called if an error occurs */ + + /* ---------------------------------------------------------------------- */ + /* read the rest of the Numeric object */ + /* ---------------------------------------------------------------------- */ + + READ (Numeric->D, Entry, MIN (Numeric->n_row, Numeric->n_col)+1) ; + READ (Numeric->Rperm, Int, Numeric->n_row+1) ; + READ (Numeric->Cperm, Int, Numeric->n_col+1) ; + READ (Numeric->Lpos, Int, Numeric->npiv+1) ; + READ (Numeric->Lilen, Int, Numeric->npiv+1) ; + READ (Numeric->Lip, Int, Numeric->npiv+1) ; + READ (Numeric->Upos, Int, Numeric->npiv+1) ; + READ (Numeric->Uilen, Int, Numeric->npiv+1) ; + READ (Numeric->Uip, Int, Numeric->npiv+1) ; + if (Numeric->scale != UMFPACK_SCALE_NONE) + { + READ (Numeric->Rs, double, Numeric->n_row) ; + } + if (Numeric->ulen > 0) + { + READ (Numeric->Upattern, Int, Numeric->ulen+1) ; + } + READ (Numeric->Memory, Unit, Numeric->size) ; + + /* close the file */ + fclose (f) ; + + /* make sure the Numeric object is valid */ + if (!UMF_valid_numeric (Numeric)) + { + UMFPACK_free_numeric ((void **) &Numeric) ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + *NumericHandle = (void *) Numeric ; + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_load_symbolic.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_load_symbolic.c new file mode 100644 index 0000000000..dceea052f7 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_load_symbolic.c @@ -0,0 +1,166 @@ +/* ========================================================================== */ +/* === UMFPACK_load_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Loads a Symbolic object from a file created by + umfpack_*_save_symbolic. +*/ + +#include "umf_internal.h" +#include "umf_valid_symbolic.h" +#include "umf_malloc.h" +#include "umf_free.h" + +#define READ(object,type,n) \ +{ \ + object = (type *) UMF_malloc (n, sizeof (type)) ; \ + if (object == (type *) NULL) \ + { \ + UMFPACK_free_symbolic ((void **) &Symbolic) ; \ + fclose (f) ; \ + return (UMFPACK_ERROR_out_of_memory) ; \ + } \ + if (fread (object, sizeof (type), n, f) != n) \ + { \ + UMFPACK_free_symbolic ((void **) &Symbolic) ; \ + fclose (f) ; \ + return (UMFPACK_ERROR_file_IO) ; \ + } \ + if (ferror (f)) \ + { \ + UMFPACK_free_symbolic ((void **) &Symbolic) ; \ + fclose (f) ; \ + return (UMFPACK_ERROR_file_IO) ; \ + } \ +} + +/* ========================================================================== */ +/* === UMFPACK_load_symbolic ================================================ */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_load_symbolic +( + void **SymbolicHandle, + char *user_filename +) +{ + SymbolicType *Symbolic ; + char *filename ; + FILE *f ; + + *SymbolicHandle = (void *) NULL ; + + /* ---------------------------------------------------------------------- */ + /* get the filename, or use the default name if filename is NULL */ + /* ---------------------------------------------------------------------- */ + + if (user_filename == (char *) NULL) + { + filename = "symbolic.umf" ; + } + else + { + filename = user_filename ; + } + f = fopen (filename, "rb") ; + if (!f) + { + return (UMFPACK_ERROR_file_IO) ; + } + + /* ---------------------------------------------------------------------- */ + /* read the Symbolic header from the file, in binary */ + /* ---------------------------------------------------------------------- */ + + Symbolic = (SymbolicType *) UMF_malloc (1, sizeof (SymbolicType)) ; + if (Symbolic == (SymbolicType *) NULL) + { + fclose (f) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + if (fread (Symbolic, sizeof (SymbolicType), 1, f) != 1) + { + (void) UMF_free ((void *) Symbolic) ; + fclose (f) ; + return (UMFPACK_ERROR_file_IO) ; + } + if (ferror (f)) + { + (void) UMF_free ((void *) Symbolic) ; + fclose (f) ; + return (UMFPACK_ERROR_file_IO) ; + } + + if (Symbolic->valid != SYMBOLIC_VALID || Symbolic->n_row <= 0 || + Symbolic->n_col <= 0 || Symbolic->nfr < 0 || Symbolic->nchains < 0 || + Symbolic->esize < 0) + { + /* Symbolic does not point to a Symbolic object */ + (void) UMF_free ((void *) Symbolic) ; + fclose (f) ; + return (UMFPACK_ERROR_invalid_Symbolic_object) ; + } + + Symbolic->Cperm_init = (Int *) NULL ; + Symbolic->Rperm_init = (Int *) NULL ; + Symbolic->Front_npivcol = (Int *) NULL ; + Symbolic->Front_parent = (Int *) NULL ; + Symbolic->Front_1strow = (Int *) NULL ; + Symbolic->Front_leftmostdesc = (Int *) NULL ; + Symbolic->Chain_start = (Int *) NULL ; + Symbolic->Chain_maxrows = (Int *) NULL ; + Symbolic->Chain_maxcols = (Int *) NULL ; + Symbolic->Cdeg = (Int *) NULL ; + Symbolic->Rdeg = (Int *) NULL ; + Symbolic->Esize = (Int *) NULL ; + Symbolic->Diagonal_map = (Int *) NULL ; + + /* umfpack_free_symbolic can now be safely called if an error occurs */ + + /* ---------------------------------------------------------------------- */ + /* read the rest of the Symbolic object */ + /* ---------------------------------------------------------------------- */ + + READ (Symbolic->Cperm_init, Int, Symbolic->n_col+1) ; + READ (Symbolic->Rperm_init, Int, Symbolic->n_row+1) ; + READ (Symbolic->Front_npivcol, Int, Symbolic->nfr+1) ; + READ (Symbolic->Front_parent, Int, Symbolic->nfr+1) ; + READ (Symbolic->Front_1strow, Int, Symbolic->nfr+1) ; + READ (Symbolic->Front_leftmostdesc, Int, Symbolic->nfr+1) ; + READ (Symbolic->Chain_start, Int, Symbolic->nchains+1) ; + READ (Symbolic->Chain_maxrows, Int, Symbolic->nchains+1) ; + READ (Symbolic->Chain_maxcols, Int, Symbolic->nchains+1) ; + READ (Symbolic->Cdeg, Int, Symbolic->n_col+1) ; + READ (Symbolic->Rdeg, Int, Symbolic->n_row+1) ; + if (Symbolic->esize > 0) + { + /* only when dense rows are present */ + READ (Symbolic->Esize, Int, Symbolic->esize) ; + } + if (Symbolic->prefer_diagonal) + { + /* only when diagonal pivoting is prefered */ + READ (Symbolic->Diagonal_map, Int, Symbolic->n_col+1) ; + } + + /* close the file */ + fclose (f) ; + + /* make sure the Symbolic object is valid */ + if (!UMF_valid_symbolic (Symbolic)) + { + UMFPACK_free_symbolic ((void **) &Symbolic) ; + return (UMFPACK_ERROR_invalid_Symbolic_object) ; + } + + *SymbolicHandle = (void *) Symbolic ; + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_numeric.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_numeric.c new file mode 100644 index 0000000000..9456d911ed --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_numeric.c @@ -0,0 +1,796 @@ +/* ========================================================================== */ +/* === UMFPACK_numeric ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Factorizes A into its LU factors, given a symbolic + pre-analysis computed by UMFPACK_symbolic. See umfpack_numeric.h for a + description. + + Dynamic memory allocation: substantial. See comments (1) through (7), + below. If an error occurs, all allocated space is free'd by UMF_free. + If successful, the Numeric object contains 11 to 13 objects allocated by + UMF_malloc that hold the LU factors of the input matrix. +*/ + +#include "umf_internal.h" +#include "umf_valid_symbolic.h" +#include "umf_set_stats.h" +#include "umf_kernel.h" +#include "umf_malloc.h" +#include "umf_free.h" +#include "umf_realloc.h" + +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif + +PRIVATE Int work_alloc +( + WorkType *Work, + SymbolicType *Symbolic +) ; + +PRIVATE void free_work +( + WorkType *Work +) ; + +PRIVATE Int numeric_alloc +( + NumericType **NumericHandle, + SymbolicType *Symbolic, + double alloc_init, + Int scale +) ; + +PRIVATE void error +( + NumericType **Numeric, + WorkType *Work +) ; + + +/* ========================================================================== */ +/* === UMFPACK_numeric ====================================================== */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_numeric +( + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + void *SymbolicHandle, + void **NumericHandle, + const double Control [UMFPACK_CONTROL], + double User_Info [UMFPACK_INFO] +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + NumericType *Numeric ; + SymbolicType *Symbolic ; + WorkType WorkSpace, *Work ; + Int n_row, n_col, n_inner, newsize, i, status, *inew, npiv, ulen, scale ; + Unit *mnew ; + double Info2 [UMFPACK_INFO], *Info, alloc_init, relpt, relpt2, droptol, + front_alloc_init, stats [2] ; + + /* ---------------------------------------------------------------------- */ + /* get the amount of time used by the process so far */ + /* ---------------------------------------------------------------------- */ + + umfpack_tic (stats) ; + + /* ---------------------------------------------------------------------- */ + /* initialize and check inputs */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + UMF_dump_start ( ) ; + init_count = UMF_malloc_count ; + DEBUGm4 (("\nUMFPACK numeric: U transpose version\n")) ; +#endif + + /* If front_alloc_init negative then allocate that size of front in + * UMF_start_front. If alloc_init negative, then allocate that initial + * size of Numeric->Memory. */ + + relpt = GET_CONTROL (UMFPACK_PIVOT_TOLERANCE, + UMFPACK_DEFAULT_PIVOT_TOLERANCE) ; + relpt2 = GET_CONTROL (UMFPACK_SYM_PIVOT_TOLERANCE, + UMFPACK_DEFAULT_SYM_PIVOT_TOLERANCE) ; + alloc_init = GET_CONTROL (UMFPACK_ALLOC_INIT, UMFPACK_DEFAULT_ALLOC_INIT) ; + front_alloc_init = GET_CONTROL (UMFPACK_FRONT_ALLOC_INIT, + UMFPACK_DEFAULT_FRONT_ALLOC_INIT) ; + scale = GET_CONTROL (UMFPACK_SCALE, UMFPACK_DEFAULT_SCALE) ; + droptol = GET_CONTROL (UMFPACK_DROPTOL, UMFPACK_DEFAULT_DROPTOL) ; + + relpt = MAX (0.0, MIN (relpt, 1.0)) ; + relpt2 = MAX (0.0, MIN (relpt2, 1.0)) ; + droptol = MAX (0.0, droptol) ; + front_alloc_init = MIN (1.0, front_alloc_init) ; + + if (scale != UMFPACK_SCALE_NONE && scale != UMFPACK_SCALE_MAX) + { + scale = UMFPACK_DEFAULT_SCALE ; + } + + if (User_Info != (double *) NULL) + { + /* return Info in user's array */ + Info = User_Info ; + /* clear the parts of Info that are set by UMFPACK_numeric */ + for (i = UMFPACK_NUMERIC_SIZE ; i <= UMFPACK_MAX_FRONT_NCOLS ; i++) + { + Info [i] = EMPTY ; + } + for (i = UMFPACK_NUMERIC_DEFRAG ; i < UMFPACK_IR_TAKEN ; i++) + { + Info [i] = EMPTY ; + } + } + else + { + /* no Info array passed - use local one instead */ + Info = Info2 ; + for (i = 0 ; i < UMFPACK_INFO ; i++) + { + Info [i] = EMPTY ; + } + } + + Symbolic = (SymbolicType *) SymbolicHandle ; + Numeric = (NumericType *) NULL ; + if (!UMF_valid_symbolic (Symbolic)) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_Symbolic_object ; + return (UMFPACK_ERROR_invalid_Symbolic_object) ; + } + + /* compute alloc_init automatically for AMD ordering */ + if (Symbolic->ordering == UMFPACK_ORDERING_AMD && alloc_init >= 0) + { + alloc_init = (Symbolic->nz + Symbolic->amd_lunz) / Symbolic->lunz_bound; + alloc_init = MIN (1.0, alloc_init) ; + alloc_init *= UMF_REALLOC_INCREASE ; + } + + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + n_inner = MIN (n_row, n_col) ; + + /* check for integer overflow in Numeric->Memory minimum size */ + if (INT_OVERFLOW (Symbolic->dnum_mem_init_usage * sizeof (Unit))) + { + /* :: int overflow, initial Numeric->Memory size :: */ + /* There's no hope to allocate a Numeric object big enough simply to + * hold the initial matrix, so return an out-of-memory condition */ + DEBUGm4 (("out of memory: numeric int overflow\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + Info [UMFPACK_STATUS] = UMFPACK_OK ; + Info [UMFPACK_NROW] = n_row ; + Info [UMFPACK_NCOL] = n_col ; + Info [UMFPACK_SIZE_OF_UNIT] = (double) (sizeof (Unit)) ; + + if (!Ap || !Ai || !Ax || !NumericHandle +#ifdef COMPLEX + || !Az +#endif + ) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + + Info [UMFPACK_NZ] = Ap [n_col] ; + *NumericHandle = (void *) NULL ; + + /* ---------------------------------------------------------------------- */ + /* allocate the Work object */ + /* ---------------------------------------------------------------------- */ + + /* (1) calls UMF_malloc 15 or 17 times, to obtain temporary workspace of + * size c+1 Entry's and 2*(n_row+1) + 3*(n_col+1) + (n_col+n_inner+1) + + * (nn+1) + * 3*(c+1) + 2*(r+1) + max(r,c) + (nfr+1) integers plus 2*nn + * more integers if diagonal pivoting is to be done. r is the maximum + * number of rows in any frontal matrix, c is the maximum number of columns + * in any frontal matrix, n_inner is min (n_row,n_col), nn is + * max (n_row,n_col), and nfr is the number of frontal matrices. For a + * square matrix, this is c+1 Entry's and about 8n + 3c + 2r + max(r,c) + + * nfr integers, plus 2n more for diagonal pivoting. + */ + + Work = &WorkSpace ; + Work->n_row = n_row ; + Work->n_col = n_col ; + Work->nfr = Symbolic->nfr ; + Work->nb = Symbolic->nb ; + Work->n1 = Symbolic->n1 ; + + if (!work_alloc (Work, Symbolic)) + { + DEBUGm4 (("out of memory: numeric work\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Numeric, Work) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + ASSERT (UMF_malloc_count == init_count + 16 + 2*Symbolic->prefer_diagonal) ; + + /* ---------------------------------------------------------------------- */ + /* allocate Numeric object */ + /* ---------------------------------------------------------------------- */ + + /* (2) calls UMF_malloc 10 or 11 times, for a total space of + * sizeof (NumericType) bytes, 4*(n_row+1) + 4*(n_row+1) integers, and + * (n_inner+1) Entry's, plus n_row Entry's if row scaling is to be done. + * sizeof (NumericType) is a small constant. Next, it calls UMF_malloc + * once, for the variable-sized part of the Numeric object + * (Numeric->Memory). The size of this object is the larger of + * (Control [UMFPACK_ALLOC_INIT]) * (the approximate upper bound computed + * by UMFPACK_symbolic), and the minimum required to start the numerical + * factorization. * This request is reduced if it fails. + */ + + if (!numeric_alloc (&Numeric, Symbolic, alloc_init, scale)) + { + DEBUGm4 (("out of memory: initial numeric\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Numeric, Work) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n", + init_count, UMF_malloc_count)) ; + ASSERT (UMF_malloc_count == init_count + + (16 + 2*Symbolic->prefer_diagonal) + + (11 + (scale != UMFPACK_SCALE_NONE))) ; + + /* set control parameters */ + Numeric->relpt = relpt ; + Numeric->relpt2 = relpt2 ; + Numeric->droptol = droptol ; + Numeric->alloc_init = alloc_init ; + Numeric->front_alloc_init = front_alloc_init ; + Numeric->scale = scale ; + + DEBUG0 (("umf relpt %g %g init %g %g inc %g red %g\n", + relpt, relpt2, alloc_init, front_alloc_init, + UMF_REALLOC_INCREASE, UMF_REALLOC_REDUCTION)) ; + + /* ---------------------------------------------------------------------- */ + /* scale and factorize */ + /* ---------------------------------------------------------------------- */ + + /* (3) During numerical factorization (inside UMF_kernel), the variable-size + * block of memory is increased in size via a call to UMF_realloc if it is + * found to be too small. During factorization, this block holds the + * pattern and values of L and U at the top end, and the elements + * (contibution blocks) and the current frontal matrix (Work->F*) at the + * bottom end. The peak size of the variable-sized object is estimated in + * UMFPACK_*symbolic (Info [UMFPACK_VARIABLE_PEAK_ESTIMATE]), although this + * upper bound can be very loose. The size of the Symbolic object + * (which is currently allocated) is in Info [UMFPACK_SYMBOLIC_SIZE], and + * is between 2*n and 13*n integers. + */ + + DEBUG0 (("Calling umf_kernel\n")) ; + status = UMF_kernel (Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + Numeric, Work, Symbolic) ; + + Info [UMFPACK_STATUS] = status ; + if (status < UMFPACK_OK) + { + /* out of memory, or pattern has changed */ + error (&Numeric, Work) ; + return (status) ; + } + + Info [UMFPACK_FORCED_UPDATES] = Work->nforced ; + Info [UMFPACK_VARIABLE_INIT] = Numeric->init_usage ; + if (Symbolic->prefer_diagonal) + { + Info [UMFPACK_NOFF_DIAG] = Work->noff_diagonal ; + } + + DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n", + init_count, UMF_malloc_count)) ; + + npiv = Numeric->npiv ; /* = n_inner for nonsingular matrices */ + ulen = Numeric->ulen ; /* = 0 for square nonsingular matrices */ + + /* ---------------------------------------------------------------------- */ + /* free Work object */ + /* ---------------------------------------------------------------------- */ + + /* (4) After numerical factorization all of the objects allocated in step + * (1) are freed via UMF_free, except that one object of size n_col+1 is + * kept if there are off-diagonal nonzeros in the last pivot row (can only + * occur for singular or rectangular matrices). This is Work->Upattern, + * which is transfered to Numeric->Upattern if ulen > 0. + */ + + DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n", + init_count, UMF_malloc_count)) ; + + free_work (Work) ; + + DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n", + init_count, UMF_malloc_count)) ; + DEBUG0 (("Numeric->ulen: "ID" scale: "ID"\n", ulen, scale)) ; + ASSERT (UMF_malloc_count == init_count + (ulen > 0) + + (11 + (scale != UMFPACK_SCALE_NONE))) ; + + /* ---------------------------------------------------------------------- */ + /* reduce Lpos, Lilen, Lip, Upos, Uilen and Uip to size npiv+1 */ + /* ---------------------------------------------------------------------- */ + + /* (5) Six components of the Numeric object are reduced in size if the + * matrix is singular or rectangular. The original size is 3*(n_row+1) + + * 3*(n_col+1) integers. The new size is 6*(npiv+1) integers. For + * square non-singular matrices, these two sizes are the same. + */ + + if (npiv < n_row) + { + /* reduce Lpos, Uilen, and Uip from size n_row+1 to size npiv */ + inew = (Int *) UMF_realloc (Numeric->Lpos, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Lpos = inew ; + } + inew = (Int *) UMF_realloc (Numeric->Uilen, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Uilen = inew ; + } + inew = (Int *) UMF_realloc (Numeric->Uip, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Uip = inew ; + } + } + + if (npiv < n_col) + { + /* reduce Upos, Lilen, and Lip from size n_col+1 to size npiv */ + inew = (Int *) UMF_realloc (Numeric->Upos, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Upos = inew ; + } + inew = (Int *) UMF_realloc (Numeric->Lilen, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Lilen = inew ; + } + inew = (Int *) UMF_realloc (Numeric->Lip, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Lip = inew ; + } + } + + /* ---------------------------------------------------------------------- */ + /* reduce Numeric->Upattern from size n_col+1 to size ulen+1 */ + /* ---------------------------------------------------------------------- */ + + /* (6) The size of Numeric->Upattern (formerly Work->Upattern) is reduced + * from size n_col+1 to size ulen + 1. If ulen is zero, the object does + * not exist. */ + + DEBUG4 (("ulen: "ID" Upattern "ID"\n", ulen, (Int) Numeric->Upattern)) ; + ASSERT (IMPLIES (ulen == 0, Numeric->Upattern == (Int *) NULL)) ; + if (ulen > 0 && ulen < n_col) + { + inew = (Int *) UMF_realloc (Numeric->Upattern, ulen+1, sizeof (Int)) ; + if (inew) + { + Numeric->Upattern = inew ; + } + } + + /* ---------------------------------------------------------------------- */ + /* reduce Numeric->Memory to hold just the LU factors at the head */ + /* ---------------------------------------------------------------------- */ + + /* (7) The variable-sized block (Numeric->Memory) is reduced to hold just L + * and U, via a call to UMF_realloc, since the frontal matrices are no + * longer needed. + */ + + newsize = Numeric->ihead ; + if (newsize < Numeric->size) + { + mnew = (Unit *) UMF_realloc (Numeric->Memory, newsize, sizeof (Unit)) ; + if (mnew) + { + /* realloc succeeded (how can it fail since the size is reduced?) */ + Numeric->Memory = mnew ; + Numeric->size = newsize ; + } + } + Numeric->ihead = Numeric->size ; + Numeric->itail = Numeric->ihead ; + Numeric->tail_usage = 0 ; + Numeric->ibig = EMPTY ; + /* UMF_mem_alloc_tail_block can no longer be called (no tail marker) */ + + /* ---------------------------------------------------------------------- */ + /* report the results and return the Numeric object */ + /* ---------------------------------------------------------------------- */ + + UMF_set_stats ( + Info, + Symbolic, + (double) Numeric->max_usage, /* actual peak Numeric->Memory */ + (double) Numeric->size, /* actual final Numeric->Memory */ + Numeric->flops, /* actual "true flops" */ + (double) Numeric->lnz + n_inner, /* actual nz in L */ + (double) Numeric->unz + Numeric->nnzpiv, /* actual nz in U */ + (double) Numeric->maxfrsize, /* actual largest front size */ + (double) ulen, /* actual Numeric->Upattern size */ + (double) npiv, /* actual # pivots found */ + (double) Numeric->maxnrows, /* actual largest #rows in front */ + (double) Numeric->maxncols, /* actual largest #cols in front */ + scale != UMFPACK_SCALE_NONE, + Symbolic->prefer_diagonal, + ACTUAL) ; + + Info [UMFPACK_ALLOC_INIT_USED] = Numeric->alloc_init ; + Info [UMFPACK_NUMERIC_DEFRAG] = Numeric->ngarbage ; + Info [UMFPACK_NUMERIC_REALLOC] = Numeric->nrealloc ; + Info [UMFPACK_NUMERIC_COSTLY_REALLOC] = Numeric->ncostly ; + Info [UMFPACK_COMPRESSED_PATTERN] = Numeric->isize ; + Info [UMFPACK_LU_ENTRIES] = Numeric->nLentries + Numeric->nUentries + + Numeric->npiv ; + Info [UMFPACK_UDIAG_NZ] = Numeric->nnzpiv ; + Info [UMFPACK_RSMIN] = Numeric->rsmin ; + Info [UMFPACK_RSMAX] = Numeric->rsmax ; + Info [UMFPACK_WAS_SCALED] = Numeric->scale ; + + /* nz in L and U with no dropping of small entries */ + Info [UMFPACK_ALL_LNZ] = Numeric->all_lnz + n_inner ; + Info [UMFPACK_ALL_UNZ] = Numeric->all_unz + Numeric->nnzpiv ; + Info [UMFPACK_NZDROPPED] = + (Numeric->all_lnz - Numeric->lnz) + + (Numeric->all_unz - Numeric->unz) ; + + /* estimate of the reciprocal of the condition number. */ + if (SCALAR_IS_ZERO (Numeric->min_udiag) + || SCALAR_IS_ZERO (Numeric->max_udiag) + || SCALAR_IS_NAN (Numeric->min_udiag) + || SCALAR_IS_NAN (Numeric->max_udiag)) + { + /* rcond is zero if there is any zero or NaN on the diagonal */ + Numeric->rcond = 0.0 ; + } + else + { + /* estimate of the recipricol of the condition number. */ + /* This is NaN if diagonal is zero-free, but has one or more NaN's. */ + Numeric->rcond = Numeric->min_udiag / Numeric->max_udiag ; + } + Info [UMFPACK_UMIN] = Numeric->min_udiag ; + Info [UMFPACK_UMAX] = Numeric->max_udiag ; + Info [UMFPACK_RCOND] = Numeric->rcond ; + + if (Numeric->nnzpiv < n_inner + || SCALAR_IS_ZERO (Numeric->rcond) || SCALAR_IS_NAN (Numeric->rcond)) + { + /* there are zeros and/or NaN's on the diagonal of U */ + DEBUG0 (("Warning, matrix is singular in umfpack_numeric\n")) ; + DEBUG0 (("nnzpiv "ID" n_inner "ID" rcond %g\n", Numeric->nnzpiv, + n_inner, Numeric->rcond)) ; + status = UMFPACK_WARNING_singular_matrix ; + Info [UMFPACK_STATUS] = status ; + } + + Numeric->valid = NUMERIC_VALID ; + *NumericHandle = (void *) Numeric ; + + /* Numeric has 11 to 13 objects */ + ASSERT (UMF_malloc_count == init_count + 11 + + + (ulen > 0) /* Numeric->Upattern */ + + (scale != UMFPACK_SCALE_NONE)) ; /* Numeric->Rs */ + + /* ---------------------------------------------------------------------- */ + /* get the time used by UMFPACK_numeric */ + /* ---------------------------------------------------------------------- */ + + umfpack_toc (stats) ; + Info [UMFPACK_NUMERIC_WALLTIME] = stats [0] ; + Info [UMFPACK_NUMERIC_TIME] = stats [1] ; + + /* return UMFPACK_OK or UMFPACK_WARNING_singular_matrix */ + return (status) ; + +} + + +/* ========================================================================== */ +/* === numeric_alloc ======================================================== */ +/* ========================================================================== */ + +/* Allocate the Numeric object */ + +PRIVATE Int numeric_alloc +( + NumericType **NumericHandle, + SymbolicType *Symbolic, + double alloc_init, + Int scale +) +{ + Int n_row, n_col, n_inner, min_usage, trying ; + NumericType *Numeric ; + double nsize, bsize ; + + DEBUG0 (("numeric alloc:\n")) ; + + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + n_inner = MIN (n_row, n_col) ; + *NumericHandle = (NumericType *) NULL ; + + /* 1 allocation: accounted for in UMF_set_stats (num_On_size1), + * free'd in umfpack_free_numeric */ + Numeric = (NumericType *) UMF_malloc (1, sizeof (NumericType)) ; + + if (!Numeric) + { + return (FALSE) ; /* out of memory */ + } + Numeric->valid = 0 ; + *NumericHandle = Numeric ; + + /* 9 allocations: accounted for in UMF_set_stats (num_On_size1), + * free'd in umfpack_free_numeric */ + Numeric->D = (Entry *) UMF_malloc (n_inner+1, sizeof (Entry)) ; + Numeric->Rperm = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + Numeric->Cperm = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Numeric->Lpos = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + Numeric->Lilen = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Numeric->Lip = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Numeric->Upos = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Numeric->Uilen = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + Numeric->Uip = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + + /* 1 allocation if scaling: in UMF_set_stats (num_On_size1), + * free'd in umfpack_free_numeric */ + if (scale != UMFPACK_SCALE_NONE) + { + DEBUG0 (("Allocating scale factors\n")) ; + Numeric->Rs = (double *) UMF_malloc (n_row, sizeof (double)) ; + } + else + { + DEBUG0 (("No scale factors allocated (R = I)\n")) ; + Numeric->Rs = (double *) NULL ; + } + + Numeric->Memory = (Unit *) NULL ; + + /* Upattern has already been allocated as part of the Work object. If + * the matrix is singular or rectangular, and there are off-diagonal + * nonzeros in the last pivot row, then Work->Upattern is not free'd. + * Instead it is transfered to Numeric->Upattern. If it exists, + * Numeric->Upattern is free'd in umfpack_free_numeric. */ + Numeric->Upattern = (Int *) NULL ; /* used for singular matrices only */ + + if (!Numeric->D || !Numeric->Rperm || !Numeric->Cperm || !Numeric->Upos || + !Numeric->Lpos || !Numeric->Lilen || !Numeric->Uilen || !Numeric->Lip || + !Numeric->Uip || (scale != UMFPACK_SCALE_NONE && !Numeric->Rs)) + { + return (FALSE) ; /* out of memory */ + } + + /* ---------------------------------------------------------------------- */ + /* allocate initial Numeric->Memory for LU factors and elements */ + /* ---------------------------------------------------------------------- */ + + if (alloc_init < 0) + { + /* -alloc_init is the exact size to initially allocate */ + nsize = -alloc_init ; + } + else + { + /* alloc_init is a ratio of the upper bound memory usage */ + nsize = (alloc_init * Symbolic->num_mem_usage_est) + 1 ; + } + min_usage = Symbolic->num_mem_init_usage ; + + /* Numeric->Memory must be large enough for UMF_kernel_init */ + nsize = MAX (min_usage, nsize) ; + + /* Numeric->Memory cannot be larger in size than Int_MAX / sizeof(Unit) */ + /* For ILP32 mode: 2GB (nsize cannot be bigger than 256 Mwords) */ + bsize = ((double) Int_MAX) / sizeof (Unit) - 1 ; + DEBUG0 (("bsize %g\n", bsize)) ; + nsize = MIN (nsize, bsize) ; + + Numeric->size = (Int) nsize ; + + DEBUG0 (("Num init %g usage_est %g numsize "ID" minusage "ID"\n", + alloc_init, Symbolic->num_mem_usage_est, Numeric->size, min_usage)) ; + + /* allocates 1 object: */ + /* keep trying until successful, or memory request is too small */ + trying = TRUE ; + while (trying) + { + Numeric->Memory = (Unit *) UMF_malloc (Numeric->size, sizeof (Unit)) ; + if (Numeric->Memory) + { + DEBUG0 (("Successful Numeric->size: "ID"\n", Numeric->size)) ; + return (TRUE) ; + } + /* too much, reduce the request (but not below the minimum) */ + /* and try again */ + trying = Numeric->size > min_usage ; + Numeric->size = (Int) + (UMF_REALLOC_REDUCTION * ((double) Numeric->size)) ; + Numeric->size = MAX (min_usage, Numeric->size) ; + } + + return (FALSE) ; /* we failed to allocate Numeric->Memory */ +} + + +/* ========================================================================== */ +/* === work_alloc =========================================================== */ +/* ========================================================================== */ + +/* Allocate the Work object. Return TRUE if successful. */ + +PRIVATE Int work_alloc +( + WorkType *Work, + SymbolicType *Symbolic +) +{ + Int n_row, n_col, nn, maxnrows, maxncols, nfr, ok, maxnrc, n1 ; + + n_row = Work->n_row ; + n_col = Work->n_col ; + nn = MAX (n_row, n_col) ; + nfr = Work->nfr ; + n1 = Symbolic->n1 ; + ASSERT (n1 <= n_row && n1 <= n_col) ; + + maxnrows = Symbolic->maxnrows + Symbolic->nb ; + maxnrows = MIN (n_row, maxnrows) ; + maxncols = Symbolic->maxncols + Symbolic->nb ; + maxncols = MIN (n_col, maxncols) ; + maxnrc = MAX (maxnrows, maxncols) ; + + DEBUG0 (("work alloc: maxnrows+nb "ID" maxncols+nb "ID"\n", + maxnrows, maxncols)) ; + + /* 15 allocations, freed in free_work: */ + /* accounted for in UMF_set_stats (work_usage) */ + Work->Wx = (Entry *) UMF_malloc (maxnrows + 1, sizeof (Entry)) ; + Work->Wy = (Entry *) UMF_malloc (maxnrows + 1, sizeof (Entry)) ; + Work->Frpos = (Int *) UMF_malloc (n_row + 1, sizeof (Int)) ; + Work->Lpattern = (Int *) UMF_malloc (n_row + 1, sizeof (Int)) ; + Work->Fcpos = (Int *) UMF_malloc (n_col + 1, sizeof (Int)) ; + Work->Wp = (Int *) UMF_malloc (nn + 1, sizeof (Int)) ; + Work->Wrp = (Int *) UMF_malloc (MAX (n_col,maxnrows) + 1, sizeof (Int)) ; + Work->Frows = (Int *) UMF_malloc (maxnrows + 1, sizeof (Int)) ; + Work->Wm = (Int *) UMF_malloc (maxnrows + 1, sizeof (Int)) ; + Work->Fcols = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ; + Work->Wio = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ; + Work->Woi = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ; + Work->Woo = (Int *) UMF_malloc (maxnrc + 1, sizeof (Int)); + Work->elen = (n_col - n1) + (n_row - n1) + MIN (n_col-n1, n_row-n1) + 1 ; + Work->E = (Int *) UMF_malloc (Work->elen, sizeof (Int)) ; + Work->Front_new1strow = (Int *) UMF_malloc (nfr + 1, sizeof (Int)) ; + + ok = (Work->Frpos && Work->Fcpos && Work->Lpattern + && Work->Wp && Work->Wrp && Work->Frows && Work->Fcols + && Work->Wio && Work->Woi && Work->Woo && Work->Wm + && Work->E && Work->Front_new1strow && Work->Wx && Work->Wy) ; + + /* 2 allocations: accounted for in UMF_set_stats (work_usage) */ + if (Symbolic->prefer_diagonal) + { + Work->Diagonal_map = (Int *) UMF_malloc (nn, sizeof (Int)) ; + Work->Diagonal_imap = (Int *) UMF_malloc (nn, sizeof (Int)) ; + ok = ok && Work->Diagonal_map && Work->Diagonal_imap ; + } + else + { + /* no diagonal map needed for rectangular matrices */ + Work->Diagonal_map = (Int *) NULL ; + Work->Diagonal_imap = (Int *) NULL ; + } + + /* 1 allocation, may become part of Numeric (if singular or rectangular): */ + Work->Upattern = (Int *) UMF_malloc (n_col + 1, sizeof (Int)) ; + ok = ok && Work->Upattern ; + + /* current frontal matrix does not yet exist */ + Work->Flublock = (Entry *) NULL ; + Work->Flblock = (Entry *) NULL ; + Work->Fublock = (Entry *) NULL ; + Work->Fcblock = (Entry *) NULL ; + + DEBUG0 (("work alloc done.\n")) ; + return (ok) ; +} + + +/* ========================================================================== */ +/* === free_work ============================================================ */ +/* ========================================================================== */ + +PRIVATE void free_work +( + WorkType *Work +) +{ + DEBUG0 (("work free:\n")) ; + if (Work) + { + /* these 16 objects do exist */ + Work->Wx = (Entry *) UMF_free ((void *) Work->Wx) ; + Work->Wy = (Entry *) UMF_free ((void *) Work->Wy) ; + Work->Frpos = (Int *) UMF_free ((void *) Work->Frpos) ; + Work->Fcpos = (Int *) UMF_free ((void *) Work->Fcpos) ; + Work->Lpattern = (Int *) UMF_free ((void *) Work->Lpattern) ; + Work->Upattern = (Int *) UMF_free ((void *) Work->Upattern) ; + Work->Wp = (Int *) UMF_free ((void *) Work->Wp) ; + Work->Wrp = (Int *) UMF_free ((void *) Work->Wrp) ; + Work->Frows = (Int *) UMF_free ((void *) Work->Frows) ; + Work->Fcols = (Int *) UMF_free ((void *) Work->Fcols) ; + Work->Wio = (Int *) UMF_free ((void *) Work->Wio) ; + Work->Woi = (Int *) UMF_free ((void *) Work->Woi) ; + Work->Woo = (Int *) UMF_free ((void *) Work->Woo) ; + Work->Wm = (Int *) UMF_free ((void *) Work->Wm) ; + Work->E = (Int *) UMF_free ((void *) Work->E) ; + Work->Front_new1strow = + (Int *) UMF_free ((void *) Work->Front_new1strow) ; + + /* these objects might not exist */ + Work->Diagonal_map = (Int *) UMF_free ((void *) Work->Diagonal_map) ; + Work->Diagonal_imap = (Int *) UMF_free ((void *) Work->Diagonal_imap) ; + } + DEBUG0 (("work free done.\n")) ; +} + + +/* ========================================================================== */ +/* === error ================================================================ */ +/* ========================================================================== */ + +/* Error return from UMFPACK_numeric. Free all allocated memory. */ + +PRIVATE void error +( + NumericType **Numeric, + WorkType *Work +) +{ + free_work (Work) ; + UMFPACK_free_numeric ((void **) Numeric) ; + ASSERT (UMF_malloc_count == init_count) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_qsymbolic.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_qsymbolic.c new file mode 100644 index 0000000000..910a50c117 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_qsymbolic.c @@ -0,0 +1,2366 @@ +/* ========================================================================== */ +/* === UMFPACK_qsymbolic ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Performs a symbolic factorization. + See umfpack_qsymbolic.h and umfpack_symbolic.h for details. + + Dynamic memory usage: about (3.4nz + 8n + n) integers and n double's as + workspace (via UMF_malloc, for a square matrix). All of it is free'd via + UMF_free if an error occurs. If successful, the Symbolic object contains + 12 to 14 objects allocated by UMF_malloc, with a total size of no more + than about 13*n integers. +*/ + +#include "umf_internal.h" +#include "umf_symbolic_usage.h" +#include "umf_colamd.h" +#include "umf_set_stats.h" +#include "umf_analyze.h" +#include "umf_transpose.h" +#include "umf_is_permutation.h" +#include "umf_malloc.h" +#include "umf_free.h" +#include "umf_2by2.h" +#include "umf_singletons.h" + +typedef struct /* SWType */ +{ + Int *Front_npivcol ; /* size n_col + 1 */ + Int *Front_nrows ; /* size n_col */ + Int *Front_ncols ; /* size n_col */ + Int *Front_parent ; /* size n_col */ + Int *Front_cols ; /* size n_col */ + Int *InFront ; /* size n_row */ + Int *Ci ; /* size Clen */ + Int *Cperm1 ; /* size n_col */ + Int *Rperm1 ; /* size n_row */ + Int *InvRperm1 ; /* size n_row */ + Int *Si ; /* size nz */ + Int *Sp ; /* size n_col + 1 */ + double *Rs ; /* size n_row */ + Int *Rperm_2by2 ; /* size n_row */ + +} SWType ; + +PRIVATE void free_work +( + SWType *SW +) ; + +PRIVATE void error +( + SymbolicType **Symbolic, + SWType *SW +) ; + +/* worst-case usage for SW object */ +#define SYM_WORK_USAGE(n_col,n_row,Clen) \ + (DUNITS (Int, Clen) + \ + DUNITS (Int, nz) + \ + 4 * DUNITS (Int, n_row) + \ + 4 * DUNITS (Int, n_col) + \ + 2 * DUNITS (Int, n_col + 1) + \ + DUNITS (double, n_row)) + +/* required size of Ci for code that calls UMF_transpose and UMF_analyze below*/ +#define UMF_ANALYZE_CLEN(nz,n_row,n_col,nn) \ + ((n_col) + MAX ((nz),(n_col)) + 3*(nn)+1 + (n_col)) + +/* size of an element (in Units), including tuples */ +#define ELEMENT_SIZE(r,c) \ + (DGET_ELEMENT_SIZE (r, c) + 1 + (r + c) * UNITS (Tuple, 1)) + +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif + +/* ========================================================================== */ +/* === do_amd =============================================================== */ +/* ========================================================================== */ + +PRIVATE void do_amd +( + Int n, + const Int Ap [ ], /* size n+1 */ + const Int Ai [ ], /* size nz = Ap [n] */ + Int Q [ ], /* output permutation, j = Q [k] */ + Int Qinv [ ], /* output inverse permutation, Qinv [j] = k */ + Int Sdeg [ ], /* degree of A+A', from AMD_aat */ + Int Clen, /* size of Ci */ + Int Ci [ ], /* size Ci workspace */ + double amd_Control [ ], /* AMD control parameters */ + double amd_Info [ ], /* AMD info */ + SymbolicType *Symbolic, /* Symbolic object */ + double Info [ ] /* UMFPACK info */ +) +{ + + if (n == 0) + { + Symbolic->amd_dmax = 0 ; + Symbolic->amd_lunz = 0 ; + Info [UMFPACK_SYMMETRIC_LUNZ] = 0 ; + Info [UMFPACK_SYMMETRIC_FLOPS] = 0 ; + Info [UMFPACK_SYMMETRIC_DMAX] = 0 ; + Info [UMFPACK_SYMMETRIC_NDENSE] = 0 ; + } + else + { + AMD_1 (n, Ap, Ai, Q, Qinv, Sdeg, Clen, Ci, amd_Control, amd_Info) ; + + /* return estimates computed from AMD on PA+PA' */ + Symbolic->amd_dmax = amd_Info [AMD_DMAX] ; + Symbolic->amd_lunz = 2 * amd_Info [AMD_LNZ] + n ; + Info [UMFPACK_SYMMETRIC_LUNZ] = Symbolic->amd_lunz ; + Info [UMFPACK_SYMMETRIC_FLOPS] = DIV_FLOPS * amd_Info [AMD_NDIV] + + MULTSUB_FLOPS * amd_Info [AMD_NMULTSUBS_LU] ; + Info [UMFPACK_SYMMETRIC_DMAX] = Symbolic->amd_dmax ; + Info [UMFPACK_SYMMETRIC_NDENSE] = amd_Info [AMD_NDENSE] ; + Info [UMFPACK_SYMBOLIC_DEFRAG] += amd_Info [AMD_NCMPA] ; + } +} + +/* ========================================================================== */ +/* === prune_singletons ===================================================== */ +/* ========================================================================== */ + +/* Create the submatrix after removing the n1 singletons. The matrix has + * row and column indices in the range 0 to n_row-n1 and 0 to n_col-n1, + * respectively. */ + +PRIVATE Int prune_singletons +( + Int n1, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + Int Cperm1 [ ], + Int InvRperm1 [ ], + Int Si [ ], + Int Sp [ ] +#ifndef NDEBUG + , Int Rperm1 [ ] + , Int n_row +#endif +) +{ + Int row, k, pp, p, oldcol, newcol, newrow, nzdiag, do_nzdiag ; + + nzdiag = 0 ; + do_nzdiag = (Ax != (double *) NULL) +#ifdef COMPLEX + && (Az != (double *) NULL) +#endif + ; + +#ifndef NDEBUG + DEBUGm4 (("Prune : S = A (Cperm1 (n1+1:end), Rperm1 (n1+1:end))\n")) ; + for (k = 0 ; k < n_row ; k++) + { + ASSERT (Rperm1 [k] >= 0 && Rperm1 [k] < n_row) ; + ASSERT (InvRperm1 [Rperm1 [k]] == k) ; + } +#endif + + /* create the submatrix after removing singletons */ + + pp = 0 ; + for (k = n1 ; k < n_col ; k++) + { + oldcol = Cperm1 [k] ; + newcol = k - n1 ; + DEBUG5 (("Prune singletons k "ID" oldcol "ID" newcol "ID": "ID"\n", + k, oldcol, newcol, pp)) ; + Sp [newcol] = pp ; /* load column pointers */ + for (p = Ap [oldcol] ; p < Ap [oldcol+1] ; p++) + { + row = Ai [p] ; + DEBUG5 ((" "ID": row "ID, pp, row)) ; + ASSERT (row >= 0 && row < n_row) ; + newrow = InvRperm1 [row] - n1 ; + ASSERT (newrow < n_row - n1) ; + if (newrow >= 0) + { + DEBUG5 ((" newrow "ID, newrow)) ; + Si [pp++] = newrow ; + if (do_nzdiag) + { + /* count the number of truly nonzero entries on the + * diagonal of S, excluding entries that are present, + * but numerically zero */ + if (newrow == newcol) + { + /* this is the diagonal entry */ + if (SCALAR_IS_NONZERO (Ax [p]) +#ifdef COMPLEX + || SCALAR_IS_NONZERO (Az [p]) +#endif + ) + { + nzdiag++ ; + } + } + } + } + DEBUG5 (("\n")) ; + } + } + Sp [n_col - n1] = pp ; + + ASSERT (AMD_valid (n_row - n1, n_col - n1, Sp, Si)) ; + return (nzdiag) ; +} + +/* ========================================================================== */ +/* === combine_ordering ===================================================== */ +/* ========================================================================== */ + +PRIVATE void combine_ordering +( + Int n1, + Int nempty_col, + Int n_col, + Int Cperm_init [ ], /* output permutation */ + Int Cperm1 [ ], /* singleton and empty column ordering */ + Int Qinv [ ] /* Qinv from AMD or COLAMD */ +) +{ + Int k, oldcol, newcol, knew ; + + /* combine the singleton ordering with Qinv */ +#ifndef NDEBUG + for (k = 0 ; k < n_col ; k++) + { + Cperm_init [k] = EMPTY ; + } +#endif + for (k = 0 ; k < n1 ; k++) + { + DEBUG1 ((ID" Initial singleton: "ID"\n", k, Cperm1 [k])) ; + Cperm_init [k] = Cperm1 [k] ; + } + for (k = n1 ; k < n_col - nempty_col ; k++) + { + /* this is a non-singleton column */ + oldcol = Cperm1 [k] ; /* user's name for this column */ + newcol = k - n1 ; /* Qinv's name for this column */ + knew = Qinv [newcol] ; /* Qinv's ordering for this column */ + knew += n1 ; /* shift order, after singletons */ + DEBUG1 ((" k "ID" oldcol "ID" newcol "ID" knew "ID"\n", + k, oldcol, newcol, knew)) ; + ASSERT (knew >= 0 && knew < n_col - nempty_col) ; + ASSERT (Cperm_init [knew] == EMPTY) ; + Cperm_init [knew] = oldcol ; + } + for (k = n_col - nempty_col ; k < n_col ; k++) + { + Cperm_init [k] = Cperm1 [k] ; + } +#ifndef NDEBUG + { + Int *W = (Int *) malloc ((n_col + 1) * sizeof (Int)) ; + ASSERT (UMF_is_permutation (Cperm_init, W, n_col, n_col)) ; + free (W) ; + } +#endif + +} + +/* ========================================================================== */ +/* === UMFPACK_qsymbolic ==================================================== */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_qsymbolic +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + const Int Quser [ ], + void **SymbolicHandle, + const double Control [UMFPACK_CONTROL], + double User_Info [UMFPACK_INFO] +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int i, nz, j, newj, status, f1, f2, maxnrows, maxncols, nfr, col, + nchains, maxrows, maxcols, p, nb, nn, *Chain_start, *Chain_maxrows, + *Chain_maxcols, *Front_npivcol, *Ci, Clen, colamd_stats [COLAMD_STATS], + fpiv, n_inner, child, parent, *Link, row, *Front_parent, + analyze_compactions, k, chain, is_sym, *Si, *Sp, n2, do_UMF_analyze, + fpivcol, fallrows, fallcols, *InFront, *F1, snz, *Front_1strow, f1rows, + kk, *Cperm_init, *Rperm_init, newrow, *InvRperm1, *Front_leftmostdesc, + Clen_analyze, strategy, Clen_amd, fixQ, prefer_diagonal, nzdiag, nzaat, + *Wq, *Sdeg, *Fr_npivcol, nempty, *Fr_nrows, *Fr_ncols, *Fr_parent, + *Fr_cols, nempty_row, nempty_col, user_auto_strategy, fail, max_rdeg, + head_usage, tail_usage, lnz, unz, esize, *Esize, rdeg, *Cdeg, *Rdeg, + *Cperm1, *Rperm1, n1, oldcol, newcol, n1c, n1r, *Rperm_2by2, oldrow, + dense_row_threshold, tlen, aggressive ; + double knobs [COLAMD_KNOBS], flops, f, r, c, *Info, force_fixQ, + Info2 [UMFPACK_INFO], drow, dcol, dtail_usage, dlf, duf, dmax_usage, + dhead_usage, dlnz, dunz, dmaxfrsize, dClen, dClen_analyze, sym, + amd_Info [AMD_INFO], dClen_amd, dr, dc, cr, cc, cp, + amd_Control [AMD_CONTROL], stats [2], tol, scale ; + SymbolicType *Symbolic ; + SWType SWspace, *SW ; + +#ifndef NDEBUG + UMF_dump_start ( ) ; + init_count = UMF_malloc_count ; + PRINTF (( +"**** Debugging enabled (UMFPACK will be exceedingly slow!) *****************\n" + )) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get the amount of time used by the process so far */ + /* ---------------------------------------------------------------------- */ + + umfpack_tic (stats) ; + + /* ---------------------------------------------------------------------- */ + /* get control settings and check input parameters */ + /* ---------------------------------------------------------------------- */ + + drow = GET_CONTROL (UMFPACK_DENSE_ROW, UMFPACK_DEFAULT_DENSE_ROW) ; + dcol = GET_CONTROL (UMFPACK_DENSE_COL, UMFPACK_DEFAULT_DENSE_COL) ; + nb = GET_CONTROL (UMFPACK_BLOCK_SIZE, UMFPACK_DEFAULT_BLOCK_SIZE) ; + strategy = GET_CONTROL (UMFPACK_STRATEGY, UMFPACK_DEFAULT_STRATEGY) ; + tol = GET_CONTROL (UMFPACK_2BY2_TOLERANCE, UMFPACK_DEFAULT_2BY2_TOLERANCE) ; + scale = GET_CONTROL (UMFPACK_SCALE, UMFPACK_DEFAULT_SCALE) ; + force_fixQ = GET_CONTROL (UMFPACK_FIXQ, UMFPACK_DEFAULT_FIXQ) ; + AMD_defaults (amd_Control) ; + amd_Control [AMD_DENSE] = + GET_CONTROL (UMFPACK_AMD_DENSE, UMFPACK_DEFAULT_AMD_DENSE) ; + aggressive = + (GET_CONTROL (UMFPACK_AGGRESSIVE, UMFPACK_DEFAULT_AGGRESSIVE) != 0) ; + amd_Control [AMD_AGGRESSIVE] = aggressive ; + + nb = MAX (2, nb) ; + nb = MIN (nb, MAXNB) ; + ASSERT (nb >= 0) ; + if (nb % 2 == 1) nb++ ; /* make sure nb is even */ + DEBUG0 (("UMFPACK_qsymbolic: nb = "ID" aggressive = "ID"\n", nb, + aggressive)) ; + + tol = MAX (0.0, MIN (tol, 1.0)) ; + if (scale != UMFPACK_SCALE_NONE && scale != UMFPACK_SCALE_MAX) + { + scale = UMFPACK_DEFAULT_SCALE ; + } + + if (User_Info != (double *) NULL) + { + /* return Info in user's array */ + Info = User_Info ; + } + else + { + /* no Info array passed - use local one instead */ + Info = Info2 ; + } + /* clear all of Info */ + for (i = 0 ; i < UMFPACK_INFO ; i++) + { + Info [i] = EMPTY ; + } + + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + + Info [UMFPACK_STATUS] = UMFPACK_OK ; + Info [UMFPACK_NROW] = n_row ; + Info [UMFPACK_NCOL] = n_col ; + Info [UMFPACK_SIZE_OF_UNIT] = (double) (sizeof (Unit)) ; + Info [UMFPACK_SIZE_OF_INT] = (double) (sizeof (int)) ; + Info [UMFPACK_SIZE_OF_LONG] = (double) (sizeof (long)) ; + Info [UMFPACK_SIZE_OF_POINTER] = (double) (sizeof (void *)) ; + Info [UMFPACK_SIZE_OF_ENTRY] = (double) (sizeof (Entry)) ; + Info [UMFPACK_SYMBOLIC_DEFRAG] = 0 ; + + if (!Ai || !Ap || !SymbolicHandle) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + + *SymbolicHandle = (void *) NULL ; + + if (n_row <= 0 || n_col <= 0) /* n_row, n_col must be > 0 */ + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_n_nonpositive ; + return (UMFPACK_ERROR_n_nonpositive) ; + } + + nz = Ap [n_col] ; + DEBUG0 (("n_row "ID" n_col "ID" nz "ID"\n", n_row, n_col, nz)) ; + Info [UMFPACK_NZ] = nz ; + if (nz < 0) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_matrix ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + + /* ---------------------------------------------------------------------- */ + /* get the requested strategy */ + /* ---------------------------------------------------------------------- */ + + if (n_row != n_col) + { + /* if the matrix is rectangular, the only available strategy is + * unsymmetric */ + strategy = UMFPACK_STRATEGY_UNSYMMETRIC ; + DEBUGm3 (("Rectangular: forcing unsymmetric strategy\n")) ; + } + + if (strategy < UMFPACK_STRATEGY_AUTO + || strategy > UMFPACK_STRATEGY_SYMMETRIC) + { + /* unrecognized strategy */ + strategy = UMFPACK_STRATEGY_AUTO ; + } + + user_auto_strategy = (strategy == UMFPACK_STRATEGY_AUTO) ; + + /* ---------------------------------------------------------------------- */ + /* determine amount of memory required for UMFPACK_symbolic */ + /* ---------------------------------------------------------------------- */ + + /* The size of Clen required for UMF_colamd is always larger than */ + /* UMF_analyze, but the max is included here in case that changes in */ + /* future versions. */ + + /* This is about 2.2*nz + 9*n_col + 6*n_row, or nz/5 + 13*n_col + 6*n_row, + * whichever is bigger. For square matrices, it works out to + * 2.2nz + 15n, or nz/5 + 19n, whichever is bigger (typically 2.2nz+15n). */ + dClen = UMF_COLAMD_RECOMMENDED ((double) nz, (double) n_row, + (double) n_col) ; + + /* This is defined above, as max (nz,n_col) + 3*nn+1 + 2*n_col, where + * nn = max (n_row,n_col). It is always smaller than the space required + * for colamd or amd. */ + dClen_analyze = UMF_ANALYZE_CLEN ((double) nz, (double) n_row, + (double) n_col, (double) nn) ; + dClen = MAX (dClen, dClen_analyze) ; + + /* The space for AMD can be larger than what's required for colamd: */ + dClen_amd = 2.4 * (double) nz + 8 * (double) n_inner ; + /* additional space for the 2-by-2 strategy */ + dClen_amd += (double) MAX (nn, nz) ; + dClen = MAX (dClen, dClen_amd) ; + + /* worst case total memory usage for UMFPACK_symbolic (revised below) */ + Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] = + SYM_WORK_USAGE (n_col, n_row, dClen) + + UMF_symbolic_usage (n_row, n_col, n_col, n_col, n_col, TRUE) ; + + if (INT_OVERFLOW (dClen * sizeof (Int))) + { + /* :: int overflow, Clen too large :: */ + /* Problem is too large for array indexing (Ci [i]) with an Int i. */ + /* Cannot even analyze the problem to determine upper bounds on */ + /* memory usage. Need to use the long integer version, umfpack_*l_*. */ + DEBUGm4 (("out of memory: symbolic int overflow\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + /* repeat the size calculations, in integers */ + Clen = UMF_COLAMD_RECOMMENDED (nz, n_row, n_col) ; + Clen_analyze = UMF_ANALYZE_CLEN (nz, n_row, n_col, nn) ; + Clen = MAX (Clen, Clen_analyze) ; + Clen_amd = 2.4 * nz + 8 * n_inner ; + Clen_amd += MAX (nn, nz) ; /* for Ri, in UMF_2by2 */ + Clen = MAX (Clen, Clen_amd) ; + + /* ---------------------------------------------------------------------- */ + /* allocate the first part of the Symbolic object (header and Cperm_init) */ + /* ---------------------------------------------------------------------- */ + + /* (1) Five calls to UMF_malloc are made, for a total space of + * 2 * (n_row + n_col) + 4 integers + sizeof (SymbolicType). + * sizeof (SymbolicType) is a small constant. This space is part of the + * Symbolic object and is not freed unless an error occurs. If A is square + * then this is about 4*n integers. + */ + + Symbolic = (SymbolicType *) UMF_malloc (1, sizeof (SymbolicType)) ; + + if (!Symbolic) + { + /* If we fail here, Symbolic is NULL and thus it won't be */ + /* dereferenced by UMFPACK_free_symbolic, as called by error ( ). */ + DEBUGm4 (("out of memory: symbolic object\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, (SWType *) NULL) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + /* We now know that Symbolic has been allocated */ + Symbolic->valid = 0 ; + Symbolic->Chain_start = (Int *) NULL ; + Symbolic->Chain_maxrows = (Int *) NULL ; + Symbolic->Chain_maxcols = (Int *) NULL ; + Symbolic->Front_npivcol = (Int *) NULL ; + Symbolic->Front_parent = (Int *) NULL ; + Symbolic->Front_1strow = (Int *) NULL ; + Symbolic->Front_leftmostdesc = (Int *) NULL ; + Symbolic->Esize = (Int *) NULL ; + Symbolic->esize = 0 ; + + Symbolic->Cperm_init = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Symbolic->Rperm_init = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + Symbolic->Cdeg = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Symbolic->Rdeg = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + Symbolic->Diagonal_map = (Int *) NULL ; + + Cperm_init = Symbolic->Cperm_init ; + Rperm_init = Symbolic->Rperm_init ; + Cdeg = Symbolic->Cdeg ; + Rdeg = Symbolic->Rdeg ; + + if (!Cperm_init || !Rperm_init || !Cdeg || !Rdeg) + { + DEBUGm4 (("out of memory: symbolic perm\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, (SWType *) NULL) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + Symbolic->n_row = n_row ; + Symbolic->n_col = n_col ; + Symbolic->nz = nz ; + Symbolic->nb = nb ; + + /* ---------------------------------------------------------------------- */ + /* check user's input permutation */ + /* ---------------------------------------------------------------------- */ + + if (Quser != (Int *) NULL) + { + /* use Cperm_init as workspace to check input permutation */ + if (!UMF_is_permutation (Quser, Cperm_init, n_col, n_col)) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_permutation ; + error (&Symbolic, (SWType *) NULL) ; + return (UMFPACK_ERROR_invalid_permutation) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* allocate workspace */ + /* ---------------------------------------------------------------------- */ + + /* (2) Eleven calls to UMF_malloc are made, for workspace of size + * Clen + nz + 7*n_col + 2*n_row + 2 integers. Clen is the larger of + * MAX (2*nz, 4*n_col) + 8*n_col + 6*n_row + n_col + nz/5 and + * 2.4*nz + 8 * MIN (n_row, n_col) + MAX (n_row, n_col, nz) + * If A is square and non-singular, then Clen is + * MAX (MAX (2*nz, 4*n) + 7*n + nz/5, 3.4*nz) + 8*n + * If A has at least 4*n nonzeros then Clen is + * MAX (2.2*nz + 7*n, 3.4*nz) + 8*n + * If A has at least (7/1.2)*n nonzeros, (about 5.8*n), then Clen is + * 3.4*nz + 8*n + * This space will be free'd when this routine finishes. + * + * Total space thus far is about 3.4nz + 12n integers. + * For the double precision, 32-bit integer version, the user's matrix + * requires an equivalent space of 3*nz + n integers. So this space is just + * slightly larger than the user's input matrix (including the numerical + * values themselves). + */ + + SW = &SWspace ; /* used for UMFPACK_symbolic only */ + + /* Note that SW->Front_* does not include the dummy placeholder front. */ + /* This space is accounted for by the SYM_WORK_USAGE macro. */ + + /* this is free'd early */ + SW->Si = (Int *) UMF_malloc (nz, sizeof (Int)) ; + SW->Sp = (Int *) UMF_malloc (n_col + 1, sizeof (Int)) ; + SW->InvRperm1 = (Int *) UMF_malloc (n_row, sizeof (Int)) ; + SW->Cperm1 = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + + /* this is free'd late */ + SW->Ci = (Int *) UMF_malloc (Clen, sizeof (Int)) ; + SW->Front_npivcol = (Int *) UMF_malloc (n_col + 1, sizeof (Int)) ; + SW->Front_nrows = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + SW->Front_ncols = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + SW->Front_parent = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + SW->Front_cols = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + SW->Rperm1 = (Int *) UMF_malloc (n_row, sizeof (Int)) ; + SW->InFront = (Int *) UMF_malloc (n_row, sizeof (Int)) ; + + /* this is allocated later, and free'd after Cperm1 but before Ci */ + SW->Rperm_2by2 = (Int *) NULL ; /* will be nn Int's */ + + /* this is allocated last, and free'd first */ + SW->Rs = (double *) NULL ; /* will be n_row double's */ + + Ci = SW->Ci ; + Fr_npivcol = SW->Front_npivcol ; + Fr_nrows = SW->Front_nrows ; + Fr_ncols = SW->Front_ncols ; + Fr_parent = SW->Front_parent ; + Fr_cols = SW->Front_cols ; + Cperm1 = SW->Cperm1 ; + Rperm1 = SW->Rperm1 ; + Si = SW->Si ; + Sp = SW->Sp ; + InvRperm1 = SW->InvRperm1 ; + Rperm_2by2 = (Int *) NULL ; + InFront = SW->InFront ; + + if (!Ci || !Fr_npivcol || !Fr_nrows || !Fr_ncols || !Fr_parent || !Fr_cols + || !Cperm1 || !Rperm1 || !Si || !Sp || !InvRperm1 || !InFront) + { + DEBUGm4 (("out of memory: symbolic work\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, SW) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + DEBUG0 (("Symbolic UMF_malloc_count - init_count = "ID"\n", + UMF_malloc_count - init_count)) ; + ASSERT (UMF_malloc_count == init_count + 17) ; + + /* ---------------------------------------------------------------------- */ + /* find the row and column singletons */ + /* ---------------------------------------------------------------------- */ + + /* [ use first nz + n_row + MAX (n_row, n_col) entries in Ci as workspace, + * and use Rperm_init as workspace */ + ASSERT (Clen >= nz + n_row + MAX (n_row, n_col)) ; + + status = UMF_singletons (n_row, n_col, Ap, Ai, Quser, Cdeg, Cperm1, Rdeg, + Rperm1, InvRperm1, &n1, &n1c, &n1r, &nempty_col, &nempty_row, &is_sym, + &max_rdeg, /* workspace: */ Rperm_init, Ci, Ci + nz, Ci + nz + n_row) ; + + /* ] done using Rperm_init and Ci as workspace */ + + /* InvRperm1 is now the inverse of Rperm1 */ + + if (status != UMFPACK_OK) + { + DEBUGm4 (("matrix invalid: UMF_singletons\n")) ; + Info [UMFPACK_STATUS] = status ; + error (&Symbolic, SW) ; + return (status) ; + } + Info [UMFPACK_NEMPTY_COL] = nempty_col ; + Info [UMFPACK_NEMPTY_ROW] = nempty_row ; + Info [UMFPACK_NDENSE_COL] = 0 ; /* # dense rows/cols recomputed below */ + Info [UMFPACK_NDENSE_ROW] = 0 ; + Info [UMFPACK_COL_SINGLETONS] = n1c ; + Info [UMFPACK_ROW_SINGLETONS] = n1r ; + Info [UMFPACK_S_SYMMETRIC] = is_sym ; + + nempty = MIN (nempty_col, nempty_row) ; + Symbolic->nempty_row = nempty_row ; + Symbolic->nempty_col = nempty_col ; + + /* UMF_singletons has verified that the user's input matrix is valid */ + ASSERT (AMD_valid (n_row, n_col, Ap, Ai)) ; + + Symbolic->n1 = n1 ; + Symbolic->nempty = nempty ; + ASSERT (n1 <= n_inner) ; + n2 = nn - n1 - nempty ; + + dense_row_threshold = + UMFPACK_DENSE_DEGREE_THRESHOLD (drow, n_col - n1 - nempty_col) ; + Symbolic->dense_row_threshold = dense_row_threshold ; + + if (!is_sym) + { + /* either the pruned submatrix rectangular, or it is square and + * Rperm [n1 .. n-nempty-1] is not the same as Cperm [n1 .. n-nempty-1]. + * For the auto strategy, switch to the unsymmetric strategy. + * Otherwise, if the strategy selected by the user is symmetric or + * 2-by-2, then the singletons will be discarded. */ + strategy = UMFPACK_STRATEGY_UNSYMMETRIC ; + DEBUGm4 (("Strategy: Unsymmetric singletons\n")) ; + } + + /* ---------------------------------------------------------------------- */ + /* determine symmetry, nzdiag, and degrees of S+S' */ + /* ---------------------------------------------------------------------- */ + + /* S is the matrix obtained after removing singletons + * = A (Cperm1 [n1..n_col-nempty_col-1], Rperm1 [n1..n_row-nempty_row-1]) + */ + + Wq = Rperm_init ; /* use Rperm_init as workspace for Wq [ */ + Sdeg = Cperm_init ; /* use Cperm_init as workspace for Sdeg [ */ + sym = EMPTY ; + nzaat = EMPTY ; + nzdiag = EMPTY ; + for (i = 0 ; i < AMD_INFO ; i++) + { + amd_Info [i] = EMPTY ; + } + + if (strategy != UMFPACK_STRATEGY_UNSYMMETRIC) + { + /* This also determines the degree of each node in S+S' (Sdeg), which + * is needed by the 2-by-2 strategy, the symmetry of S, and the number + * of nonzeros on the diagonal of S. */ + ASSERT (n_row == n_col) ; + ASSERT (nempty_row == nempty_col) ; + + /* get the count of nonzeros on the diagonal of S, excluding explicitly + * zero entries. nzdiag = amd_Info [AMD_NZDIAG] counts the zero entries + * in S. */ + + nzdiag = prune_singletons (n1, nn, Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + Cperm1, InvRperm1, Si, Sp +#ifndef NDEBUG + , Rperm1, nn +#endif + ) ; + nzaat = AMD_aat (n2, Sp, Si, Sdeg, Wq, amd_Info) ; + sym = amd_Info [AMD_SYMMETRY] ; + Info [UMFPACK_N2] = n2 ; + /* nzdiag = amd_Info [AMD_NZDIAG] counts the zero entries of S too */ + +#ifndef NDEBUG + for (k = 0 ; k < n2 ; k++) + { + ASSERT (Sdeg [k] >= 0 && Sdeg [k] < n2) ; + } + ASSERT (Sp [n2] - n2 <= nzaat && nzaat <= 2 * Sp [n2]) ; + DEBUG0 (("Explicit zeros: "ID" %g\n", nzdiag, amd_Info [AMD_NZDIAG])) ; +#endif + } + + /* get statistics from amd_aat, if computed */ + Symbolic->sym = sym ; + Symbolic->nzaat = nzaat ; + Symbolic->nzdiag = nzdiag ; + Symbolic->amd_dmax = EMPTY ; + + Info [UMFPACK_PATTERN_SYMMETRY] = sym ; + Info [UMFPACK_NZ_A_PLUS_AT] = nzaat ; + Info [UMFPACK_NZDIAG] = nzdiag ; + + /* ---------------------------------------------------------------------- */ + /* determine the initial strategy based on symmetry and nnz (diag (S)) */ + /* ---------------------------------------------------------------------- */ + + if (strategy == UMFPACK_STRATEGY_AUTO) + { + if (sym < 0.10) + { + /* highly unsymmetric: use the unsymmetric strategy */ + strategy = UMFPACK_STRATEGY_UNSYMMETRIC ; + DEBUGm4 (("Strategy: select unsymmetric\n")) ; + } + else if (sym >= 0.7 && nzdiag == n2) + { + /* mostly symmetric, zero-free diagonal: use symmetric strategy */ + strategy = UMFPACK_STRATEGY_SYMMETRIC ; + DEBUGm4 (("Strategy: select symmetric\n")) ; + } + else + { + /* Evaluate the symmetric 2-by-2 strategy, and select it, or + * the unsymmetric strategy if the 2-by-2 strategy doesn't look + * promising. */ + strategy = UMFPACK_STRATEGY_2BY2 ; + DEBUGm4 (("Strategy: try 2-by-2\n")) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* try the 2-by-2 strategy */ + /* ---------------------------------------------------------------------- */ + + /* (3) If the 2-by-2 strategy is attempted, additional workspace of size + * nn integers and nn double's is allocated, where nn = n_row = n_col. + * The real workspace is immediately free'd. The integer workspace of + * size nn remains until the end of umfpack_qsymbolic. */ + + /* If the resulting matrix S (Rperm_2by2, :) is too unsymmetric, then the + * unsymmetric strategy will be used instead. */ + + if (strategy == UMFPACK_STRATEGY_2BY2) + { + Int *Rp, *Ri, *Blen, *W, nz_papat, nzd2, nweak, unmatched, + Clen3 ; + double sym2 ; + + /* ------------------------------------------------------------------ */ + /* get workspace for UMF_2by2 */ + /* ------------------------------------------------------------------ */ + + ASSERT (n_row == n_col && nn == n_row) ; + +#ifndef NDEBUG + for (k = 0 ; k < n2 ; k++) + { + ASSERT (Sdeg [k] >= 0 && Sdeg [k] < n2) ; + } +#endif + + /* allocate Rperm_2by2 */ + SW->Rperm_2by2 = (Int *) UMF_malloc (nn, sizeof (Int)) ; + Rperm_2by2 = SW->Rperm_2by2 ; + if (Rperm_2by2 == (Int *) NULL) + { + DEBUGm4 (("out of memory: Rperm_2by2\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, SW) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + /* allocate Ri from the tail end of Ci [ */ + Clen3 = Clen - (MAX (nn, nz) + 1) ; + Ri = Ci + Clen3 ; + ASSERT (Clen3 >= nz) ; /* space required for UMF_2by2 */ + + /* use Fr_* as workspace for Rp, Blen, and W [ */ + Rp = Fr_npivcol ; + Blen = Fr_ncols ; + W = Fr_cols ; + + if (scale != UMFPACK_SCALE_NONE) + { + SW->Rs = (double *) UMF_malloc (nn, sizeof (double)) ; + if (SW->Rs == (double *) NULL) + { + DEBUGm4 (("out of memory: scale factors for 2-by-2\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, SW) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + } + + /* ------------------------------------------------------------------ */ + /* find the 2-by-2 row permutation */ + /* ------------------------------------------------------------------ */ + + /* find a row permutation Rperm_2by2 such that S (Rperm_2by2, :) + * has a healthy diagonal */ + + UMF_2by2 (nn, Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + tol, scale, Cperm1, +#ifndef NDEBUG + Rperm1, +#endif + InvRperm1, n1, nempty, Sdeg, Rperm_2by2, &nweak, &unmatched, + Ri, Rp, SW->Rs, Blen, W, Ci, Wq) ; + DEBUGm3 (("2by2: nweak "ID" unmatched "ID"\n", nweak, unmatched)) ; + Info [UMFPACK_2BY2_NWEAK] = nweak ; + Info [UMFPACK_2BY2_UNMATCHED] = unmatched ; + + SW->Rs = (double *) UMF_free ((void *) SW->Rs) ; + + /* R = S (Rperm_2by2,:)' */ + (void) UMF_transpose (n2, n2, Sp, Si, (double *) NULL, Rperm_2by2, + (Int *) NULL, 0, Rp, Ri, (double *) NULL, W, FALSE +#ifdef COMPLEX + , (double *) NULL, (double *) NULL, FALSE +#endif + ) ; + ASSERT (AMD_valid (n2, n2, Rp, Ri)) ; + + /* contents of Si and Sp no longer needed, but the space is + * still needed */ + + /* ------------------------------------------------------------------ */ + /* find symmetry of S (Rperm_2by2, :)', and prepare to order with AMD */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < AMD_INFO ; i++) + { + amd_Info [i] = EMPTY ; + } + nz_papat = AMD_aat (n2, Rp, Ri, Sdeg, Wq, amd_Info) ; + sym2 = amd_Info [AMD_SYMMETRY] ; + nzd2 = amd_Info [AMD_NZDIAG] ; + + Info [UMFPACK_2BY2_PATTERN_SYMMETRY] = sym2 ; + Info [UMFPACK_2BY2_NZ_PA_PLUS_PAT] = nz_papat ; + Info [UMFPACK_2BY2_NZDIAG] = nzd2 ; + + DEBUG0 (("2by2: sym2 %g nzd2 "ID" n2 "ID"\n", sym2, nzd2, n2)) ; + + /* ------------------------------------------------------------------ */ + /* evaluate the 2-by-2 results */ + /* ------------------------------------------------------------------ */ + + if (user_auto_strategy) + { + if ((sym2 > 1.1 * sym) && (nzd2 > 0.9 * n2)) + { + /* 2-by-2 made it much more symmetric */ + DEBUGm4 (("eval Strategy 2by2: much more symmetric: 2by2\n")) ; + strategy = UMFPACK_STRATEGY_2BY2 ; + } + else if (sym2 < 0.7 * sym) + { + /* 2-by-2 made it much more unsymmetric */ + DEBUGm4 (("eval Strategy 2by2: much more UNsymmetric:unsym\n")); + strategy = UMFPACK_STRATEGY_UNSYMMETRIC ; + } + else if (sym2 < 0.25) + { + DEBUGm4 (("eval Strategy 2by2: is UNsymmetric: unsym\n")); + strategy = UMFPACK_STRATEGY_UNSYMMETRIC ; + } + else if (sym2 >= 0.51) + { + DEBUGm4 (("eval Strategy 2by2: sym2 >= 0.51: 2by2\n")) ; + strategy = UMFPACK_STRATEGY_2BY2 ; + } + else if (sym2 >= 0.999 * sym) + { + /* 2-by-2 improved symmetry, or made it only slightly worse */ + DEBUGm4 (("eval Strategy 2by2: sym2 >= 0.999 sym: 2by2\n")) ; + strategy = UMFPACK_STRATEGY_2BY2 ; + } + else + { + /* can't decide what to do, so pick the unsymmetric strategy */ + DEBUGm4 (("eval Strategy 2by2: punt: unsym\n")); + strategy = UMFPACK_STRATEGY_UNSYMMETRIC ; + } + } + + /* ------------------------------------------------------------------ */ + /* if the 2-by-2 strategy is selected: */ + /* ------------------------------------------------------------------ */ + + if (strategy == UMFPACK_STRATEGY_2BY2) + { + if (Quser == (Int *) NULL) + { + /* 2-by-2 strategy is successful */ + /* compute amd (S) */ + Int *Qinv = Fr_npivcol ; + ASSERT (Clen3 >= (nz_papat + nz_papat/5 + nn) + 7*nn) ; + do_amd (n2, Rp, Ri, Wq, Qinv, Sdeg, Clen3, Ci, + amd_Control, amd_Info, Symbolic, Info) ; + /* combine the singleton ordering and the AMD ordering */ + combine_ordering (n1, nempty, nn, Cperm_init, Cperm1, Qinv) ; + } + /* fix Rperm_2by2 to reflect A, not S */ + for (k = 0 ; k < n1 ; k++) + { + oldcol = Cperm1 [k] ; + i = k ; + oldrow = Rperm1 [k] ; + W [oldcol] = oldrow ; + } + for (k = n1 ; k < nn - nempty ; k++) + { + oldcol = Cperm1 [k] ; + i = Rperm_2by2 [k - n1] + n1 ; + oldrow = Rperm1 [i] ; + W [oldcol] = oldrow ; + } + for (k = nn - nempty ; k < nn ; k++) + { + oldcol = Cperm1 [k] ; + i = k ; + oldrow = Rperm1 [k] ; + W [oldcol] = oldrow ; + } + for (k = 0 ; k < nn ; k++) + { + Rperm_2by2 [k] = W [k] ; + } + + /* Now, the "diagonal" entry in oldcol (where oldcol is the user's + * name for a column, is the entry in row oldrow (where oldrow is + * the user's name for a row, and oldrow = Rperm_2by2 [oldcol] */ + } + + /* Fr_* no longer needed for Rp, Blen, W ] */ + } + + /* ---------------------------------------------------------------------- */ + /* finalize the strategy, including fixQ and prefer_diagonal */ + /* ---------------------------------------------------------------------- */ + + if (strategy == UMFPACK_STRATEGY_SYMMETRIC) + { + /* use given Quser or AMD (A+A'), fix Q during factorization, + * prefer diagonal */ + DEBUG0 (("\nStrategy: symmetric\n")) ; + ASSERT (n_row == n_col) ; + Symbolic->ordering = UMFPACK_ORDERING_AMD ; + fixQ = TRUE ; + prefer_diagonal = TRUE ; + } + else if (strategy == UMFPACK_STRATEGY_2BY2) + { + /* use Q = given Quser or Q = AMD (PA+PA'), fix Q during factorization, + * prefer diagonal, and factorize PAQ, where P is found by UMF_2by2. */ + DEBUG0 (("\nStrategy: symmetric 2-by-2\n")) ; + ASSERT (n_row == n_col) ; + Symbolic->ordering = UMFPACK_ORDERING_AMD ; + fixQ = TRUE ; + prefer_diagonal = TRUE ; + } + else + { + /* use given Quser or COLAMD (A), refine Q during factorization, + * no diagonal preference */ + ASSERT (strategy == UMFPACK_STRATEGY_UNSYMMETRIC) ; + DEBUG0 (("\nStrategy: unsymmetric\n")) ; + Symbolic->ordering = UMFPACK_ORDERING_COLAMD ; + fixQ = FALSE ; + prefer_diagonal = FALSE ; + } + + if (Quser != (Int *) NULL) + { + Symbolic->ordering = UMFPACK_ORDERING_GIVEN ; + } + + if (force_fixQ > 0) + { + fixQ = TRUE ; + DEBUG0 (("Force fixQ true\n")) ; + } + else if (force_fixQ < 0) + { + fixQ = FALSE ; + DEBUG0 (("Force fixQ false\n")) ; + } + + DEBUG0 (("Strategy: ordering: "ID"\n", Symbolic->ordering)) ; + DEBUG0 (("Strategy: fixQ: "ID"\n", fixQ)) ; + DEBUG0 (("Strategy: prefer diag "ID"\n", prefer_diagonal)) ; + + /* get statistics from amd_aat, if computed */ + Symbolic->strategy = strategy ; + Symbolic->fixQ = fixQ ; + Symbolic->prefer_diagonal = prefer_diagonal ; + + Info [UMFPACK_STRATEGY_USED] = strategy ; + Info [UMFPACK_ORDERING_USED] = Symbolic->ordering ; + Info [UMFPACK_QFIXED] = fixQ ; + Info [UMFPACK_DIAG_PREFERRED] = prefer_diagonal ; + + /* ---------------------------------------------------------------------- */ + /* get the AMD ordering for the symmetric strategy */ + /* ---------------------------------------------------------------------- */ + + if (strategy == UMFPACK_STRATEGY_SYMMETRIC && Quser == (Int *) NULL) + { + /* symmetric strategy for a matrix with mostly symmetric pattern */ + Int *Qinv = Fr_npivcol ; + ASSERT (n_row == n_col && nn == n_row) ; + ASSERT (Clen >= (nzaat + nzaat/5 + nn) + 7*nn) ; + do_amd (n2, Sp, Si, Wq, Qinv, Sdeg, Clen, Ci, + amd_Control, amd_Info, Symbolic, Info) ; + /* combine the singleton ordering and the AMD ordering */ + combine_ordering (n1, nempty, nn, Cperm_init, Cperm1, Qinv) ; + } + /* Sdeg no longer needed ] */ + /* done using Rperm_init as workspace for Wq ] */ + + /* Contents of Si and Sp no longer needed, but the space is still needed */ + + /* ---------------------------------------------------------------------- */ + /* use the user's input column ordering (already in Cperm1) */ + /* ---------------------------------------------------------------------- */ + + if (Quser != (Int *) NULL) + { + for (k = 0 ; k < n_col ; k++) + { + Cperm_init [k] = Cperm1 [k] ; + } + } + + /* ---------------------------------------------------------------------- */ + /* use COLAMD to order the matrix */ + /* ---------------------------------------------------------------------- */ + + if (strategy == UMFPACK_STRATEGY_UNSYMMETRIC && Quser == (Int *) NULL) + { + + /* ------------------------------------------------------------------ */ + /* copy the matrix into colamd workspace (colamd destroys its input) */ + /* ------------------------------------------------------------------ */ + + /* C = A (Cperm1 (n1+1:end), Rperm1 (n1+1:end)), where Ci is used as + * the row indices and Cperm_init (on input) is used as the column + * pointers. */ + + (void) prune_singletons (n1, n_col, Ap, Ai, + (double *) NULL, +#ifdef COMPLEX + (double *) NULL, +#endif + Cperm1, InvRperm1, Ci, Cperm_init +#ifndef NDEBUG + , Rperm1, n_row +#endif + ) ; + + /* ------------------------------------------------------------------ */ + /* set UMF_colamd defaults */ + /* ------------------------------------------------------------------ */ + + UMF_colamd_set_defaults (knobs) ; + knobs [COLAMD_DENSE_ROW] = drow ; + knobs [COLAMD_DENSE_COL] = dcol ; + knobs [COLAMD_AGGRESSIVE] = aggressive ; + + /* ------------------------------------------------------------------ */ + /* check input matrix and find the initial column pre-ordering */ + /* ------------------------------------------------------------------ */ + + /* NOTE: umf_colamd is not given any original empty rows or columns. + * Those have already been removed via prune_singletons, above. The + * umf_colamd routine has been modified to assume that all rows and + * columns have at least one entry in them. It will break if it is + * given empty rows or columns (an assertion is triggered when running + * in debug mode. */ + + (void) UMF_colamd ( + n_row - n1 - nempty_row, + n_col - n1 - nempty_col, + Clen, Ci, Cperm_init, knobs, colamd_stats, + Fr_npivcol, Fr_nrows, Fr_ncols, Fr_parent, Fr_cols, &nfr, + InFront) ; + ASSERT (colamd_stats [COLAMD_EMPTY_ROW] == 0) ; + ASSERT (colamd_stats [COLAMD_EMPTY_COL] == 0) ; + + /* # of dense rows will be recomputed below */ + Info [UMFPACK_NDENSE_ROW] = colamd_stats [COLAMD_DENSE_ROW] ; + Info [UMFPACK_NDENSE_COL] = colamd_stats [COLAMD_DENSE_COL] ; + Info [UMFPACK_SYMBOLIC_DEFRAG] = colamd_stats [COLAMD_DEFRAG_COUNT] ; + + /* re-analyze if any "dense" rows or cols ignored by UMF_colamd */ + do_UMF_analyze = + colamd_stats [COLAMD_DENSE_ROW] > 0 || + colamd_stats [COLAMD_DENSE_COL] > 0 ; + + /* Combine the singleton and colamd ordering into Cperm_init */ + /* Note that colamd returns its inverse permutation in Ci */ + combine_ordering (n1, nempty_col, n_col, Cperm_init, Cperm1, Ci) ; + + /* contents of Ci no longer needed */ + +#ifndef NDEBUG + for (col = 0 ; col < n_col ; col++) + { + DEBUG1 (("Cperm_init ["ID"] = "ID"\n", col, Cperm_init[col])); + } + /* make sure colamd returned a valid permutation */ + ASSERT (Cperm_init != (Int *) NULL) ; + ASSERT (UMF_is_permutation (Cperm_init, Ci, n_col, n_col)) ; +#endif + + } + else + { + + /* ------------------------------------------------------------------ */ + /* do not call colamd - use input Quser or AMD instead */ + /* ------------------------------------------------------------------ */ + + /* The ordering (Quser or Qamd) is already in Cperm_init */ + do_UMF_analyze = TRUE ; + + } + + Cperm_init [n_col] = EMPTY ; /* unused in Cperm_init */ + + /* ---------------------------------------------------------------------- */ + /* AMD ordering, if it exists, has been copied into Cperm_init */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG3 (("Cperm_init column permutation:\n")) ; + ASSERT (UMF_is_permutation (Cperm_init, Ci, n_col, n_col)) ; + for (k = 0 ; k < n_col ; k++) + { + DEBUG3 ((ID"\n", Cperm_init [k])) ; + } + /* ensure that empty columns have been placed last in A (:,Cperm_init) */ + for (newj = 0 ; newj < n_col ; newj++) + { + /* empty columns will be last in A (:, Cperm_init (1:n_col)) */ + j = Cperm_init [newj] ; + ASSERT (IMPLIES (newj >= n_col-nempty_col, Cdeg [j] == 0)) ; + ASSERT (IMPLIES (newj < n_col-nempty_col, Cdeg [j] > 0)) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* symbolic factorization (unless colamd has already done it) */ + /* ---------------------------------------------------------------------- */ + + if (do_UMF_analyze) + { + + Int *W, *Bp, *Bi, *Cperm2, ok, *P, Clen2, bsize, Clen0 ; + + /* ------------------------------------------------------------------ */ + /* construct column pre-ordered, pruned submatrix */ + /* ------------------------------------------------------------------ */ + + /* S = column form submatrix after removing singletons and applying + * initial column ordering (includes singleton ordering) */ + (void) prune_singletons (n1, n_col, Ap, Ai, + (double *) NULL, +#ifdef COMPLEX + (double *) NULL, +#endif + Cperm_init, InvRperm1, Si, Sp +#ifndef NDEBUG + , Rperm1, n_row +#endif + ) ; + + /* ------------------------------------------------------------------ */ + /* Ci [0 .. Clen-1] holds the following work arrays: + + first Clen0 entries empty space, where Clen0 = + Clen - (nn+1 + 2*nn + n_col) + and Clen0 >= nz + n_col + next nn+1 entries Bp [0..nn] + next nn entries Link [0..nn-1] + next nn entries W [0..nn-1] + last n_col entries Cperm2 [0..n_col-1] + + We have Clen >= n_col + MAX (nz,n_col) + 3*nn+1 + n_col, + So Clen0 >= 2*n_col as required for AMD_postorder + and Clen0 >= n_col + nz as required + */ + + Clen0 = Clen - (nn+1 + 2*nn + n_col) ; + Bp = Ci + Clen0 ; + Link = Bp + (nn+1) ; + W = Link + nn ; + Cperm2 = W + nn ; + ASSERT (Cperm2 + n_col == Ci + Clen) ; + ASSERT (Clen0 >= nz + n_col) ; + ASSERT (Clen0 >= 2*n_col) ; + + /* ------------------------------------------------------------------ */ + /* P = order that rows will be used in UMF_analyze */ + /* ------------------------------------------------------------------ */ + + /* use W to mark rows, and use Link for row permutation P [ [ */ + for (row = 0 ; row < n_row - n1 ; row++) + { + W [row] = FALSE ; + } + P = Link ; + + k = 0 ; + + for (col = 0 ; col < n_col - n1 ; col++) + { + /* empty columns are last in S */ + for (p = Sp [col] ; p < Sp [col+1] ; p++) + { + row = Si [p] ; + if (!W [row]) + { + /* this row has just been seen for the first time */ + W [row] = TRUE ; + P [k++] = row ; + } + } + } + + /* If the matrix has truly empty rows, then P will not be */ + /* complete, and visa versa. The matrix is structurally singular. */ + nempty_row = n_row - n1 - k ; + if (k < n_row - n1) + { + /* complete P by putting empty rows last in their natural order, */ + /* rather than declaring an error (the matrix is singular) */ + for (row = 0 ; row < n_row - n1 ; row++) + { + if (!W [row]) + { + /* W [row] = TRUE ; (not required) */ + P [k++] = row ; + } + } + } + + /* contents of W no longer needed ] */ + +#ifndef NDEBUG + DEBUG3 (("Induced row permutation:\n")) ; + ASSERT (k == n_row - n1) ; + ASSERT (UMF_is_permutation (P, W, n_row - n1, n_row - n1)) ; + for (k = 0 ; k < n_row - n1 ; k++) + { + DEBUG3 ((ID"\n", P [k])) ; + } +#endif + + /* ------------------------------------------------------------------ */ + /* B = row-form of the pattern of S (excluding empty columns) */ + /* ------------------------------------------------------------------ */ + + /* Ci [0 .. Clen-1] holds the following work arrays: + + first Clen2 entries empty space, must be at least >= n_col + next max (nz,1) Bi [0..max (nz,1)-1] + next nn+1 entries Bp [0..nn] + next nn entries Link [0..nn-1] + next nn entries W [0..nn-1] + last n_col entries Cperm2 [0..n_col-1] + + This memory usage is accounted for by the UMF_ANALYZE_CLEN + macro. + */ + + Clen2 = Clen0 ; + snz = Sp [n_col - n1] ; + bsize = MAX (snz, 1) ; + Clen2 -= bsize ; + Bi = Ci + Clen2 ; + ASSERT (Clen2 >= n_col) ; + + (void) UMF_transpose (n_row - n1, n_col - n1 - nempty_col, + Sp, Si, (double *) NULL, + P, (Int *) NULL, 0, Bp, Bi, (double *) NULL, W, FALSE +#ifdef COMPLEX + , (double *) NULL, (double *) NULL, FALSE +#endif + ) ; + + /* contents of Si and Sp no longer needed */ + + /* contents of P (same as Link) and W not needed */ + /* still need Link and W as work arrays, though ] */ + + ASSERT (Bp [0] == 0) ; + ASSERT (Bp [n_row - n1] == snz) ; + + /* increment Bp to point into Ci, not Bi */ + for (i = 0 ; i <= n_row - n1 ; i++) + { + Bp [i] += Clen2 ; + } + ASSERT (Bp [0] == Clen0 - bsize) ; + ASSERT (Bp [n_row - n1] <= Clen0) ; + + /* Ci [0 .. Clen-1] holds the following work arrays: + + first Clen0 entries Ci [0 .. Clen0-1], where the col indices + of B are at the tail end of this part, + and Bp [0] = Clen2 >= n_col. Note + that Clen0 = Clen2 + max (snz,1). + next nn+1 entries Bp [0..nn] + next nn entries Link [0..nn-1] + next nn entries W [0..nn-1] + last n_col entries Cperm2 [0..n_col-1] + */ + + /* ------------------------------------------------------------------ */ + /* analyze */ + /* ------------------------------------------------------------------ */ + + /* only analyze the non-empty, non-singleton part of the matrix */ + ok = UMF_analyze ( + n_row - n1 - nempty_row, + n_col - n1 - nempty_col, + Ci, Bp, Cperm2, fixQ, W, Link, + Fr_ncols, Fr_nrows, Fr_npivcol, + Fr_parent, &nfr, &analyze_compactions) ; + if (!ok) + { + /* :: internal error in umf_analyze :: */ + Info [UMFPACK_STATUS] = UMFPACK_ERROR_internal_error ; + error (&Symbolic, SW) ; + return (UMFPACK_ERROR_internal_error) ; + } + Info [UMFPACK_SYMBOLIC_DEFRAG] += analyze_compactions ; + + /* ------------------------------------------------------------------ */ + /* combine the input permutation and UMF_analyze's permutation */ + /* ------------------------------------------------------------------ */ + + if (!fixQ) + { + /* Cperm2 is the column etree post-ordering */ + ASSERT (UMF_is_permutation (Cperm2, W, + n_col-n1-nempty_col, n_col-n1-nempty_col)) ; + + /* Note that the empty columns remain at the end of Cperm_init */ + for (k = 0 ; k < n_col - n1 - nempty_col ; k++) + { + W [k] = Cperm_init [n1 + Cperm2 [k]] ; + } + + for (k = 0 ; k < n_col - n1 - nempty_col ; k++) + { + Cperm_init [n1 + k] = W [k] ; + } + } + + ASSERT (UMF_is_permutation (Cperm_init, W, n_col, n_col)) ; + + } + + /* ---------------------------------------------------------------------- */ + /* free some of the workspace */ + /* ---------------------------------------------------------------------- */ + + /* (4) The real workspace, Rs, of size n_row doubles has already been + * free'd. An additional workspace of size nz + n_col+1 + n_col integers + * is now free'd as well. */ + + SW->Si = (Int *) UMF_free ((void *) SW->Si) ; + SW->Sp = (Int *) UMF_free ((void *) SW->Sp) ; + SW->Cperm1 = (Int *) UMF_free ((void *) SW->Cperm1) ; + ASSERT (SW->Rs == (double *) NULL) ; + + /* ---------------------------------------------------------------------- */ + /* determine the size of the Symbolic object */ + /* ---------------------------------------------------------------------- */ + + /* ---------------------------------------------------------------------- */ + /* determine the size of the Symbolic object */ + /* ---------------------------------------------------------------------- */ + + nchains = 0 ; + for (i = 0 ; i < nfr ; i++) + { + if (Fr_parent [i] != i+1) + { + nchains++ ; + } + } + + Symbolic->nchains = nchains ; + Symbolic->nfr = nfr ; + Symbolic->esize + = (max_rdeg > dense_row_threshold) ? (n_col - n1 - nempty_col) : 0 ; + + /* true size of Symbolic object */ + Info [UMFPACK_SYMBOLIC_SIZE] = UMF_symbolic_usage (n_row, n_col, nchains, + nfr, Symbolic->esize, prefer_diagonal) ; + + /* actual peak memory usage for UMFPACK_symbolic (actual nfr, nchains) */ + Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] = + SYM_WORK_USAGE (n_col, n_row, Clen) + Info [UMFPACK_SYMBOLIC_SIZE] ; + Symbolic->peak_sym_usage = Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] ; + + DEBUG0 (("Number of fronts: "ID"\n", nfr)) ; + + /* ---------------------------------------------------------------------- */ + /* allocate the second part of the Symbolic object (Front_*, Chain_*) */ + /* ---------------------------------------------------------------------- */ + + /* (5) UMF_malloc is called 7 or 8 times, for a total space of + * (4*(nfr+1) + 3*(nchains+1) + esize) integers, where nfr is the total + * number of frontal matrices and nchains is the total number of frontal + * matrix chains, and where nchains <= nfr <= n_col. esize is zero if there + * are no dense rows, or n_col-n1-nempty_col otherwise (n1 is the number of + * singletons and nempty_col is the number of empty columns). This space is + * part of the Symbolic object and is not free'd unless an error occurs. + * This is between 7 and about 8n integers when A is square. + */ + + /* Note that Symbolic->Front_* does include the dummy placeholder front */ + Symbolic->Front_npivcol = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ; + Symbolic->Front_parent = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ; + Symbolic->Front_1strow = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ; + Symbolic->Front_leftmostdesc = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ; + Symbolic->Chain_start = (Int *) UMF_malloc (nchains+1, sizeof (Int)) ; + Symbolic->Chain_maxrows = (Int *) UMF_malloc (nchains+1, sizeof (Int)) ; + Symbolic->Chain_maxcols = (Int *) UMF_malloc (nchains+1, sizeof (Int)) ; + + fail = (!Symbolic->Front_npivcol || !Symbolic->Front_parent || + !Symbolic->Front_1strow || !Symbolic->Front_leftmostdesc || + !Symbolic->Chain_start || !Symbolic->Chain_maxrows || + !Symbolic->Chain_maxcols) ; + + if (Symbolic->esize > 0) + { + Symbolic->Esize = (Int *) UMF_malloc (Symbolic->esize, sizeof (Int)) ; + fail = fail || !Symbolic->Esize ; + } + + if (fail) + { + DEBUGm4 (("out of memory: rest of symbolic object\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, SW) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + DEBUG0 (("Symbolic UMF_malloc_count - init_count = "ID"\n", + UMF_malloc_count - init_count)) ; + ASSERT (UMF_malloc_count == init_count + 21 + + (SW->Rperm_2by2 != (Int *) NULL) + + (Symbolic->Esize != (Int *) NULL)) ; + + Front_npivcol = Symbolic->Front_npivcol ; + Front_parent = Symbolic->Front_parent ; + Front_1strow = Symbolic->Front_1strow ; + Front_leftmostdesc = Symbolic->Front_leftmostdesc ; + + Chain_start = Symbolic->Chain_start ; + Chain_maxrows = Symbolic->Chain_maxrows ; + Chain_maxcols = Symbolic->Chain_maxcols ; + + Esize = Symbolic->Esize ; + + /* ---------------------------------------------------------------------- */ + /* assign rows to fronts */ + /* ---------------------------------------------------------------------- */ + + /* find InFront, unless colamd has already computed it */ + if (do_UMF_analyze) + { + + DEBUGm4 ((">>>>>>>>>Computing Front_1strow from scratch\n")) ; + /* empty rows go to dummy front nfr */ + for (row = 0 ; row < n_row ; row++) + { + InFront [row] = nfr ; + } + /* assign the singleton pivot rows to the "empty" front */ + for (k = 0 ; k < n1 ; k++) + { + row = Rperm1 [k] ; + InFront [row] = EMPTY ; + } + DEBUG1 (("Front (EMPTY), singleton nrows "ID" ncols "ID"\n", k, k)) ; + newj = n1 ; + for (i = 0 ; i < nfr ; i++) + { + fpivcol = Fr_npivcol [i] ; + f1rows = 0 ; + /* for all pivot columns in front i */ + for (kk = 0 ; kk < fpivcol ; kk++, newj++) + { + j = Cperm_init [newj] ; + ASSERT (IMPLIES (newj >= n_col-nempty_col, + Ap [j+1] - Ap [j] == 0)); + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + row = Ai [p] ; + if (InFront [row] == nfr) + { + /* this row belongs to front i */ + DEBUG1 ((" Row "ID" in Front "ID"\n", row, i)) ; + InFront [row] = i ; + f1rows++ ; + } + } + } + Front_1strow [i] = f1rows ; + DEBUG1 ((" Front "ID" has 1strows: "ID" pivcols "ID"\n", + i, f1rows, fpivcol)) ; + } + + } + else + { + + /* COLAMD has already computed InFront, but it is not yet + * InFront [row] = front i, where row is an original row. It is + * InFront [k-n1] = i for k in the range n1 to n_row-nempty_row, + * and where row = Rperm1 [k]. Need to permute InFront. Also compute + * # of original rows assembled into each front. + * [ use Ci as workspace */ + DEBUGm4 ((">>>>>>>>>Computing Front_1strow from colamd's InFront\n")) ; + for (i = 0 ; i <= nfr ; i++) + { + Front_1strow [i] = 0 ; + } + /* assign the singleton pivot rows to "empty" front */ + for (k = 0 ; k < n1 ; k++) + { + row = Rperm1 [k] ; + Ci [row] = EMPTY ; + } + /* assign the non-empty rows to the front that assembled them */ + for ( ; k < n_row - nempty_row ; k++) + { + row = Rperm1 [k] ; + i = InFront [k - n1] ; + ASSERT (i >= EMPTY && i < nfr) ; + if (i != EMPTY) + { + Front_1strow [i]++ ; + } + /* use Ci as permuted version of InFront */ + Ci [row] = i ; + } + /* empty rows go to the "dummy" front */ + for ( ; k < n_row ; k++) + { + row = Rperm1 [k] ; + Ci [row] = nfr ; + } + /* permute InFront so that InFront [row] = i if the original row is + * in front i */ + for (row = 0 ; row < n_row ; row++) + { + InFront [row] = Ci [row] ; + } + /* ] no longer need Ci as workspace */ + } + +#ifndef NDEBUG + for (row = 0 ; row < n_row ; row++) + { + if (InFront [row] == nfr) + { + DEBUG1 ((" Row "ID" in Dummy Front "ID"\n", row, nfr)) ; + } + else if (InFront [row] == EMPTY) + { + DEBUG1 ((" singleton Row "ID"\n", row)) ; + } + else + { + DEBUG1 ((" Row "ID" in Front "ID"\n", row, nfr)) ; + } + } + for (i = 0 ; i <= nfr ; i++) + { + DEBUG1 (("Front "ID" has 1strows: "ID" pivcols "ID"\n", + i, f1rows, fpivcol)) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* copy front information into Symbolic object */ + /* ---------------------------------------------------------------------- */ + + k = n1 ; + for (i = 0 ; i < nfr ; i++) + { + fpivcol = Fr_npivcol [i] ; + DEBUG1 (("Front "ID" k "ID" npivcol "ID" nrows "ID" ncols "ID"\n", + i, k, fpivcol, Fr_nrows [i], Fr_ncols [i])) ; + k += fpivcol ; + /* copy Front info into Symbolic object from SW */ + Front_npivcol [i] = fpivcol ; + Front_parent [i] = Fr_parent [i] ; + } + + /* assign empty columns to dummy placehold front nfr */ + DEBUG1 (("Dummy Cols in Front "ID" : "ID"\n", nfr, n_col-k)) ; + Front_npivcol [nfr] = n_col - k ; + Front_parent [nfr] = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* find initial row permutation */ + /* ---------------------------------------------------------------------- */ + + /* order the singleton pivot rows */ + for (k = 0 ; k < n1 ; k++) + { + Rperm_init [k] = Rperm1 [k] ; + } + + /* determine the first row in each front (in the new row ordering) */ + for (i = 0 ; i < nfr ; i++) + { + f1rows = Front_1strow [i] ; + DEBUG1 (("Front "ID" : npivcol "ID" parent "ID, + i, Front_npivcol [i], Front_parent [i])) ; + DEBUG1 ((" 1st rows in Front "ID" : "ID"\n", i, f1rows)) ; + Front_1strow [i] = k ; + k += f1rows ; + } + + /* assign empty rows to dummy placehold front nfr */ + DEBUG1 (("Rows in Front "ID" (dummy): "ID"\n", nfr, n_row-k)) ; + Front_1strow [nfr] = k ; + DEBUG1 (("nfr "ID" 1strow[nfr] "ID" nrow "ID"\n", nfr, k, n_row)) ; + + /* Use Ci as temporary workspace for F1 */ + F1 = Ci ; /* [ of size nfr+1 */ + ASSERT (Clen >= 2*n_row + nfr+1) ; + + for (i = 0 ; i <= nfr ; i++) + { + F1 [i] = Front_1strow [i] ; + } + + for (row = 0 ; row < n_row ; row++) + { + i = InFront [row] ; + if (i != EMPTY) + { + newrow = F1 [i]++ ; + ASSERT (newrow >= n1) ; + Rperm_init [newrow] = row ; + } + } + Rperm_init [n_row] = EMPTY ; /* unused */ + +#ifndef NDEBUG + for (k = 0 ; k < n_row ; k++) + { + DEBUG2 (("Rperm_init ["ID"] = "ID"\n", k, Rperm_init [k])) ; + } +#endif + + /* ] done using F1 */ + + /* ---------------------------------------------------------------------- */ + /* find the diagonal map */ + /* ---------------------------------------------------------------------- */ + + /* Rperm_init [newrow] = row gives the row permutation that is implied + * by the column permutation, where "row" is a row index of the original + * matrix A. It is not dependent on the Rperm_2by2 permutation, which + * only redefines the "diagonal". Both are used to construct the + * Diagonal_map. Diagonal_map only needs to be defined for + * k = n1 to nn - nempty, but go ahead and define it for all of + * k = 0 to nn */ + + if (prefer_diagonal) + { + Int *Diagonal_map ; + ASSERT (n_row == n_col && nn == n_row) ; + ASSERT (nempty_row == nempty_col && nempty == nempty_row) ; + + /* allocate the Diagonal_map */ + Symbolic->Diagonal_map = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Diagonal_map = Symbolic->Diagonal_map ; + if (Diagonal_map == (Int *) NULL) + { + /* :: out of memory (diagonal map) :: */ + DEBUGm4 (("out of memory: Diagonal map\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, SW) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + /* use Ci as workspace to compute the inverse of Rperm_init [ */ + for (newrow = 0 ; newrow < nn ; newrow++) + { + oldrow = Rperm_init [newrow] ; + ASSERT (oldrow >= 0 && oldrow < nn) ; + Ci [oldrow] = newrow ; + } + if (strategy == UMFPACK_STRATEGY_2BY2) + { + ASSERT (Rperm_2by2 != (Int *) NULL) ; + for (newcol = 0 ; newcol < nn ; newcol++) + { + oldcol = Cperm_init [newcol] ; + /* 2-by-2 pivoting done in S */ + oldrow = Rperm_2by2 [oldcol] ; + newrow = Ci [oldrow] ; + Diagonal_map [newcol] = newrow ; + } + } + else + { + for (newcol = 0 ; newcol < nn ; newcol++) + { + oldcol = Cperm_init [newcol] ; + /* no 2-by-2 pivoting in S */ + oldrow = oldcol ; + newrow = Ci [oldrow] ; + Diagonal_map [newcol] = newrow ; + } + } + +#ifndef NDEBUG + DEBUG1 (("\nDiagonal map:\n")) ; + for (newcol = 0 ; newcol < nn ; newcol++) + { + oldcol = Cperm_init [newcol] ; + DEBUG3 (("oldcol "ID" newcol "ID":\n", oldcol, newcol)) ; + for (p = Ap [oldcol] ; p < Ap [oldcol+1] ; p++) + { + Entry aij ; + oldrow = Ai [p] ; + newrow = Ci [oldrow] ; + if (Ax != (double *) NULL +#ifdef COMPLEX + && Az != (double *) NULL +#endif + ) + { + ASSIGN (aij, Ax [p], Az [p]) ; + } + if (oldrow == oldcol) + { + DEBUG2 ((" old diagonal : oldcol "ID" oldrow "ID" ", + oldcol, oldrow)) ; + EDEBUG2 (aij) ; + DEBUG2 (("\n")) ; + } + if (newrow == Diagonal_map [newcol]) + { + DEBUG2 ((" MAP diagonal : newcol "ID" MAProw "ID" ", + newcol, Diagonal_map [newrow])) ; + EDEBUG2 (aij) ; + DEBUG2 (("\n")) ; + } + } + } +#endif + /* done using Ci as workspace ] */ + + } + + /* ---------------------------------------------------------------------- */ + /* find the leftmost descendant of each front */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i <= nfr ; i++) + { + Front_leftmostdesc [i] = EMPTY ; + } + + for (i = 0 ; i < nfr ; i++) + { + /* start at i and walk up the tree */ + DEBUG2 (("Walk up front tree from "ID"\n", i)) ; + j = i ; + while (j != EMPTY && Front_leftmostdesc [j] == EMPTY) + { + DEBUG3 ((" Leftmost desc of "ID" is "ID"\n", j, i)) ; + Front_leftmostdesc [j] = i ; + j = Front_parent [j] ; + DEBUG3 ((" go to j = "ID"\n", j)) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* find the frontal matrix chains and max frontal matrix sizes */ + /* ---------------------------------------------------------------------- */ + + maxnrows = 1 ; /* max # rows in any front */ + maxncols = 1 ; /* max # cols in any front */ + dmaxfrsize = 1 ; /* max frontal matrix size */ + + /* start the first chain */ + nchains = 0 ; /* number of chains */ + Chain_start [0] = 0 ; /* front 0 starts a new chain */ + maxrows = 1 ; /* max # rows for any front in current chain */ + maxcols = 1 ; /* max # cols for any front in current chain */ + DEBUG1 (("Constructing chains:\n")) ; + + for (i = 0 ; i < nfr ; i++) + { + /* get frontal matrix info */ + fpivcol = Front_npivcol [i] ; /* # candidate pivot columns */ + fallrows = Fr_nrows [i] ; /* all rows (not just Schur comp) */ + fallcols = Fr_ncols [i] ; /* all cols (not just Schur comp) */ + parent = Front_parent [i] ; /* parent in column etree */ + fpiv = MIN (fpivcol, fallrows) ; /* # pivot rows and cols */ + maxrows = MAX (maxrows, fallrows) ; + maxcols = MAX (maxcols, fallcols) ; + + DEBUG1 (("Front: "ID", pivcol "ID", "ID"-by-"ID" parent "ID + ", npiv "ID" Chain: maxrows "ID" maxcols "ID"\n", i, fpivcol, + fallrows, fallcols, parent, fpiv, maxrows, maxcols)) ; + + if (parent != i+1) + { + /* this is the end of a chain */ + double s ; + DEBUG1 (("\nEnd of chain "ID"\n", nchains)) ; + + /* make sure maxrows is an odd number */ + ASSERT (maxrows >= 0) ; + if (maxrows % 2 == 0) maxrows++ ; + + DEBUG1 (("Chain maxrows "ID" maxcols "ID"\n", maxrows, maxcols)) ; + + Chain_maxrows [nchains] = maxrows ; + Chain_maxcols [nchains] = maxcols ; + + /* keep track of the maximum front size for all chains */ + + /* for Info only: */ + s = (double) maxrows * (double) maxcols ; + dmaxfrsize = MAX (dmaxfrsize, s) ; + + /* for the subsequent numerical factorization */ + maxnrows = MAX (maxnrows, maxrows) ; + maxncols = MAX (maxncols, maxcols) ; + + DEBUG1 (("Chain dmaxfrsize %g\n\n", dmaxfrsize)) ; + + /* start the next chain */ + nchains++ ; + Chain_start [nchains] = i+1 ; + maxrows = 1 ; + maxcols = 1 ; + } + } + + /* for Info only: */ + dmaxfrsize = ceil (dmaxfrsize) ; + DEBUGm1 (("dmaxfrsize %30.20g Int_MAX "ID"\n", dmaxfrsize, Int_MAX)) ; + ASSERT (Symbolic->nchains == nchains) ; + + /* For allocating objects in umfpack_numeric (does not include all possible + * pivots, particularly pivots from prior fronts in the chain. Need to add + * nb to these to get the # of columns in the L block, for example. This + * is the largest row dimension and largest column dimension of any frontal + * matrix. maxnrows is always odd. */ + Symbolic->maxnrows = maxnrows ; + Symbolic->maxncols = maxncols ; + DEBUGm3 (("maxnrows "ID" maxncols "ID"\n", maxnrows, maxncols)) ; + + /* ---------------------------------------------------------------------- */ + /* find the initial element sizes */ + /* ---------------------------------------------------------------------- */ + + if (max_rdeg > dense_row_threshold) + { + /* there are one or more dense rows in the input matrix */ + /* count the number of dense rows in each column */ + /* use Ci as workspace for inverse of Rperm_init [ */ + ASSERT (Esize != (Int *) NULL) ; + for (newrow = 0 ; newrow < n_row ; newrow++) + { + oldrow = Rperm_init [newrow] ; + ASSERT (oldrow >= 0 && oldrow < nn) ; + Ci [oldrow] = newrow ; + } + for (col = n1 ; col < n_col - nempty_col ; col++) + { + oldcol = Cperm_init [col] ; + esize = Cdeg [oldcol] ; + ASSERT (esize > 0) ; + for (p = Ap [oldcol] ; p < Ap [oldcol+1] ; p++) + { + oldrow = Ai [p] ; + newrow = Ci [oldrow] ; + if (newrow >= n1 && Rdeg [oldrow] > dense_row_threshold) + { + esize-- ; + } + } + ASSERT (esize >= 0) ; + Esize [col - n1] = esize ; + } + /* done using Ci as workspace ] */ + } + + /* If there are no dense rows, then Esize [col-n1] is identical to + * Cdeg [col], once Cdeg is permuted below */ + + /* ---------------------------------------------------------------------- */ + /* permute Cdeg and Rdeg according to initial column and row permutation */ + /* ---------------------------------------------------------------------- */ + + /* use Ci as workspace [ */ + for (k = 0 ; k < n_col ; k++) + { + Ci [k] = Cdeg [Cperm_init [k]] ; + } + for (k = 0 ; k < n_col ; k++) + { + Cdeg [k] = Ci [k] ; + } + for (k = 0 ; k < n_row ; k++) + { + Ci [k] = Rdeg [Rperm_init [k]] ; + } + for (k = 0 ; k < n_row ; k++) + { + Rdeg [k] = Ci [k] ; + } + /* done using Ci as workspace ] */ + + /* ---------------------------------------------------------------------- */ + /* simulate UMF_kernel_init */ + /* ---------------------------------------------------------------------- */ + + /* count elements and tuples at tail, LU factors of singletons, and + * head and tail markers */ + + dlnz = n_inner ; /* upper limit of nz in L (incl diag) */ + dunz = dlnz ; /* upper limit of nz in U (incl diag) */ + + /* head marker */ + head_usage = 1 ; + dhead_usage = 1 ; + + /* tail markers: */ + tail_usage = 2 ; + dtail_usage = 2 ; + + /* allocate the Rpi and Rpx workspace for UMF_kernel_init (incl. headers) */ + tail_usage += UNITS (Int *, n_row+1) + UNITS (Entry *, n_row+1) + 2 ; + dtail_usage += DUNITS (Int *, n_row+1) + DUNITS (Entry *, n_row+1) + 2 ; + DEBUG1 (("Symbolic usage after Rpi/Rpx allocation: head "ID" tail "ID"\n", + head_usage, tail_usage)) ; + + /* LU factors for singletons, at the head of memory */ + for (k = 0 ; k < n1 ; k++) + { + lnz = Cdeg [k] - 1 ; + unz = Rdeg [k] - 1 ; + dlnz += lnz ; + dunz += unz ; + DEBUG1 (("singleton k "ID" pivrow "ID" pivcol "ID" lnz "ID" unz "ID"\n", + k, Rperm_init [k], Cperm_init [k], lnz, unz)) ; + head_usage += UNITS (Int, lnz) + UNITS (Entry, lnz) + + UNITS (Int, unz) + UNITS (Entry, unz) ; + dhead_usage += DUNITS (Int, lnz) + DUNITS (Entry, lnz) + + DUNITS (Int, unz) + DUNITS (Entry, unz) ; + } + DEBUG1 (("Symbolic init head usage: "ID" for LU singletons\n",head_usage)) ; + + /* column elements: */ + for (k = n1 ; k < n_col - nempty_col; k++) + { + esize = Esize ? Esize [k-n1] : Cdeg [k] ; + DEBUG2 ((" esize: "ID"\n", esize)) ; + ASSERT (esize >= 0) ; + if (esize > 0) + { + tail_usage += GET_ELEMENT_SIZE (esize, 1) + 1 ; + dtail_usage += DGET_ELEMENT_SIZE (esize, 1) + 1 ; + } + } + + /* dense row elements */ + if (Esize) + { + Int nrow_elements = 0 ; + for (k = n1 ; k < n_row - nempty_row ; k++) + { + rdeg = Rdeg [k] ; + if (rdeg > dense_row_threshold) + { + tail_usage += GET_ELEMENT_SIZE (1, rdeg) + 1 ; + dtail_usage += GET_ELEMENT_SIZE (1, rdeg) + 1 ; + nrow_elements++ ; + } + } + Info [UMFPACK_NDENSE_ROW] = nrow_elements ; + } + + DEBUG1 (("Symbolic usage: "ID" = head "ID" + tail "ID" after els\n", + head_usage + tail_usage, head_usage, tail_usage)) ; + + /* compute the tuple lengths */ + if (Esize) + { + /* row tuples */ + for (row = n1 ; row < n_row ; row++) + { + rdeg = Rdeg [row] ; + tlen = (rdeg > dense_row_threshold) ? 1 : rdeg ; + tail_usage += 1 + UNITS (Tuple, TUPLES (tlen)) ; + dtail_usage += 1 + DUNITS (Tuple, TUPLES (tlen)) ; + } + /* column tuples */ + for (col = n1 ; col < n_col - nempty_col ; col++) + { + /* tlen is 1 plus the number of dense rows in this column */ + esize = Esize [col - n1] ; + tlen = (esize > 0) + (Cdeg [col] - esize) ; + tail_usage += 1 + UNITS (Tuple, TUPLES (tlen)) ; + dtail_usage += 1 + DUNITS (Tuple, TUPLES (tlen)) ; + } + for ( ; col < n_col ; col++) + { + tail_usage += 1 + UNITS (Tuple, TUPLES (0)) ; + dtail_usage += 1 + DUNITS (Tuple, TUPLES (0)) ; + } + } + else + { + /* row tuples */ + for (row = n1 ; row < n_row ; row++) + { + tlen = Rdeg [row] ; + tail_usage += 1 + UNITS (Tuple, TUPLES (tlen)) ; + dtail_usage += 1 + DUNITS (Tuple, TUPLES (tlen)) ; + } + /* column tuples */ + for (col = n1 ; col < n_col ; col++) + { + tail_usage += 1 + UNITS (Tuple, TUPLES (1)) ; + dtail_usage += 1 + DUNITS (Tuple, TUPLES (1)) ; + } + } + + Symbolic->num_mem_init_usage = head_usage + tail_usage ; + DEBUG1 (("Symbolic usage: "ID" = head "ID" + tail "ID" final\n", + Symbolic->num_mem_init_usage, head_usage, tail_usage)) ; + + ASSERT (UMF_is_permutation (Rperm_init, Ci, n_row, n_row)) ; + + /* initial head and tail usage in Numeric->Memory */ + dmax_usage = dhead_usage + dtail_usage ; + dmax_usage = MAX (Symbolic->num_mem_init_usage, ceil (dmax_usage)) ; + Info [UMFPACK_VARIABLE_INIT_ESTIMATE] = dmax_usage ; + + /* In case Symbolic->num_mem_init_usage overflows, keep as a double, too */ + Symbolic->dnum_mem_init_usage = dmax_usage ; + + /* free the Rpi and Rpx workspace */ + tail_usage -= UNITS (Int *, n_row+1) + UNITS (Entry *, n_row+1) ; + dtail_usage -= DUNITS (Int *, n_row+1) + DUNITS (Entry *, n_row+1) ; + + /* ---------------------------------------------------------------------- */ + /* simulate UMF_kernel, assuming unsymmetric pivoting */ + /* ---------------------------------------------------------------------- */ + + /* Use Ci as temporary workspace for link lists [ */ + Link = Ci ; + for (i = 0 ; i < nfr ; i++) + { + Link [i] = EMPTY ; + } + + flops = 0 ; /* flop count upper bound */ + + for (chain = 0 ; chain < nchains ; chain++) + { + double fsize ; + f1 = Chain_start [chain] ; + f2 = Chain_start [chain+1] - 1 ; + + /* allocate frontal matrix working array (C, L, and U) */ + dr = Chain_maxrows [chain] ; + dc = Chain_maxcols [chain] ; + fsize = + nb*nb /* LU is nb-by-nb */ + + dr*nb /* L is dr-by-nb */ + + nb*dc /* U is nb-by-dc, stored by rows */ + + dr*dc ; /* C is dr by dc */ + dtail_usage += DUNITS (Entry, fsize) ; + dmax_usage = MAX (dmax_usage, dhead_usage + dtail_usage) ; + + for (i = f1 ; i <= f2 ; i++) + { + + /* get frontal matrix info */ + fpivcol = Front_npivcol [i] ; /* # candidate pivot columns */ + fallrows = Fr_nrows [i] ; /* all rows (not just Schur comp*/ + fallcols = Fr_ncols [i] ; /* all cols (not just Schur comp*/ + parent = Front_parent [i] ; /* parent in column etree */ + fpiv = MIN (fpivcol, fallrows) ; /* # pivot rows and cols */ + f = (double) fpiv ; + r = fallrows - fpiv ; /* # rows in Schur comp. */ + c = fallcols - fpiv ; /* # cols in Schur comp. */ + + /* assemble all children of front i in column etree */ + for (child = Link [i] ; child != EMPTY ; child = Link [child]) + { + ASSERT (child >= 0 && child < i) ; + ASSERT (Front_parent [child] == i) ; + /* free the child element and remove it from tuple lists */ + cp = MIN (Front_npivcol [child], Fr_nrows [child]) ; + cr = Fr_nrows [child] - cp ; + cc = Fr_ncols [child] - cp ; + ASSERT (cp >= 0 && cr >= 0 && cc >= 0) ; + dtail_usage -= ELEMENT_SIZE (cr, cc) ; + + } + + /* The flop count computed here is "canonical". */ + + /* factorize the frontal matrix */ + flops += DIV_FLOPS * (f*r + (f-1)*f/2) /* scale pivot columns */ + /* f outer products: */ + + MULTSUB_FLOPS * (f*r*c + (r+c)*(f-1)*f/2 + (f-1)*f*(2*f-1)/6); + + /* count nonzeros and memory usage in double precision */ + dlf = (f*f-f)/2 + f*r ; /* nz in L below diagonal */ + duf = (f*f-f)/2 + f*c ; /* nz in U above diagonal */ + dlnz += dlf ; + dunz += duf ; + + /* store f columns of L and f rows of U */ + dhead_usage += + DUNITS (Entry, dlf + duf) /* numerical values (excl diag) */ + + DUNITS (Int, r + c + f) ; /* indices (compressed) */ + + if (parent != EMPTY) + { + /* create new element and place in tuple lists */ + dtail_usage += ELEMENT_SIZE (r, c) ; + + /* place in link list of parent */ + Link [i] = Link [parent] ; + Link [parent] = i ; + } + + /* keep track of peak Numeric->Memory usage */ + dmax_usage = MAX (dmax_usage, dhead_usage + dtail_usage) ; + + } + + /* free the current frontal matrix */ + dtail_usage -= DUNITS (Entry, fsize) ; + } + + dhead_usage = ceil (dhead_usage) ; + dmax_usage = ceil (dmax_usage) ; + Symbolic->num_mem_size_est = dhead_usage ; + Symbolic->num_mem_usage_est = dmax_usage ; + Symbolic->lunz_bound = dlnz + dunz - n_inner ; + + /* ] done using Ci as workspace for Link array */ + + /* ---------------------------------------------------------------------- */ + /* estimate total memory usage in UMFPACK_numeric */ + /* ---------------------------------------------------------------------- */ + + UMF_set_stats ( + Info, + Symbolic, + dmax_usage, /* estimated peak size of Numeric->Memory */ + dhead_usage, /* estimated final size of Numeric->Memory */ + flops, /* estimated "true flops" */ + dlnz, /* estimated nz in L */ + dunz, /* estimated nz in U */ + dmaxfrsize, /* estimated largest front size */ + (double) n_col, /* worst case Numeric->Upattern size */ + (double) n_inner, /* max possible pivots to be found */ + (double) maxnrows, /* estimated largest #rows in front */ + (double) maxncols, /* estimated largest #cols in front */ + TRUE, /* assume scaling is to be performed */ + prefer_diagonal, + ESTIMATE) ; + + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + for (i = 0 ; i < nchains ; i++) + { + DEBUG2 (("Chain "ID" start "ID" end "ID" maxrows "ID" maxcols "ID"\n", + i, Chain_start [i], Chain_start [i+1] - 1, + Chain_maxrows [i], Chain_maxcols [i])) ; + UMF_dump_chain (Chain_start [i], Fr_parent, Fr_npivcol, Fr_nrows, + Fr_ncols, nfr) ; + } + fpivcol = 0 ; + for (i = 0 ; i < nfr ; i++) + { + fpivcol = MAX (fpivcol, Front_npivcol [i]) ; + } + DEBUG0 (("Max pivot cols in any front: "ID"\n", fpivcol)) ; + DEBUG1 (("Largest front: maxnrows "ID" maxncols "ID" dmaxfrsize %g\n", + maxnrows, maxncols, dmaxfrsize)) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* UMFPACK_symbolic was successful, return the object handle */ + /* ---------------------------------------------------------------------- */ + + Symbolic->valid = SYMBOLIC_VALID ; + *SymbolicHandle = (void *) Symbolic ; + + /* ---------------------------------------------------------------------- */ + /* free workspace */ + /* ---------------------------------------------------------------------- */ + + /* (6) The last of the workspace is free'd. The final Symbolic object + * consists of 12 to 14 allocated objects. Its final total size is lies + * roughly between 4*n and 13*n for a square matrix, which is all that is + * left of the memory allocated by this routine. If an error occurs, the + * entire Symbolic object is free'd when this routine returns (the error + * return routine, below). + */ + + free_work (SW) ; + + DEBUG0 (("(3)Symbolic UMF_malloc_count - init_count = "ID"\n", + UMF_malloc_count - init_count)) ; + ASSERT (UMF_malloc_count == init_count + 12 + + (Symbolic->Esize != (Int *) NULL) + + (Symbolic->Diagonal_map != (Int *) NULL)) ; + + /* ---------------------------------------------------------------------- */ + /* get the time used by UMFPACK_*symbolic */ + /* ---------------------------------------------------------------------- */ + + umfpack_toc (stats) ; + Info [UMFPACK_SYMBOLIC_WALLTIME] = stats [0] ; + Info [UMFPACK_SYMBOLIC_TIME] = stats [1] ; + + return (UMFPACK_OK) ; +} + + +/* ========================================================================== */ +/* === free_work ============================================================ */ +/* ========================================================================== */ + +PRIVATE void free_work +( + SWType *SW +) +{ + if (SW) + { + SW->Rperm_2by2 = (Int *) UMF_free ((void *) SW->Rperm_2by2) ; + SW->InvRperm1 = (Int *) UMF_free ((void *) SW->InvRperm1) ; + SW->Rs = (double *) UMF_free ((void *) SW->Rs) ; + SW->Si = (Int *) UMF_free ((void *) SW->Si) ; + SW->Sp = (Int *) UMF_free ((void *) SW->Sp) ; + SW->Ci = (Int *) UMF_free ((void *) SW->Ci) ; + SW->Front_npivcol = (Int *) UMF_free ((void *) SW->Front_npivcol); + SW->Front_nrows = (Int *) UMF_free ((void *) SW->Front_nrows) ; + SW->Front_ncols = (Int *) UMF_free ((void *) SW->Front_ncols) ; + SW->Front_parent = (Int *) UMF_free ((void *) SW->Front_parent) ; + SW->Front_cols = (Int *) UMF_free ((void *) SW->Front_cols) ; + SW->Cperm1 = (Int *) UMF_free ((void *) SW->Cperm1) ; + SW->Rperm1 = (Int *) UMF_free ((void *) SW->Rperm1) ; + SW->InFront = (Int *) UMF_free ((void *) SW->InFront) ; + } +} + + +/* ========================================================================== */ +/* === error ================================================================ */ +/* ========================================================================== */ + +/* Error return from UMFPACK_symbolic. Free all allocated memory. */ + +PRIVATE void error +( + SymbolicType **Symbolic, + SWType *SW +) +{ + + free_work (SW) ; + UMFPACK_free_symbolic ((void **) Symbolic) ; + ASSERT (UMF_malloc_count == init_count) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_control.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_control.c new file mode 100644 index 0000000000..fdc614f279 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_control.c @@ -0,0 +1,387 @@ +/* ========================================================================== */ +/* === UMFPACK_report_control =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints the control settings. See umfpack_report_control.h + for details. +*/ + +#include "umf_internal.h" + +GLOBAL void UMFPACK_report_control +( + const double Control [UMFPACK_CONTROL] +) +{ + Int prl, nb, irstep, strategy, scale, s ; + double drow, dcol, relpt, relpt2, alloc_init, front_alloc_init, amd_alpha, + tol, force_fixQ, droptol, aggr ; + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (prl < 2) + { + /* default is to print nothing */ + return ; + } + + PRINTF (("\n%s, Control:\n\n", UMFPACK_VERSION)) ; + + /* ---------------------------------------------------------------------- */ + /* run-time options */ + /* ---------------------------------------------------------------------- */ + + /* This is a "run-time" option because all four umfpack_* versions */ + /* compiled into the UMFPACK library. */ + +#ifdef DINT + PRINTF ((" Matrix entry defined as: double\n")) ; + PRINTF ((" Int (generic integer) defined as: int\n")) ; +#endif +#ifdef DLONG + PRINTF ((" Matrix entry defined as: double\n")) ; + PRINTF ((" Int (generic integer) defined as: long\n")) ; +#endif +#ifdef ZINT + PRINTF ((" Matrix entry defined as: double complex\n")) ; + PRINTF ((" Int (generic integer) defined as: int\n")) ; +#endif +#ifdef ZLONG + PRINTF ((" Matrix entry defined as: double complex\n")) ; + PRINTF ((" Int (generic integer) defined as: long\n")) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* printing level */ + /* ---------------------------------------------------------------------- */ + + PRINTF (("\n "ID": print level: "ID"\n", + (Int) INDEX (UMFPACK_PRL), prl)) ; + + /* ---------------------------------------------------------------------- */ + /* dense row/col parameters */ + /* ---------------------------------------------------------------------- */ + + drow = GET_CONTROL (UMFPACK_DENSE_ROW, UMFPACK_DEFAULT_DENSE_ROW) ; + dcol = GET_CONTROL (UMFPACK_DENSE_COL, UMFPACK_DEFAULT_DENSE_COL) ; + + PRINTF ((" "ID": dense row parameter: %g\n", + (Int) INDEX (UMFPACK_DENSE_ROW), drow)) ; + PRINTF ((" \"dense\" rows have > max (16, (%g)*16*sqrt(n_col)" + " entries)\n", drow)) ; + PRINTF ((" "ID": dense column parameter: %g\n", + (Int) INDEX (UMFPACK_DENSE_COL), dcol)) ; + PRINTF ((" \"dense\" columns have > max (16, (%g)*16*sqrt(n_row)" + " entries)\n", dcol)) ; + + /* ---------------------------------------------------------------------- */ + /* pivot tolerance */ + /* ---------------------------------------------------------------------- */ + + relpt = GET_CONTROL (UMFPACK_PIVOT_TOLERANCE, + UMFPACK_DEFAULT_PIVOT_TOLERANCE) ; + relpt = MAX (0.0, MIN (relpt, 1.0)) ; + PRINTF ((" "ID": pivot tolerance: %g\n", + (Int) INDEX (UMFPACK_PIVOT_TOLERANCE), relpt)) ; + + /* ---------------------------------------------------------------------- */ + /* block size */ + /* ---------------------------------------------------------------------- */ + + nb = GET_CONTROL (UMFPACK_BLOCK_SIZE, UMFPACK_DEFAULT_BLOCK_SIZE) ; + nb = MAX (1, nb) ; + PRINTF ((" "ID": block size for dense matrix kernels: "ID"\n", + (Int) INDEX (UMFPACK_BLOCK_SIZE), nb)) ; + + /* ---------------------------------------------------------------------- */ + /* strategy */ + /* ---------------------------------------------------------------------- */ + + strategy = GET_CONTROL (UMFPACK_STRATEGY, UMFPACK_DEFAULT_STRATEGY) ; + if (strategy < UMFPACK_STRATEGY_AUTO + || strategy > UMFPACK_STRATEGY_SYMMETRIC) + { + strategy = UMFPACK_STRATEGY_AUTO ; + } + + PRINTF ((" "ID": strategy: "ID, + (Int) INDEX (UMFPACK_STRATEGY), strategy)) ; + + if (strategy == UMFPACK_STRATEGY_SYMMETRIC) + { + PRINTF ((" (symmetric)\n" + " Q = AMD (A+A'), Q not refined during numerical\n" + " factorization, and diagonal pivoting (P=Q') attempted.\n")) ; + } + else if (strategy == UMFPACK_STRATEGY_UNSYMMETRIC) + { + PRINTF ((" (unsymmetric)\n" + " Q = COLAMD (A), Q refined during numerical\n" + " factorization, and no attempt at diagonal pivoting.\n")) ; + } + else if (strategy == UMFPACK_STRATEGY_2BY2) + { + PRINTF ((" (symmetric, with 2-by-2 block pivoting)\n" + " P2 = row permutation that tries to place large entries on\n" + " the diagonal. Q = AMD (P2*A+(P2*A)'), Q not refined during\n" + " numerical factorization, attempt to select pivots from the\n" + " diagonal of P2*A.\n")) ; + } + else /* auto strategy */ + { + strategy = UMFPACK_STRATEGY_AUTO ; + PRINTF ((" (auto)\n")) ; + } + + /* ---------------------------------------------------------------------- */ + /* initial allocation parameter */ + /* ---------------------------------------------------------------------- */ + + alloc_init = GET_CONTROL (UMFPACK_ALLOC_INIT, UMFPACK_DEFAULT_ALLOC_INIT) ; + if (alloc_init >= 0) + { + PRINTF ((" "ID": initial allocation ratio: %g\n", + (Int) INDEX (UMFPACK_ALLOC_INIT), alloc_init)) ; + } + else + { + s = -alloc_init ; + s = MAX (1, s) ; + PRINTF ((" "ID": initial allocation (in Units): "ID"\n", + (Int) INDEX (UMFPACK_ALLOC_INIT), s)) ; + } + + /* ---------------------------------------------------------------------- */ + /* maximum iterative refinement steps */ + /* ---------------------------------------------------------------------- */ + + irstep = GET_CONTROL (UMFPACK_IRSTEP, UMFPACK_DEFAULT_IRSTEP) ; + irstep = MAX (0, irstep) ; + PRINTF ((" "ID": max iterative refinement steps: "ID"\n", + (Int) INDEX (UMFPACK_IRSTEP), irstep)) ; + + /* ---------------------------------------------------------------------- */ + /* 2-by-2 pivot tolerance */ + /* ---------------------------------------------------------------------- */ + + tol = GET_CONTROL (UMFPACK_2BY2_TOLERANCE, UMFPACK_DEFAULT_2BY2_TOLERANCE) ; + tol = MAX (0.0, MIN (tol, 1.0)) ; + PRINTF ((" "ID": 2-by-2 pivot tolerance: %g\n", + (Int) INDEX (UMFPACK_2BY2_TOLERANCE), tol)) ; + + /* ---------------------------------------------------------------------- */ + /* force fixQ */ + /* ---------------------------------------------------------------------- */ + + force_fixQ = GET_CONTROL (UMFPACK_FIXQ, UMFPACK_DEFAULT_FIXQ) ; + PRINTF ((" "ID": Q fixed during numerical factorization: %g ", + (Int) INDEX (UMFPACK_FIXQ), force_fixQ)) ; + if (force_fixQ > 0) + { + PRINTF (("(yes)\n")) ; + } + else if (force_fixQ < 0) + { + PRINTF (("(no)\n")) ; + } + else + { + PRINTF (("(auto)\n")) ; + } + + /* ---------------------------------------------------------------------- */ + /* AMD parameters */ + /* ---------------------------------------------------------------------- */ + + amd_alpha = GET_CONTROL (UMFPACK_AMD_DENSE, UMFPACK_DEFAULT_AMD_DENSE) ; + PRINTF ((" "ID": AMD dense row/col parameter: %g\n", + (Int) INDEX (UMFPACK_AMD_DENSE), amd_alpha)) ; + if (amd_alpha < 0) + { + PRINTF ((" no \"dense\" rows/columns\n")) ; + } + else + { + PRINTF ((" \"dense\" rows/columns have > max (16, (%g)*sqrt(n))" + " entries\n", amd_alpha)) ; + } + PRINTF ((" Only used if the AMD ordering is used.\n")) ; + + /* ---------------------------------------------------------------------- */ + /* pivot tolerance for symmetric pivoting */ + /* ---------------------------------------------------------------------- */ + + relpt2 = GET_CONTROL (UMFPACK_SYM_PIVOT_TOLERANCE, + UMFPACK_DEFAULT_SYM_PIVOT_TOLERANCE) ; + relpt2 = MAX (0.0, MIN (relpt2, 1.0)) ; + PRINTF ((" "ID": diagonal pivot tolerance: %g\n" + " Only used if diagonal pivoting is attempted.\n", + (Int) INDEX (UMFPACK_SYM_PIVOT_TOLERANCE), relpt2)) ; + + /* ---------------------------------------------------------------------- */ + /* scaling */ + /* ---------------------------------------------------------------------- */ + + scale = GET_CONTROL (UMFPACK_SCALE, UMFPACK_DEFAULT_SCALE) ; + if (scale != UMFPACK_SCALE_NONE && scale != UMFPACK_SCALE_MAX) + { + scale = UMFPACK_DEFAULT_SCALE ; + } + PRINTF ((" "ID": scaling: "ID, (Int) INDEX (UMFPACK_SCALE), scale)) ; + if (scale == UMFPACK_SCALE_NONE) + { + PRINTF ((" (no)")) ; + } + else if (scale == UMFPACK_SCALE_SUM) + { + PRINTF ((" (divide each row by sum of abs. values in each row)")) ; + } + else if (scale == UMFPACK_SCALE_MAX) + { + PRINTF ((" (divide each row by max. abs. value in each row)")) ; + } + PRINTF (("\n")) ; + + /* ---------------------------------------------------------------------- */ + /* frontal matrix allocation parameter */ + /* ---------------------------------------------------------------------- */ + + front_alloc_init = GET_CONTROL (UMFPACK_FRONT_ALLOC_INIT, + UMFPACK_DEFAULT_FRONT_ALLOC_INIT) ; + front_alloc_init = MIN (1.0, front_alloc_init) ; + if (front_alloc_init >= 0) + { + PRINTF ((" "ID": frontal matrix allocation ratio: %g\n", + (Int) INDEX (UMFPACK_FRONT_ALLOC_INIT), front_alloc_init)) ; + } + else + { + s = -front_alloc_init ; + s = MAX (1, s) ; + PRINTF ((" "ID": initial frontal matrix size (# of Entry's): "ID"\n", + (Int) INDEX (UMFPACK_FRONT_ALLOC_INIT), s)) ; + } + + /* ---------------------------------------------------------------------- */ + /* drop tolerance */ + /* ---------------------------------------------------------------------- */ + + droptol = GET_CONTROL (UMFPACK_DROPTOL, UMFPACK_DEFAULT_DROPTOL) ; + PRINTF ((" "ID": drop tolerance: %g\n", + (Int) INDEX (UMFPACK_DROPTOL), droptol)) ; + + /* ---------------------------------------------------------------------- */ + /* aggressive absorption */ + /* ---------------------------------------------------------------------- */ + + aggr = GET_CONTROL (UMFPACK_AGGRESSIVE, UMFPACK_DEFAULT_AGGRESSIVE) ; + PRINTF ((" "ID": AMD and COLAMD aggressive absorption: %g", + (Int) INDEX (UMFPACK_AGGRESSIVE), aggr)) ; + if (aggr != 0.0) + { + PRINTF ((" (yes)\n")) ; + } + else + { + PRINTF ((" (no)\n")) ; + } + + /* ---------------------------------------------------------------------- */ + /* compile-time options */ + /* ---------------------------------------------------------------------- */ + + PRINTF (( + "\n The following options can only be changed at compile-time:\n")) ; + + PRINTF ((" "ID": BLAS library used: ", + (Int) INDEX (UMFPACK_COMPILED_WITH_BLAS))) ; + +#if defined (USE_NO_BLAS) + PRINTF (("none. UMFPACK will be slow.\n")) ; +#elif defined (USE_C_BLAS) + PRINTF (("C-BLAS.\n")) ; +#elif defined (USE_MATLAB_BLAS) + PRINTF (("built-in MATLAB BLAS (ATLAS).\n")) ; +#elif defined (USE_SUNPERF_BLAS) + PRINTF (("Sun Performance Library BLAS.\n")) ; +#elif defined (USE_SCSL_BLAS) + PRINTF (("SGI SCSL BLAS.\n")) ; +#elif defined (USE_FORTRAN_BLAS) + PRINTF (("Fortran BLAS.\n")) ; +#endif + +#ifdef MATLAB_MEX_FILE +#ifdef NUTIL + PRINTF ((" "ID": compiled for MATLAB" + " (uses mxMalloc, mxFree, mxRealloc, and mexPrintf)\n", + (Int) INDEX (UMFPACK_COMPILED_FOR_MATLAB))) ; +#else + PRINTF ((" "ID": compiled for MATLAB" + " (uses utMalloc, utFree, utRealloc, and mexPrintf)\n", + (Int) INDEX (UMFPACK_COMPILED_FOR_MATLAB))) ; +#endif +#else +#ifdef MATHWORKS + PRINTF ((" "ID": compiled for MATLAB, using internal utility routines\n" + " (uses utMalloc, utFree, utRealloc, and utPrintf)\n", + (Int) INDEX (UMFPACK_COMPILED_FOR_MATLAB))) ; + PRINTF ((" (complex version uses utDivideComplex, utFdlibm_hypot)\n")) ; +#else + PRINTF ((" "ID": compiled for ANSI C" + " (uses malloc, free, realloc, and printf)\n", + (Int) INDEX (UMFPACK_COMPILED_FOR_MATLAB))) ; +#endif +#endif + +#ifdef NO_TIMER + PRINTF ((" "ID": no CPU timer \n", + (Int) INDEX (UMFPACK_COMPILED_WITH_GETRUSAGE))) ; +#else +#ifndef NPOSIX + PRINTF ((" "ID": CPU timer is POSIX times ( ) routine.\n", + (Int) INDEX (UMFPACK_COMPILED_WITH_GETRUSAGE))) ; +#else +#ifdef GETRUSAGE + PRINTF ((" "ID": CPU timer is getrusage.\n", + (Int) INDEX (UMFPACK_COMPILED_WITH_GETRUSAGE))) ; +#else + PRINTF ((" "ID": CPU timer is ANSI C clock (may wrap around).\n", + (Int) INDEX (UMFPACK_COMPILED_WITH_GETRUSAGE))) ; +#endif +#endif +#endif + +#ifndef NDEBUG + PRINTF (( +"**** Debugging enabled (UMFPACK will be exceedingly slow!) *****************\n" +" "ID": compiled with debugging enabled. ", + (Int) INDEX (UMFPACK_COMPILED_IN_DEBUG_MODE))) ; +#ifdef MATLAB_MEX_FILE + PRINTF (("Uses mxAssert.\n")) ; +#else +#ifdef MATHWORKS + PRINTF (("Uses utAssert.\n")) ; +#else + PRINTF (("Uses ANSI C assert.\n")) ; +#endif +#endif +#else + PRINTF ((" "ID": compiled for normal operation (debugging disabled)\n", + (Int) INDEX (UMFPACK_COMPILED_IN_DEBUG_MODE))) ; +#endif + + PRINTF ((" computer/operating system: %s\n", UMFPACK_ARCHITECTURE)) ; + PRINTF ((" size of int: %g long: %g Int: %g pointer: %g" + " double: %g Entry: %g (in bytes)\n\n", (double) sizeof (int), + (double) sizeof (long), (double) sizeof (Int), + (double) sizeof (void *), (double) sizeof (double), + (double) sizeof (Entry))) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_info.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_info.c new file mode 100644 index 0000000000..4c806d2f90 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_info.c @@ -0,0 +1,616 @@ +/* ========================================================================== */ +/* === UMFPACK_report_info ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints the Info array. See umfpack_report_info.h for + details. +*/ + +#include "umf_internal.h" + +#define PRINT_INFO(format,x) \ +{ \ + if (SCALAR_IS_NAN (x) || (!SCALAR_IS_LTZERO (x))) \ + { \ + PRINTF ((format, x)) ; \ + } \ +} + +/* RATIO macro uses a double relop, but ignore NaN case: */ +#define RATIO(a,b,c) (((b) == 0) ? (c) : (((double) a)/((double) b))) + +/* ========================================================================== */ +/* === print_ratio ========================================================== */ +/* ========================================================================== */ + +PRIVATE void print_ratio +( + char *what, + char *format, + double estimate, + double actual +) +{ + if (estimate < 0 && actual < 0) /* double relop, but ignore Nan case */ + { + return ; + } + PRINTF ((" %-27s", what)) ; + if (estimate >= 0) /* double relop, but ignore Nan case */ + { + PRINTF ((format, estimate)) ; + } + else + { + PRINTF ((" -")) ; + } + if (actual >= 0) /* double relop, but ignore Nan case */ + { + PRINTF ((format, actual)) ; + } + else + { + PRINTF ((" -")) ; + } + if (estimate >= 0 && actual >= 0) /* double relop, but ignore Nan case */ + { + PRINTF ((" %5.0f%%\n", 100 * RATIO (actual, estimate, 1))) ; + } + else + { + PRINTF ((" -\n")) ; + } +} + +/* ========================================================================== */ +/* === UMFPACK_report_info ================================================== */ +/* ========================================================================== */ + +GLOBAL void UMFPACK_report_info +( + const double Control [UMFPACK_CONTROL], + const double Info [UMFPACK_INFO] +) +{ + + double lnz_est, unz_est, lunz_est, lnz, unz, lunz, tsym, tnum, fnum, tsolve, + fsolve, ttot, ftot, twsym, twnum, twsolve, twtot, n2 ; + Int n_row, n_col, n_inner, prl, is_sym ; + + /* ---------------------------------------------------------------------- */ + /* get control settings and status to determine what to print */ + /* ---------------------------------------------------------------------- */ + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (!Info || prl < 2) + { + /* no output generated if Info is (double *) NULL */ + /* or if prl is less than 2 */ + return ; + } + + /* ---------------------------------------------------------------------- */ + /* print umfpack version */ + /* ---------------------------------------------------------------------- */ + + PRINTF (("\n%s, Info:\n", UMFPACK_VERSION)) ; + +#ifndef NDEBUG + PRINTF (( +"**** Debugging enabled (UMFPACK will be exceedingly slow!) *****************\n" + )) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* print run-time options */ + /* ---------------------------------------------------------------------- */ + +#ifdef DINT + PRINTF ((" matrix entry defined as: double\n")) ; + PRINTF ((" Int (generic integer) defined as: int\n")) ; +#endif +#ifdef DLONG + PRINTF ((" matrix entry defined as: double\n")) ; + PRINTF ((" Int (generic integer) defined as: long\n")) ; +#endif +#ifdef ZINT + PRINTF ((" matrix entry defined as: double complex\n")) ; + PRINTF ((" Int (generic integer) defined as: int\n")) ; +#endif +#ifdef ZLONG + PRINTF ((" matrix entry defined as: double complex\n")) ; + PRINTF ((" Int (generic integer) defined as: long\n")) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* print compile-time options */ + /* ---------------------------------------------------------------------- */ + + PRINTF ((" BLAS library used: ")) ; + +#if defined (USE_NO_BLAS) + PRINTF (("none. UMFPACK will be slow.\n")) ; +#elif defined (USE_C_BLAS) + PRINTF (("C-BLAS.\n")) ; +#elif defined (USE_MATLAB_BLAS) + PRINTF (("built-in MATLAB BLAS.\n")) ; +#elif defined (USE_SUNPERF_BLAS) + PRINTF (("Sun Performance Library BLAS.\n")) ; +#elif defined (USE_SCSL_BLAS) + PRINTF (("SGI SCSL BLAS.\n")) ; +#elif defined (USE_FORTRAN_BLAS) + PRINTF (("Fortran BLAS.\n")) ; +#endif + + PRINTF ((" MATLAB: ")) ; +#ifdef MATLAB_MEX_FILE + PRINTF (("yes.\n")) ; +#else +#ifdef MATHWORKS + PRINTF (("yes (using internal ut* routines).\n")) ; +#else + PRINTF (("no.\n")) ; +#endif +#endif + + PRINTF ((" CPU timer: ")) ; +#ifdef NO_TIMER + PRINTF (("none.\n")) ; +#else +#ifndef NPOSIX + PRINTF (("POSIX times ( ) routine.\n")) ; +#else +#ifdef GETRUSAGE + PRINTF (("getrusage ( ) routine.\n")) ; +#else + PRINTF (("ANSI clock ( ) routine.\n")) ; +#endif +#endif +#endif + + /* ---------------------------------------------------------------------- */ + /* print n and nz */ + /* ---------------------------------------------------------------------- */ + + n_row = (Int) Info [UMFPACK_NROW] ; + n_col = (Int) Info [UMFPACK_NCOL] ; + n_inner = MIN (n_row, n_col) ; + + PRINT_INFO (" number of rows in matrix A: "ID"\n", n_row) ; + PRINT_INFO (" number of columns in matrix A: "ID"\n", n_col) ; + PRINT_INFO (" entries in matrix A: "ID"\n", + (Int) Info [UMFPACK_NZ]) ; + PRINT_INFO (" memory usage reported in: "ID"-byte Units\n", + (Int) Info [UMFPACK_SIZE_OF_UNIT]) ; + + PRINT_INFO (" size of int: "ID" bytes\n", + (Int) Info [UMFPACK_SIZE_OF_INT]) ; + PRINT_INFO (" size of long: "ID" bytes\n", + (Int) Info [UMFPACK_SIZE_OF_LONG]) ; + PRINT_INFO (" size of pointer: "ID" bytes\n", + (Int) Info [UMFPACK_SIZE_OF_POINTER]) ; + PRINT_INFO (" size of numerical entry: "ID" bytes\n", + (Int) Info [UMFPACK_SIZE_OF_ENTRY]) ; + + /* ---------------------------------------------------------------------- */ + /* symbolic parameters */ + /* ---------------------------------------------------------------------- */ + + if (Info [UMFPACK_STRATEGY_USED] == UMFPACK_STRATEGY_SYMMETRIC) + { + PRINTF (("\n strategy used: symmetric\n")) ; + } + else if (Info [UMFPACK_STRATEGY_USED] == UMFPACK_STRATEGY_UNSYMMETRIC) + { + PRINTF (("\n strategy used: unsymmetric\n")) ; + } + else if (Info [UMFPACK_STRATEGY_USED] == UMFPACK_STRATEGY_2BY2) + { + PRINTF (("\n strategy used: symmetric 2-by-2\n")); + } + + if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_AMD) + { + PRINTF ((" ordering used: amd on A+A'\n")) ; + } + else if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_COLAMD) + { + PRINTF ((" ordering used: colamd on A\n")) ; + } + else if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_GIVEN) + { + PRINTF ((" ordering used: provided by user\n")) ; + } + + if (Info [UMFPACK_QFIXED] == 1) + { + PRINTF ((" modify Q during factorization: no\n")) ; + } + else if (Info [UMFPACK_QFIXED] == 0) + { + PRINTF ((" modify Q during factorization: yes\n")) ; + } + + if (Info [UMFPACK_DIAG_PREFERRED] == 0) + { + PRINTF ((" prefer diagonal pivoting: no\n")) ; + } + else if (Info [UMFPACK_DIAG_PREFERRED] == 1) + { + PRINTF ((" prefer diagonal pivoting: yes\n")) ; + } + + /* ---------------------------------------------------------------------- */ + /* singleton statistics */ + /* ---------------------------------------------------------------------- */ + + PRINT_INFO (" pivots with zero Markowitz cost: %0.f\n", + Info [UMFPACK_COL_SINGLETONS] + Info [UMFPACK_ROW_SINGLETONS]) ; + PRINT_INFO (" submatrix S after removing zero-cost pivots:\n" + " number of \"dense\" rows: %.0f\n", + Info [UMFPACK_NDENSE_ROW]) ; + PRINT_INFO (" number of \"dense\" columns: %.0f\n", + Info [UMFPACK_NDENSE_COL]) ; + PRINT_INFO (" number of empty rows: %.0f\n", + Info [UMFPACK_NEMPTY_ROW]) ; + PRINT_INFO (" number of empty columns %.0f\n", + Info [UMFPACK_NEMPTY_COL]) ; + is_sym = Info [UMFPACK_S_SYMMETRIC] ; + if (is_sym > 0) + { + PRINTF ((" submatrix S square and diagonal preserved\n")) ; + } + else if (is_sym == 0) + { + PRINTF ((" submatrix S not square or diagonal not preserved\n")); + } + + /* ---------------------------------------------------------------------- */ + /* statistics from amd_aat */ + /* ---------------------------------------------------------------------- */ + + n2 = Info [UMFPACK_N2] ; + if (n2 >= 0) + { + PRINTF ((" pattern of square submatrix S:\n")) ; + } + PRINT_INFO (" number rows and columns %.0f\n", + n2) ; + PRINT_INFO (" symmetry of nonzero pattern: %.6f\n", + Info [UMFPACK_PATTERN_SYMMETRY]) ; + PRINT_INFO (" nz in S+S' (excl. diagonal): %.0f\n", + Info [UMFPACK_NZ_A_PLUS_AT]) ; + PRINT_INFO (" nz on diagonal of matrix S: %.0f\n", + Info [UMFPACK_NZDIAG]) ; + if (Info [UMFPACK_NZDIAG] >= 0 && n2 > 0) + { + PRINTF ((" fraction of nz on diagonal: %.6f\n", + Info [UMFPACK_NZDIAG] / n2)) ; + } + + /* ---------------------------------------------------------------------- */ + /* statistics from 2-by-2 permutation */ + /* ---------------------------------------------------------------------- */ + + PRINT_INFO (" 2-by-2 pivoting to place large entries on diagonal:\n" + " # of small diagonal entries of S: %.0f\n", + Info [UMFPACK_2BY2_NWEAK]) ; + PRINT_INFO (" # unmatched: %.0f\n", + Info [UMFPACK_2BY2_UNMATCHED]) ; + PRINT_INFO (" symmetry of P2*S: %.6f\n", + Info [UMFPACK_2BY2_PATTERN_SYMMETRY]) ; + PRINT_INFO (" nz in P2*S+(P2*S)' (excl. diag.): %.0f\n", + Info [UMFPACK_2BY2_NZ_PA_PLUS_PAT]) ; + PRINT_INFO (" nz on diagonal of P2*S: %.0f\n", + Info [UMFPACK_2BY2_NZDIAG]) ; + if (Info [UMFPACK_2BY2_NZDIAG] >= 0 && n2 > 0) + { + PRINTF ((" fraction of nz on diag of P2*S: %.6f\n", + Info [UMFPACK_2BY2_NZDIAG] / n2)) ; + } + + /* ---------------------------------------------------------------------- */ + /* statistics from AMD */ + /* ---------------------------------------------------------------------- */ + + if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_AMD) + { + double dmax = Info [UMFPACK_SYMMETRIC_DMAX] ; + PRINTF ((" AMD statistics, for strict diagonal pivoting:\n")) ; + PRINT_INFO (" est. flops for LU factorization: %.5e\n", + Info [UMFPACK_SYMMETRIC_FLOPS]) ; + PRINT_INFO (" est. nz in L+U (incl. diagonal): %.0f\n", + Info [UMFPACK_SYMMETRIC_LUNZ]) ; + PRINT_INFO (" est. largest front (# entries): %.0f\n", + dmax*dmax) ; + PRINT_INFO (" est. max nz in any column of L: %.0f\n", + dmax) ; + PRINT_INFO ( + " number of \"dense\" rows/columns in S+S': %.0f\n", + Info [UMFPACK_SYMMETRIC_NDENSE]) ; + } + + /* ---------------------------------------------------------------------- */ + /* symbolic factorization */ + /* ---------------------------------------------------------------------- */ + + tsym = Info [UMFPACK_SYMBOLIC_TIME] ; + twsym = Info [UMFPACK_SYMBOLIC_WALLTIME] ; + + PRINT_INFO (" symbolic factorization defragmentations: %.0f\n", + Info [UMFPACK_SYMBOLIC_DEFRAG]) ; + PRINT_INFO (" symbolic memory usage (Units): %.0f\n", + Info [UMFPACK_SYMBOLIC_PEAK_MEMORY]) ; + PRINT_INFO (" symbolic memory usage (MBytes): %.1f\n", + MBYTES (Info [UMFPACK_SYMBOLIC_PEAK_MEMORY])) ; + PRINT_INFO (" Symbolic size (Units): %.0f\n", + Info [UMFPACK_SYMBOLIC_SIZE]) ; + PRINT_INFO (" Symbolic size (MBytes): %.0f\n", + MBYTES (Info [UMFPACK_SYMBOLIC_SIZE])) ; + PRINT_INFO (" symbolic factorization CPU time (sec): %.2f\n", + tsym) ; + PRINT_INFO (" symbolic factorization wallclock time(sec): %.2f\n", + twsym) ; + + /* ---------------------------------------------------------------------- */ + /* scaling, from numerical factorization */ + /* ---------------------------------------------------------------------- */ + + if (Info [UMFPACK_WAS_SCALED] == UMFPACK_SCALE_NONE) + { + PRINTF (("\n matrix scaled: no\n")) ; + } + else if (Info [UMFPACK_WAS_SCALED] == UMFPACK_SCALE_SUM) + { + PRINTF (("\n matrix scaled: yes ")) ; + PRINTF (("(divided each row by sum of abs values in each row)\n")) ; + PRINTF ((" minimum sum (abs (rows of A)): %.5e\n", + Info [UMFPACK_RSMIN])) ; + PRINTF ((" maximum sum (abs (rows of A)): %.5e\n", + Info [UMFPACK_RSMAX])) ; + } + else if (Info [UMFPACK_WAS_SCALED] == UMFPACK_SCALE_MAX) + { + PRINTF (("\n matrix scaled: yes ")) ; + PRINTF (("(divided each row by max abs value in each row)\n")) ; + PRINTF ((" minimum max (abs (rows of A)): %.5e\n", + Info [UMFPACK_RSMIN])) ; + PRINTF ((" maximum max (abs (rows of A)): %.5e\n", + Info [UMFPACK_RSMAX])) ; + } + + /* ---------------------------------------------------------------------- */ + /* estimate/actual in symbolic/numeric factorization */ + /* ---------------------------------------------------------------------- */ + + /* double relop, but ignore NaN case: */ + if (Info [UMFPACK_SYMBOLIC_DEFRAG] >= 0 /* UMFPACK_*symbolic called */ + || Info [UMFPACK_NUMERIC_DEFRAG] >= 0) /* UMFPACK_numeric called */ + { + PRINTF (("\n symbolic/numeric factorization: upper bound")) ; + PRINTF ((" actual %%\n")) ; + PRINTF ((" variable-sized part of Numeric object:\n")) ; + } + print_ratio (" initial size (Units)", " %20.0f", + Info [UMFPACK_VARIABLE_INIT_ESTIMATE], Info [UMFPACK_VARIABLE_INIT]) ; + print_ratio (" peak size (Units)", " %20.0f", + Info [UMFPACK_VARIABLE_PEAK_ESTIMATE], Info [UMFPACK_VARIABLE_PEAK]) ; + print_ratio (" final size (Units)", " %20.0f", + Info [UMFPACK_VARIABLE_FINAL_ESTIMATE], Info [UMFPACK_VARIABLE_FINAL]) ; + print_ratio ("Numeric final size (Units)", " %20.0f", + Info [UMFPACK_NUMERIC_SIZE_ESTIMATE], Info [UMFPACK_NUMERIC_SIZE]) ; + print_ratio ("Numeric final size (MBytes)", " %20.1f", + MBYTES (Info [UMFPACK_NUMERIC_SIZE_ESTIMATE]), + MBYTES (Info [UMFPACK_NUMERIC_SIZE])) ; + print_ratio ("peak memory usage (Units)", " %20.0f", + Info [UMFPACK_PEAK_MEMORY_ESTIMATE], Info [UMFPACK_PEAK_MEMORY]) ; + print_ratio ("peak memory usage (MBytes)", " %20.1f", + MBYTES (Info [UMFPACK_PEAK_MEMORY_ESTIMATE]), + MBYTES (Info [UMFPACK_PEAK_MEMORY])) ; + print_ratio ("numeric factorization flops", " %20.5e", + Info [UMFPACK_FLOPS_ESTIMATE], Info [UMFPACK_FLOPS]) ; + + lnz_est = Info [UMFPACK_LNZ_ESTIMATE] ; + unz_est = Info [UMFPACK_UNZ_ESTIMATE] ; + if (lnz_est >= 0 && unz_est >= 0) /* double relop, but ignore NaN case */ + { + lunz_est = lnz_est + unz_est - n_inner ; + } + else + { + lunz_est = EMPTY ; + } + lnz = Info [UMFPACK_LNZ] ; + unz = Info [UMFPACK_UNZ] ; + if (lnz >= 0 && unz >= 0) /* double relop, but ignore NaN case */ + { + lunz = lnz + unz - n_inner ; + } + else + { + lunz = EMPTY ; + } + print_ratio ("nz in L (incl diagonal)", " %20.0f", lnz_est, lnz) ; + print_ratio ("nz in U (incl diagonal)", " %20.0f", unz_est, unz) ; + print_ratio ("nz in L+U (incl diagonal)", " %20.0f", lunz_est, lunz) ; + + print_ratio ("largest front (# entries)", " %20.0f", + Info [UMFPACK_MAX_FRONT_SIZE_ESTIMATE], Info [UMFPACK_MAX_FRONT_SIZE]) ; + print_ratio ("largest # rows in front", " %20.0f", + Info [UMFPACK_MAX_FRONT_NROWS_ESTIMATE], + Info [UMFPACK_MAX_FRONT_NROWS]) ; + print_ratio ("largest # columns in front", " %20.0f", + Info [UMFPACK_MAX_FRONT_NCOLS_ESTIMATE], + Info [UMFPACK_MAX_FRONT_NCOLS]) ; + + /* ---------------------------------------------------------------------- */ + /* numeric factorization */ + /* ---------------------------------------------------------------------- */ + + tnum = Info [UMFPACK_NUMERIC_TIME] ; + twnum = Info [UMFPACK_NUMERIC_WALLTIME] ; + fnum = Info [UMFPACK_FLOPS] ; + + PRINT_INFO ("\n initial allocation ratio used: %0.3g\n", + Info [UMFPACK_ALLOC_INIT_USED]) ; + PRINT_INFO (" # of forced updates due to frontal growth: %.0f\n", + Info [UMFPACK_FORCED_UPDATES]) ; + PRINT_INFO (" number of off-diagonal pivots: %.0f\n", + Info [UMFPACK_NOFF_DIAG]) ; + PRINT_INFO (" nz in L (incl diagonal), if none dropped %.0f\n", + Info [UMFPACK_ALL_LNZ]) ; + PRINT_INFO (" nz in U (incl diagonal), if none dropped %.0f\n", + Info [UMFPACK_ALL_UNZ]) ; + PRINT_INFO (" number of small entries dropped %.0f\n", + Info [UMFPACK_NZDROPPED]) ; + PRINT_INFO (" nonzeros on diagonal of U: %.0f\n", + Info [UMFPACK_UDIAG_NZ]) ; + PRINT_INFO (" min abs. value on diagonal of U: %.2e\n", + Info [UMFPACK_UMIN]) ; + PRINT_INFO (" max abs. value on diagonal of U: %.2e\n", + Info [UMFPACK_UMAX]) ; + PRINT_INFO (" estimate of reciprocal of condition number: %.2e\n", + Info [UMFPACK_RCOND]) ; + PRINT_INFO (" indices in compressed pattern: %.0f\n", + Info [UMFPACK_COMPRESSED_PATTERN]) ; + PRINT_INFO (" numerical values stored in Numeric object: %.0f\n", + Info [UMFPACK_LU_ENTRIES]) ; + PRINT_INFO (" numeric factorization defragmentations: %.0f\n", + Info [UMFPACK_NUMERIC_DEFRAG]) ; + PRINT_INFO (" numeric factorization reallocations: %.0f\n", + Info [UMFPACK_NUMERIC_REALLOC]) ; + PRINT_INFO (" costly numeric factorization reallocations: %.0f\n", + Info [UMFPACK_NUMERIC_COSTLY_REALLOC]) ; + PRINT_INFO (" numeric factorization CPU time (sec): %.2f\n", + tnum) ; + PRINT_INFO (" numeric factorization wallclock time (sec): %.2f\n", + twnum) ; + + if (tnum > 0 && fnum > 0) + { + PRINT_INFO ( + " numeric factorization mflops (CPU time): %.2f\n", + 1e-6 * fnum / tnum) ; + } + if (twnum > 0 && fnum > 0) + { + PRINT_INFO ( + " numeric factorization mflops (wallclock): %.2f\n", + 1e-6 * fnum / twnum) ; + } + + ttot = EMPTY ; + ftot = fnum ; + if (tsym >= 0 && tnum >= 0) + { + ttot = tsym + tnum ; + PRINT_INFO (" symbolic + numeric CPU time (sec): %.2f\n", + ttot) ; + if (ftot > 0 && ttot > 0) + { + PRINT_INFO ( + " symbolic + numeric mflops (CPU time): %.2f\n", + 1e-6 * ftot / ttot) ; + } + } + + twtot = EMPTY ; + if (twsym >= 0 && twnum >= 0) + { + twtot = twsym + twnum ; + PRINT_INFO (" symbolic + numeric wall clock time (sec): %.2f\n", + twtot) ; + if (ftot > 0 && twtot > 0) + { + PRINT_INFO ( + " symbolic + numeric mflops (wall clock): %.2f\n", + 1e-6 * ftot / twtot) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* solve */ + /* ---------------------------------------------------------------------- */ + + tsolve = Info [UMFPACK_SOLVE_TIME] ; + twsolve = Info [UMFPACK_SOLVE_WALLTIME] ; + fsolve = Info [UMFPACK_SOLVE_FLOPS] ; + + PRINT_INFO ("\n solve flops: %.5e\n", + fsolve) ; + PRINT_INFO (" iterative refinement steps taken: %.0f\n", + Info [UMFPACK_IR_TAKEN]) ; + PRINT_INFO (" iterative refinement steps attempted: %.0f\n", + Info [UMFPACK_IR_ATTEMPTED]) ; + PRINT_INFO (" sparse backward error omega1: %.2e\n", + Info [UMFPACK_OMEGA1]) ; + PRINT_INFO (" sparse backward error omega2: %.2e\n", + Info [UMFPACK_OMEGA2]) ; + PRINT_INFO (" solve CPU time (sec): %.2f\n", + tsolve) ; + PRINT_INFO (" solve wall clock time (sec): %.2f\n", + twsolve) ; + if (fsolve > 0 && tsolve > 0) + { + PRINT_INFO ( + " solve mflops (CPU time): %.2f\n", + 1e-6 * fsolve / tsolve) ; + } + if (fsolve > 0 && twsolve > 0) + { + PRINT_INFO ( + " solve mflops (wall clock time): %.2f\n", + 1e-6 * fsolve / twsolve) ; + } + + if (ftot >= 0 && fsolve >= 0) + { + ftot += fsolve ; + PRINT_INFO ( + "\n total symbolic + numeric + solve flops: %.5e\n", ftot) ; + } + + if (tsolve >= 0) + { + if (ttot >= 0 && ftot >= 0) + { + ttot += tsolve ; + PRINT_INFO ( + " total symbolic + numeric + solve CPU time: %.2f\n", + ttot) ; + if (ftot > 0 && ttot > 0) + { + PRINT_INFO ( + " total symbolic + numeric + solve mflops (CPU): %.2f\n", + 1e-6 * ftot / ttot) ; + } + } + } + + if (twsolve >= 0) + { + if (twtot >= 0 && ftot >= 0) + { + twtot += tsolve ; + PRINT_INFO ( + " total symbolic+numeric+solve wall clock time: %.2f\n", + twtot) ; + if (ftot > 0 && twtot > 0) + { + PRINT_INFO ( + " total symbolic+numeric+solve mflops(wallclock) %.2f\n", + 1e-6 * ftot / twtot) ; + } + } + } + PRINTF (("\n")) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_matrix.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_matrix.c new file mode 100644 index 0000000000..d5066a4a91 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_matrix.c @@ -0,0 +1,205 @@ +/* ========================================================================== */ +/* === UMFPACK_report_matrix ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints a column or row-oriented matrix. See + umfpack_report_matrix.h for details. +*/ + +#include "umf_internal.h" +#include "umf_malloc.h" +#include "umf_free.h" + +GLOBAL Int UMFPACK_report_matrix +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + Int col_form, /* 1: column form, 0: row form */ + const double Control [UMFPACK_CONTROL] +) +{ + Int prl, i, k, length, ilast, p, nz, prl1, p1, p2, n, n_i, do_values ; + char *vector, *index ; + Entry a ; + + /* ---------------------------------------------------------------------- */ + /* determine the form, and check if inputs exist */ + /* ---------------------------------------------------------------------- */ + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (prl <= 2) + { + return (UMFPACK_OK) ; + } + + if (col_form) + { + vector = "column" ; /* column vectors */ + index = "row" ; /* with row indices */ + n = n_col ; + n_i = n_row ; + } + else + { + vector = "row" ; /* row vectors */ + index = "column" ; /* with column indices */ + n = n_row ; + n_i = n_col ; + } + + PRINTF (("%s-form matrix, n_row "ID" n_col "ID", ", vector, n_row, n_col)) ; + + if (n_row <= 0 || n_col <= 0) + { + PRINTF (("ERROR: n_row <= 0 or n_col <= 0\n\n")) ; + return (UMFPACK_ERROR_n_nonpositive) ; + } + + if (!Ap) + { + PRINTF (("ERROR: Ap missing\n\n")) ; + return (UMFPACK_ERROR_argument_missing) ; + } + + nz = Ap [n] ; + PRINTF (("nz = "ID". ", nz)) ; + if (nz < 0) + { + PRINTF (("ERROR: number of entries < 0\n\n")) ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + + if (Ap [0] != 0) + { + PRINTF (("ERROR: Ap ["ID"] = "ID" must be "ID"\n\n", + (Int) INDEX (0), INDEX (Ap [0]), (Int) INDEX (0))) ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + + if (!Ai) + { + PRINTF (("ERROR: Ai missing\n\n")) ; + return (UMFPACK_ERROR_argument_missing) ; + } + +#ifdef COMPLEX + do_values = Ax && Az ; +#else + do_values = Ax != (double *) NULL ; +#endif + + PRINTF4 (("\n")) ; + + /* ---------------------------------------------------------------------- */ + /* check the row/column pointers, Ap */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < n ; k++) + { + if (Ap [k] < 0) + { + PRINTF (("ERROR: Ap ["ID"] < 0\n\n", INDEX (k))) ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + if (Ap [k] > nz) + { + PRINTF (("ERROR: Ap ["ID"] > size of Ai\n\n", INDEX (k))) ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + } + + for (k = 0 ; k < n ; k++) + { + length = Ap [k+1] - Ap [k] ; + if (length < 0) + { + PRINTF (("ERROR: # entries in %s "ID" is < 0\n\n", + vector, INDEX (k))) ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* print each vector */ + /* ---------------------------------------------------------------------- */ + + prl1 = prl ; + + for (k = 0 ; k < n ; k++) + { + /* if prl is 4, print the first 10 entries of the first 10 vectors */ + if (k < 10) + { + prl = prl1 ; + } + /* get the vector pointers */ + p1 = Ap [k] ; + p2 = Ap [k+1] ; + length = p2 - p1 ; + PRINTF4 (("\n %s "ID": start: "ID" end: "ID" entries: "ID"\n", + vector, INDEX (k), p1, p2-1, length)) ; + ilast = EMPTY ; + for (p = p1 ; p < p2 ; p++) + { + i = Ai [p] ; + PRINTF4 (("\t%s "ID" ", index, INDEX (i))) ; + if (do_values && prl >= 4) + { + PRINTF ((":")) ; + ASSIGN (a, Ax [p], Az [p]) ; + PRINT_ENTRY (a) ; + } + if (i < 0 || i >= n_i) + { + PRINTF ((" ERROR: %s index "ID" out of range in %s "ID"\n\n", + index, INDEX (i), vector, INDEX (k))) ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + if (i <= ilast) + { + PRINTF ((" ERROR: %s index "ID" out of order (or duplicate) in " + "%s "ID"\n\n", index, INDEX (i), vector, INDEX (k))) ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + PRINTF4 (("\n")) ; + /* truncate printout, but continue to check matrix */ + if (prl == 4 && (p - p1) == 9 && length > 10) + { + PRINTF4 (("\t...\n")) ; + prl-- ; + } + ilast = i ; + } + /* truncate printout, but continue to check matrix */ + if (prl == 4 && k == 9 && n > 10) + { + PRINTF4 (("\n ...\n")) ; + prl-- ; + } + } + prl = prl1 ; + + /* ---------------------------------------------------------------------- */ + /* return the status of the matrix */ + /* ---------------------------------------------------------------------- */ + + PRINTF4 ((" %s-form matrix ", vector)) ; + PRINTF (("OK\n\n")) ; + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_numeric.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_numeric.c new file mode 100644 index 0000000000..c66a2c6c89 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_numeric.c @@ -0,0 +1,664 @@ +/* ========================================================================== */ +/* === UMFPACK_report_numeric =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints the Numeric object. + See umfpack_report_numeric.h for details. + + Dynamic memory usage: Allocates a size n*sizeof(Int) workspace via a single + call to UMF_malloc and then frees all of it via UMF_free on return. The + workspace is not allocated if an early error return occurs before the + workspace is needed. +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" +#include "umf_report_perm.h" +#include "umf_report_vector.h" +#include "umf_malloc.h" +#include "umf_free.h" + + +PRIVATE Int report_L +( + NumericType *Numeric, + Int Pattern [ ], + Int prl +) ; + + +PRIVATE Int report_U +( + NumericType *Numeric, + Int Pattern [ ], + Int prl +) ; + +/* ========================================================================== */ +/* === UMFPACK_report_numeric =============================================== */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_report_numeric +( + void *NumericHandle, + const double Control [UMFPACK_CONTROL] +) +{ + Int prl, *W, nn, n_row, n_col, n_inner, num_fixed_size, numeric_size, + npiv ; + NumericType *Numeric ; + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (prl <= 2) + { + return (UMFPACK_OK) ; + } + + PRINTF (("Numeric object: ")) ; + + Numeric = (NumericType *) NumericHandle ; + if (!UMF_valid_numeric (Numeric)) + { + PRINTF (("ERROR: LU factors invalid\n\n")) ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + n_row = Numeric->n_row ; + n_col = Numeric->n_col ; + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + npiv = Numeric->npiv ; + + DEBUG1 (("n_row "ID" n_col "ID" nn "ID" n_inner "ID" npiv "ID"\n", + n_row, n_col, nn, n_inner, npiv)) ; + + /* size of Numeric object, except Numeric->Memory and Numeric->Upattern */ + /* see also UMF_set_stats */ + num_fixed_size = + UNITS (NumericType, 1) /* Numeric structure */ + + UNITS (Entry, n_inner+1) /* D */ + + UNITS (Int, n_row+1) /* Rperm */ + + UNITS (Int, n_col+1) /* Cperm */ + + 6 * UNITS (Int, npiv+1) /* Lpos, Uilen, Uip, Upos, Lilen, Lip */ + + ((Numeric->scale != UMFPACK_SCALE_NONE) ? + UNITS (Entry, n_row) : 0) ; /* Rs */ + + DEBUG1 (("num fixed size: "ID"\n", num_fixed_size)) ; + DEBUG1 (("Numeric->size "ID"\n", Numeric->size)) ; + DEBUG1 (("ulen units "ID"\n", UNITS (Int, Numeric->ulen))) ; + + /* size of Numeric->Memory is Numeric->size */ + /* size of Numeric->Upattern is Numeric->ulen */ + numeric_size = num_fixed_size + Numeric->size + + UNITS (Int, Numeric->ulen) ; + + DEBUG1 (("numeric total size "ID"\n", numeric_size)) ; + + if (prl >= 4) + { + PRINTF (("\n n_row: "ID" n_col: "ID"\n", n_row, n_col)) ; + + PRINTF ((" relative pivot tolerance used: %g\n", + Numeric->relpt)) ; + PRINTF ((" relative symmetric pivot tolerance used: %g\n", + Numeric->relpt2)) ; + + PRINTF ((" matrix scaled: ")) ; + if (Numeric->scale == UMFPACK_SCALE_NONE) + { + PRINTF (("no")) ; + } + else if (Numeric->scale == UMFPACK_SCALE_SUM) + { + PRINTF (("yes (divided each row by sum abs value in each row)\n")) ; + PRINTF ((" minimum sum (abs (rows of A)): %.5e\n", + Numeric->rsmin)) ; + PRINTF ((" maximum sum (abs (rows of A)): %.5e", + Numeric->rsmax)) ; + } + else if (Numeric->scale == UMFPACK_SCALE_MAX) + { + PRINTF (("yes (divided each row by max abs value in each row)\n")) ; + PRINTF ((" minimum max (abs (rows of A)): %.5e\n", + Numeric->rsmin)) ; + PRINTF ((" maximum max (abs (rows of A)): %.5e", + Numeric->rsmax)) ; + } + PRINTF (("\n")) ; + + PRINTF ((" initial allocation parameter used: %g\n", + Numeric->alloc_init)) ; + PRINTF ((" frontal matrix allocation parameter used: %g\n", + Numeric->front_alloc_init)) ; + PRINTF ((" final total size of Numeric object (Units): "ID"\n", + numeric_size)) ; + PRINTF ((" final total size of Numeric object (MBytes): %.1f\n", + MBYTES (numeric_size))) ; + PRINTF ((" peak size of variable-size part (Units): "ID"\n", + Numeric->max_usage)) ; + PRINTF ((" peak size of variable-size part (MBytes): %.1f\n", + MBYTES (Numeric->max_usage))) ; + PRINTF ((" largest actual frontal matrix size: "ID"\n", + Numeric->maxfrsize)) ; + PRINTF ((" memory defragmentations: "ID"\n", + Numeric->ngarbage)) ; + PRINTF ((" memory reallocations: "ID"\n", + Numeric->nrealloc)) ; + PRINTF ((" costly memory reallocations: "ID"\n", + Numeric->ncostly)) ; + PRINTF ((" entries in compressed pattern (L and U): "ID"\n", + Numeric->isize)) ; + PRINTF ((" number of nonzeros in L (excl diag): "ID"\n", + Numeric->lnz)) ; + PRINTF ((" number of entries stored in L (excl diag): "ID"\n", + Numeric->nLentries)) ; + PRINTF ((" number of nonzeros in U (excl diag): "ID"\n", + Numeric->unz)) ; + PRINTF ((" number of entries stored in U (excl diag): "ID"\n", + Numeric->nUentries)) ; + PRINTF ((" factorization floating-point operations: %g\n", + Numeric->flops)) ; + PRINTF ((" number of nonzeros on diagonal of U: "ID"\n", + Numeric->nnzpiv)) ; + PRINTF ((" min abs. value on diagonal of U: %.5e\n", + Numeric->min_udiag)) ; + PRINTF ((" max abs. value on diagonal of U: %.5e\n", + Numeric->max_udiag)) ; + PRINTF ((" reciprocal condition number estimate: %.2e\n", + Numeric->rcond)) ; + } + + W = (Int *) UMF_malloc (nn, sizeof (Int)) ; + if (!W) + { + PRINTF ((" ERROR: out of memory to check Numeric object\n\n")) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + if (Numeric->Rs) + { +#ifndef NRECIPROCAL + if (Numeric->do_recip) + { + PRINTF4 (("\nScale factors applied via multiplication\n")) ; + } + else +#endif + { + PRINTF4 (("\nScale factors applied via division\n")) ; + } + PRINTF4 (("Scale factors, Rs: ")) ; + (void) UMF_report_vector (n_row, Numeric->Rs, (double *) NULL, + prl, FALSE, TRUE) ; + } + else + { + PRINTF4 (("Scale factors, Rs: (not present)\n")) ; + } + + PRINTF4 (("\nP: row ")) ; + if (UMF_report_perm (n_row, Numeric->Rperm, W, prl, 0) != UMFPACK_OK) + { + (void) UMF_free ((void *) W) ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + PRINTF4 (("\nQ: column ")) ; + if (UMF_report_perm (n_col, Numeric->Cperm, W, prl, 0) != UMFPACK_OK) + { + (void) UMF_free ((void *) W) ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + if (!report_L (Numeric, W, prl)) + { + (void) UMF_free ((void *) W) ; + PRINTF ((" ERROR: L factor invalid\n\n")) ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + if (!report_U (Numeric, W, prl)) + { + (void) UMF_free ((void *) W) ; + PRINTF ((" ERROR: U factor invalid\n\n")) ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + /* The diagonal of U is in "merged" (Entry) form, not "split" form. */ + PRINTF4 (("\ndiagonal of U: ")) ; + (void) UMF_report_vector (n_inner, (double *) Numeric->D, (double *) NULL, + prl, FALSE, FALSE) ; + + (void) UMF_free ((void *) W) ; + + PRINTF4 ((" Numeric object: ")) ; + PRINTF (("OK\n\n")) ; + return (UMFPACK_OK) ; +} + + +/* ========================================================================== */ +/* === report_L ============================================================= */ +/* ========================================================================== */ + +PRIVATE Int report_L +( + NumericType *Numeric, + Int Pattern [ ], + Int prl +) +{ + Int k, deg, *ip, j, row, n_row, *Lpos, *Lilen, valid, k1, + *Lip, newLchain, llen, prl1, pos, lp, p, npiv, n1, *Li ; + Entry *xp, *Lval ; + + /* ---------------------------------------------------------------------- */ + + ASSERT (prl >= 3) ; + + n_row = Numeric->n_row ; + npiv = Numeric->npiv ; + n1 = Numeric->n1 ; + Lpos = Numeric->Lpos ; + Lilen = Numeric->Lilen ; + Lip = Numeric->Lip ; + prl1 = prl ; + deg = 0 ; + + PRINTF4 (( + "\nL in Numeric object, in column-oriented compressed-pattern form:\n" + " Diagonal entries are all equal to 1.0 (not stored)\n")) ; + + ASSERT (Pattern != (Int *) NULL) ; + + /* ---------------------------------------------------------------------- */ + /* print L */ + /* ---------------------------------------------------------------------- */ + + k1 = 12 ; + + /* ---------------------------------------------------------------------- */ + /* print the singleton columns of L */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < n1 ; k++) + { + if (k1 > 0) + { + prl = prl1 ; + } + lp = Lip [k] ; + deg = Lilen [k] ; + Li = (Int *) (Numeric->Memory + lp) ; + lp += UNITS (Int, deg) ; + Lval = (Entry *) (Numeric->Memory + lp) ; + if (k1-- > 0) + { + prl = prl1 ; + } + else if (prl == 4) + { + PRINTF ((" ...\n")) ; + prl-- ; + } + PRINTF4 (("\n column "ID":", INDEX (k))) ; + PRINTF4 ((" length "ID".\n", deg)) ; + for (j = 0 ; j < deg ; j++) + { + row = Li [j] ; + PRINTF4 (("\trow "ID" : ", INDEX (row))) ; + if (prl >= 4) PRINT_ENTRY (Lval [j]) ; + if (row <= k || row >= n_row) + { + return (FALSE) ; + } + PRINTF4 (("\n")) ; + /* truncate printout, but continue to check L */ + if (prl == 4 && j == 9 && deg > 10) + { + PRINTF (("\t...\n")) ; + prl-- ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* print the regular columns of L */ + /* ---------------------------------------------------------------------- */ + + for (k = n1 ; k < npiv ; k++) + { + /* if prl is 4, print the first 10 entries of the first 10 columns */ + if (k1 > 0) + { + prl = prl1 ; + } + + lp = Lip [k] ; + newLchain = (lp < 0) ; + if (newLchain) + { + lp = -lp ; + deg = 0 ; + } + + if (k1-- > 0) + { + prl = prl1 ; + } + else if (prl == 4) + { + PRINTF ((" ...\n")) ; + prl-- ; + } + + PRINTF4 (("\n column "ID":", INDEX (k))) ; + + /* ------------------------------------------------------------------ */ + /* make column of L in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + /* remove pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + PRINTF4 ((" remove row "ID" at position "ID".", + INDEX (Pattern [pos]), INDEX (pos))) ; + valid = (!newLchain) && (deg > 0) && (pos < deg) && (pos >= 0) + && (Pattern [pos] == k) ; + if (!valid) + { + return (FALSE) ; + } + Pattern [pos] = Pattern [--deg] ; + } + + /* concatenate the pattern */ + llen = Lilen [k] ; + if (llen < 0) + { + return (FALSE) ; + } + p = lp + UNITS (Int, llen) ; + xp = (Entry *) (Numeric->Memory + p) ; + if ((llen > 0 || deg > 0) + && (p + (Int) UNITS (Entry, deg) > Numeric->size)) + { + return (FALSE) ; + } + if (llen > 0) + { + PRINTF4 ((" add "ID" entries.", llen)) ; + ip = (Int *) (Numeric->Memory + lp) ; + for (j = 0 ; j < llen ; j++) + { + Pattern [deg++] = *ip++ ; + } + } + + /* ------------------------------------------------------------------ */ + /* print column k of L */ + /* ------------------------------------------------------------------ */ + + PRINTF4 ((" length "ID".", deg)) ; + if (newLchain) + { + PRINTF4 ((" Start of Lchain.")) ; + } + PRINTF4 (("\n")) ; + + for (j = 0 ; j < deg ; j++) + { + row = Pattern [j] ; + PRINTF4 (("\trow "ID" : ", INDEX (row))) ; + if (prl >= 4) PRINT_ENTRY (*xp) ; + if (row <= k || row >= n_row) + { + return (FALSE) ; + } + PRINTF4 (("\n")) ; + xp++ ; + /* truncate printout, but continue to check L */ + if (prl == 4 && j == 9 && deg > 10) + { + PRINTF (("\t...\n")) ; + prl-- ; + } + } + } + + PRINTF4 (("\n")) ; + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === report_U ============================================================= */ +/* ========================================================================== */ + +PRIVATE Int report_U +( + NumericType *Numeric, + Int Pattern [ ], + Int prl +) +{ + /* ---------------------------------------------------------------------- */ + + Int k, deg, j, *ip, col, *Upos, *Uilen, k1, prl1, pos, + *Uip, n_col, ulen, p, newUchain, up, npiv, n1, *Ui ; + Entry *xp, *Uval ; + + /* ---------------------------------------------------------------------- */ + + ASSERT (prl >= 3) ; + + n_col = Numeric->n_col ; + npiv = Numeric->npiv ; + n1 = Numeric->n1 ; + Upos = Numeric->Upos ; + Uilen = Numeric->Uilen ; + Uip = Numeric->Uip ; + prl1 = prl ; + + PRINTF4 (( + "\nU in Numeric object, in row-oriented compressed-pattern form:\n" + " Diagonal is stored separately.\n")) ; + + ASSERT (Pattern != (Int *) NULL) ; + + k1 = 12 ; + + /* ---------------------------------------------------------------------- */ + /* print the sparse part of U */ + /* ---------------------------------------------------------------------- */ + + deg = Numeric->ulen ; + if (deg > 0) + { + /* make last pivot row of U (singular matrices only) */ + for (j = 0 ; j < deg ; j++) + { + Pattern [j] = Numeric->Upattern [j] ; + } + } + + PRINTF4 (("\n row "ID": length "ID". End of Uchain.\n", INDEX (npiv-1), + deg)) ; + + for (k = npiv-1 ; k >= n1 ; k--) + { + + /* ------------------------------------------------------------------ */ + /* print row k of U */ + /* ------------------------------------------------------------------ */ + + /* if prl is 3, print the first 10 entries of the first 10 columns */ + if (k1 > 0) + { + prl = prl1 ; + } + + up = Uip [k] ; + ulen = Uilen [k] ; + if (ulen < 0) + { + return (FALSE) ; + } + newUchain = (up < 0) ; + if (newUchain) + { + up = -up ; + p = up + UNITS (Int, ulen) ; + } + else + { + p = up ; + } + xp = (Entry *) (Numeric->Memory + p) ; + if (deg > 0 && (p + (Int) UNITS (Entry, deg) > Numeric->size)) + { + return (FALSE) ; + } + for (j = 0 ; j < deg ; j++) + { + col = Pattern [j] ; + PRINTF4 (("\tcol "ID" :", INDEX (col))) ; + if (prl >= 4) PRINT_ENTRY (*xp) ; + if (col <= k || col >= n_col) + { + return (FALSE) ; + } + PRINTF4 (("\n")) ; + xp++ ; + /* truncate printout, but continue to check U */ + if (prl == 4 && j == 9 && deg > 10) + { + PRINTF (("\t...\n")) ; + prl-- ; + } + } + + /* ------------------------------------------------------------------ */ + /* make row k-1 of U in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + if (k1-- > 0) + { + prl = prl1 ; + } + else if (prl == 4) + { + PRINTF ((" ...\n")) ; + prl-- ; + } + + if (k > 0) + { + PRINTF4 (("\n row "ID": ", INDEX (k-1))) ; + } + + if (newUchain) + { + /* next row is a new Uchain */ + if (k > 0) + { + deg = ulen ; + PRINTF4 (("length "ID". End of Uchain.\n", deg)) ; + if (up + (Int) UNITS (Int, ulen) > Numeric->size) + { + return (FALSE) ; + } + ip = (Int *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + Pattern [j] = *ip++ ; + } + } + } + else + { + if (ulen > 0) + { + PRINTF4 (("remove "ID" entries. ", ulen)) ; + } + deg -= ulen ; + if (deg < 0) + { + return (FALSE) ; + } + pos = Upos [k] ; + if (pos != EMPTY) + { + /* add the pivot column */ + PRINTF4 (("add column "ID" at position "ID". ", + INDEX (k), INDEX (pos))) ; + if (pos < 0 || pos > deg) + { + return (FALSE) ; + } + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + PRINTF4 (("length "ID".\n", deg)) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* print the singleton rows of U */ + /* ---------------------------------------------------------------------- */ + + for (k = n1 - 1 ; k >= 0 ; k--) + { + if (k1 > 0) + { + prl = prl1 ; + } + up = Uip [k] ; + deg = Uilen [k] ; + Ui = (Int *) (Numeric->Memory + up) ; + up += UNITS (Int, deg) ; + Uval = (Entry *) (Numeric->Memory + up) ; + if (k1-- > 0) + { + prl = prl1 ; + } + else if (prl == 4) + { + PRINTF ((" ...\n")) ; + prl-- ; + } + PRINTF4 (("\n row "ID":", INDEX (k))) ; + PRINTF4 ((" length "ID".\n", deg)) ; + for (j = 0 ; j < deg ; j++) + { + col = Ui [j] ; + PRINTF4 (("\tcol "ID" : ", INDEX (col))) ; + if (prl >= 4) PRINT_ENTRY (Uval [j]) ; + if (col <= k || col >= n_col) + { + return (FALSE) ; + } + PRINTF4 (("\n")) ; + /* truncate printout, but continue to check U */ + if (prl == 4 && j == 9 && deg > 10) + { + PRINTF (("\t...\n")) ; + prl-- ; + } + } + } + + prl = prl1 ; + PRINTF4 (("\n")) ; + return (TRUE) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_perm.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_perm.c new file mode 100644 index 0000000000..2dc4c13779 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_perm.c @@ -0,0 +1,45 @@ +/* ========================================================================== */ +/* === UMFPACK_report_perm ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints a permutation vector. See umfpack_report_perm.h + for details. + + Dynamic memory usage: Allocates a size max(np,1)*sizeof(Int) workspace via + a single call to UMF_malloc and then frees all of it via UMF_free on return. +*/ + +#include "umf_internal.h" +#include "umf_report_perm.h" +#include "umf_malloc.h" +#include "umf_free.h" + +GLOBAL Int UMFPACK_report_perm +( + Int np, + const Int Perm [ ], + const double Control [UMFPACK_CONTROL] +) +{ + Int prl, *W, status ; + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (prl <= 2) + { + return (UMFPACK_OK) ; + } + + W = (Int *) UMF_malloc (MAX (np,1), sizeof (Int)) ; + status = UMF_report_perm (np, Perm, W, prl, 1) ; + (void) UMF_free ((void *) W) ; + return (status) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_status.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_status.c new file mode 100644 index 0000000000..06bc9244c9 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_status.c @@ -0,0 +1,119 @@ +/* ========================================================================== */ +/* === UMFPACK_report_status ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints the return value from other UMFPACK_* routines. + See umfpack_report_status.h for details. +*/ + +#include "umf_internal.h" + +GLOBAL void UMFPACK_report_status +( + const double Control [UMFPACK_CONTROL], + Int status +) +{ + Int prl ; + + /* ---------------------------------------------------------------------- */ + /* get control settings and status to determine what to print */ + /* ---------------------------------------------------------------------- */ + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (prl < 1) + { + /* no output generated if prl is less than 1 */ + return ; + } + + if (status == UMFPACK_OK && prl <= 1) + { + /* no output generated if prl is 1 or less and no error occured. */ + /* note that the default printing level is 1. */ + return ; + } + + /* ---------------------------------------------------------------------- */ + /* print umfpack license, copyright, version, and status condition */ + /* ---------------------------------------------------------------------- */ + + PRINTF (("\n")) ; + PRINTF4 (("%s\n", UMFPACK_COPYRIGHT)) ; + PRINTF6 (("%s", UMFPACK_LICENSE_PART1)) ; + PRINTF6 (("%s", UMFPACK_LICENSE_PART2)) ; + PRINTF6 (("%s", UMFPACK_LICENSE_PART3)) ; + PRINTF (("%s: ", UMFPACK_VERSION)) ; + + switch (status) + { + case UMFPACK_OK: + PRINTF (("OK\n")) ; + break ; + + case UMFPACK_WARNING_singular_matrix: + PRINTF (("WARNING: matrix is singular\n")) ; + break ; + + case UMFPACK_ERROR_out_of_memory: + PRINTF (("ERROR: out of memory\n")) ; + break ; + + case UMFPACK_ERROR_invalid_Numeric_object: + PRINTF (("ERROR: Numeric object is invalid\n")) ; + break ; + + case UMFPACK_ERROR_invalid_Symbolic_object: + PRINTF (("ERROR: Symbolic object is invalid\n")) ; + break ; + + case UMFPACK_ERROR_argument_missing: + PRINTF (("ERROR: required argument(s) missing\n")) ; + break ; + + case UMFPACK_ERROR_n_nonpositive: + PRINTF (("ERROR: dimension (n_row or n_col) must be > 0\n")) ; + break ; + + case UMFPACK_ERROR_invalid_matrix: + PRINTF (("ERROR: input matrix is invalid\n")) ; + break ; + + case UMFPACK_ERROR_invalid_system: + PRINTF (("ERROR: system argument invalid\n")) ; + break ; + + case UMFPACK_ERROR_invalid_permutation: + PRINTF (("ERROR: invalid permutation\n")) ; + break ; + + case UMFPACK_ERROR_different_pattern: + PRINTF (("ERROR: pattern of matrix (Ap and/or Ai) has changed\n")) ; + break ; + + case UMFPACK_ERROR_internal_error: + PRINTF (("INTERNAL ERROR!\n" + "Input arguments might be corrupted or aliased, or an internal\n" + "error has occurred. Check your input arguments with the\n" + "umfpack_*_report_* routines before calling the umfpack_*\n" + "computational routines. Recompile UMFPACK with debugging\n" + "enabled, and look for failed assertions. If all else fails\n" + "please report this error to Tim Davis (davis@cise.ufl.edu).\n" + )) ; + break ; + + default: + PRINTF (("ERROR: Unrecognized error code: "ID"\n", status)) ; + + } + PRINTF (("\n")) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_symbolic.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_symbolic.c new file mode 100644 index 0000000000..14503d9280 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_symbolic.c @@ -0,0 +1,229 @@ +/* ========================================================================== */ +/* === UMFPACK_report_symbolic ============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints the Symbolic object. See umfpack_report_symbolic.h + for details. Does not print new Cdeg, Rdeg, Esize, and the Diagonal_map. + + Dynamic memory usage: Allocates a size MAX (n_row,n_col)*sizeof(Int) + workspace via a single call to UMF_malloc and then frees all of it via + UMF_free on return. The workspace is not allocated if an early error + return occurs before the workspace is needed. +*/ + +#include "umf_internal.h" +#include "umf_valid_symbolic.h" +#include "umf_report_perm.h" +#include "umf_malloc.h" +#include "umf_free.h" + +GLOBAL Int UMFPACK_report_symbolic +( + void *SymbolicHandle, + const double Control [UMFPACK_CONTROL] +) +{ + Int n_row, n_col, nz, nchains, nfr, maxnrows, maxncols, prl, + k, chain, frontid, frontid1, frontid2, kk, *Chain_start, *W, + *Chain_maxrows, *Chain_maxcols, *Front_npivcol, *Front_1strow, + *Front_leftmostdesc, *Front_parent, done, status1, status2 ; + SymbolicType *Symbolic ; + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (prl <= 2) + { + return (UMFPACK_OK) ; + } + + PRINTF (("Symbolic object: ")) ; + + Symbolic = (SymbolicType *) SymbolicHandle ; + if (!UMF_valid_symbolic (Symbolic)) + { + PRINTF (("ERROR: invalid\n")) ; + return (UMFPACK_ERROR_invalid_Symbolic_object) ; + } + + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + + nz = Symbolic->nz ; + + nchains = Symbolic->nchains ; + nfr = Symbolic->nfr ; + maxnrows = Symbolic->maxnrows ; + maxncols = Symbolic->maxncols ; + + Chain_start = Symbolic->Chain_start ; + Chain_maxrows = Symbolic->Chain_maxrows ; + Chain_maxcols = Symbolic->Chain_maxcols ; + Front_npivcol = Symbolic->Front_npivcol ; + Front_1strow = Symbolic->Front_1strow ; + Front_leftmostdesc = Symbolic->Front_leftmostdesc ; + Front_parent = Symbolic->Front_parent ; + + if (prl >= 4) + { + + PRINTF (("\n matrix to be factorized:\n")) ; + PRINTF (("\tn_row: "ID" n_col: "ID"\n", n_row, n_col)) ; + PRINTF (("\tnumber of entries: "ID"\n", nz)) ; + PRINTF ((" block size used for dense matrix kernels: "ID"\n", + Symbolic->nb)) ; + + PRINTF ((" strategy used: ")) ; + /* strategy cannot be auto */ + if (Symbolic->strategy == UMFPACK_STRATEGY_SYMMETRIC) + { + PRINTF (("symmetric")) ; + } + else if (Symbolic->strategy == UMFPACK_STRATEGY_UNSYMMETRIC) + { + PRINTF (("unsymmetric")) ; + } + else if (Symbolic->strategy == UMFPACK_STRATEGY_2BY2) + { + PRINTF (("symmetric 2-by-2")) ; + } + PRINTF (("\n")) ; + + PRINTF ((" ordering used: ")) ; + if (Symbolic->ordering == UMFPACK_ORDERING_COLAMD) + { + PRINTF (("colamd on A\n")) ; + } + else if (Symbolic->ordering == UMFPACK_ORDERING_AMD) + { + PRINTF (("amd on A+A'\n")) ; + } + else if (Symbolic->ordering == UMFPACK_ORDERING_GIVEN) + { + PRINTF (("provided by user")) ; + } + PRINTF (("\n")) ; + + PRINTF ((" performn column etree postorder: ")) ; + if (Symbolic->fixQ) + { + PRINTF (("no\n")) ; + } + else + { + PRINTF (("yes\n")) ; + } + + PRINTF ((" prefer diagonal pivoting (attempt P=Q): ")) ; + if (Symbolic->prefer_diagonal) + { + PRINTF (("yes\n")) ; + } + else + { + PRINTF (("no\n")) ; + } + + PRINTF ((" variable-size part of Numeric object:\n")) ; + PRINTF (("\tminimum initial size (Units): %.20g (MBytes): %.1f\n", + Symbolic->dnum_mem_init_usage, + MBYTES (Symbolic->dnum_mem_init_usage))) ; + PRINTF (("\testimated peak size (Units): %.20g (MBytes): %.1f\n", + Symbolic->num_mem_usage_est, + MBYTES (Symbolic->num_mem_usage_est))) ; + PRINTF (("\testimated final size (Units): %.20g (MBytes): %.1f\n", + Symbolic->num_mem_size_est, + MBYTES (Symbolic->num_mem_size_est))) ; + PRINTF ((" symbolic factorization memory usage (Units):" + " %.20g (MBytes): %.1f\n", + Symbolic->peak_sym_usage, + MBYTES (Symbolic->peak_sym_usage))) ; + PRINTF ((" frontal matrices / supercolumns:\n")) ; + PRINTF (("\tnumber of frontal chains: "ID"\n", nchains)) ; + PRINTF (("\tnumber of frontal matrices: "ID"\n", nfr)) ; + PRINTF (("\tlargest frontal matrix row dimension: "ID"\n", maxnrows)) ; + PRINTF (("\tlargest frontal matrix column dimension: "ID"\n",maxncols)); + } + + k = 0 ; + done = FALSE ; + + for (chain = 0 ; chain < nchains ; chain++) + { + frontid1 = Chain_start [chain] ; + frontid2 = Chain_start [chain+1] - 1 ; + PRINTF4 (("\n Frontal chain: "ID". Frontal matrices "ID" to "ID"\n", + INDEX (chain), INDEX (frontid1), INDEX (frontid2))) ; + PRINTF4 (("\tLargest frontal matrix in Frontal chain: "ID"-by-"ID"\n", + Chain_maxrows [chain], Chain_maxcols [chain])) ; + for (frontid = frontid1 ; frontid <= frontid2 ; frontid++) + { + kk = Front_npivcol [frontid] ; + PRINTF4 (("\tFront: "ID" pivot cols: "ID" (pivot columns "ID" to " + ID")\n", INDEX (frontid), kk, INDEX (k), INDEX (k+kk-1))) ; + PRINTF4 (("\t pivot row candidates: "ID" to "ID"\n", + INDEX (Front_1strow [Front_leftmostdesc [frontid]]), + INDEX (Front_1strow [frontid+1]-1))) ; + PRINTF4 (("\t leftmost descendant: "ID"\n", + INDEX (Front_leftmostdesc [frontid]))) ; + PRINTF4 (("\t 1st new candidate row : "ID"\n", + INDEX (Front_1strow [frontid]))) ; + PRINTF4 (("\t parent:")) ; + if (Front_parent [frontid] == EMPTY) + { + PRINTF4 ((" (none)\n")) ; + } + else + { + PRINTF4 ((" "ID"\n", INDEX (Front_parent [frontid]))) ; + } + done = (frontid == 20 && frontid < nfr-1 && prl == 4) ; + if (done) + { + PRINTF4 (("\t...\n")) ; + break ; + } + k += kk ; + } + if (Front_npivcol [nfr] != 0) + { + PRINTF4 (("\tFront: "ID" placeholder for "ID" empty columns\n", + INDEX (nfr), Front_npivcol [nfr])) ; + } + if (done) + { + break ; + } + } + + W = (Int *) UMF_malloc (MAX (n_row, n_col), sizeof (Int)) ; + if (!W) + { + PRINTF (("ERROR: out of memory to check Symbolic object\n\n")) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + PRINTF4 (("\nInitial column permutation, Q1: ")) ; + status1 = UMF_report_perm (n_col, Symbolic->Cperm_init, W, prl, 0) ; + + PRINTF4 (("\nInitial row permutation, P1: ")) ; + status2 = UMF_report_perm (n_row, Symbolic->Rperm_init, W, prl, 0) ; + + (void) UMF_free ((void *) W) ; + + if (status1 != UMFPACK_OK || status2 != UMFPACK_OK) + { + return (UMFPACK_ERROR_invalid_Symbolic_object) ; + } + + PRINTF4 ((" Symbolic object: ")) ; + PRINTF (("OK\n\n")) ; + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_triplet.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_triplet.c new file mode 100644 index 0000000000..d044dad377 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_triplet.c @@ -0,0 +1,101 @@ +/* ========================================================================== */ +/* === UMFPACK_report_triplet =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints a matrix in triplet form. See + umfpack_report_triplet.h for details. +*/ + +#include "umf_internal.h" + +GLOBAL Int UMFPACK_report_triplet +( + Int n_row, + Int n_col, + Int nz, + const Int Ti [ ], + const Int Tj [ ], + const double Tx [ ], +#ifdef COMPLEX + const double Tz [ ], +#endif + const double Control [UMFPACK_CONTROL] +) +{ + Int prl, prl1, k, i, j, do_values ; + Entry t ; + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (prl <= 2) + { + return (UMFPACK_OK) ; + } + + PRINTF (("triplet-form matrix, n_row = "ID", n_col = "ID" nz = "ID". ", + n_row, n_col, nz)) ; + + if (!Ti || !Tj) + { + PRINTF (("ERROR: indices not present\n\n")) ; + return (UMFPACK_ERROR_argument_missing) ; + } + + if (n_row <= 0 || n_col <= 0) + { + PRINTF (("ERROR: n_row or n_col is <= 0\n\n")) ; + return (UMFPACK_ERROR_n_nonpositive) ; + } + + if (nz < 0) + { + PRINTF (("ERROR: nz is < 0\n\n")) ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + + PRINTF4 (("\n")) ; + +#ifdef COMPLEX + do_values = Tx && Tz ; +#else + do_values = Tx != (double *) NULL ; +#endif + + prl1 = prl ; + for (k = 0 ; k < nz ; k++) + { + i = Ti [k] ; + j = Tj [k] ; + PRINTF4 ((" "ID" : "ID" "ID" ", INDEX (k), INDEX (i), INDEX (j))) ; + if (do_values && prl >= 4) + { + ASSIGN (t, Tx [k], Tz [k]) ; + PRINT_ENTRY (t) ; + } + PRINTF4 (("\n")) ; + if (i < 0 || i >= n_row || j < 0 || j >= n_col) + { + /* invalid triplet */ + PRINTF (("ERROR: invalid triplet\n\n")) ; + return (UMFPACK_ERROR_invalid_matrix) ; + } + if (prl == 4 && k == 9 && nz > 10) + { + PRINTF ((" ...\n")) ; + prl-- ; + } + } + prl = prl1 ; + + PRINTF4 ((" triplet-form matrix ")) ; + PRINTF (("OK\n\n")) ; + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_vector.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_vector.c new file mode 100644 index 0000000000..d5e18a1353 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_report_vector.c @@ -0,0 +1,44 @@ +/* ========================================================================== */ +/* === UMFPACK_report_vector ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints a real or complex vector. + See umfpack_report_vector.h for details. +*/ + +#include "umf_internal.h" +#include "umf_report_vector.h" + +GLOBAL Int UMFPACK_report_vector +( + Int n, + const double Xx [ ], +#ifdef COMPLEX + const double Xz [ ], +#endif + const double Control [UMFPACK_CONTROL] +) +{ + Int prl ; + +#ifndef COMPLEX + double *Xz = (double *) NULL ; +#endif + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (prl <= 2) + { + return (UMFPACK_OK) ; + } + + return (UMF_report_vector (n, Xx, Xz, prl, TRUE, FALSE)) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_save_numeric.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_save_numeric.c new file mode 100644 index 0000000000..b7803b7c17 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_save_numeric.c @@ -0,0 +1,93 @@ +/* ========================================================================== */ +/* === UMFPACK_save_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Saves a Numeric object to a file. It can later be read back + in via a call to umfpack_*_load_numeric. +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" + +#define WRITE(object,type,n) \ +{ \ + ASSERT (object != (type *) NULL) ; \ + if (fwrite (object, sizeof (type), n, f) != n) \ + { \ + fclose (f) ; \ + return (UMFPACK_ERROR_file_IO) ; \ + } \ +} + +/* ========================================================================== */ +/* === UMFPACK_save_numeric ================================================= */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_save_numeric +( + void *NumericHandle, + char *user_filename +) +{ + NumericType *Numeric ; + char *filename ; + FILE *f ; + + /* get the Numeric object */ + Numeric = (NumericType *) NumericHandle ; + + /* make sure the Numeric object is valid */ + if (!UMF_valid_numeric (Numeric)) + { + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + /* get the filename, or use the default name if filename is NULL */ + if (user_filename == (char *) NULL) + { + filename = "numeric.umf" ; + } + else + { + filename = user_filename ; + } + f = fopen (filename, "wb") ; + if (!f) + { + return (UMFPACK_ERROR_file_IO) ; + } + + /* write the Numeric object to the file, in binary */ + WRITE (Numeric, NumericType, 1) ; + WRITE (Numeric->D, Entry, MIN (Numeric->n_row, Numeric->n_col)+1) ; + WRITE (Numeric->Rperm, Int, Numeric->n_row+1) ; + WRITE (Numeric->Cperm, Int, Numeric->n_col+1) ; + WRITE (Numeric->Lpos, Int, Numeric->npiv+1) ; + WRITE (Numeric->Lilen, Int, Numeric->npiv+1) ; + WRITE (Numeric->Lip, Int, Numeric->npiv+1) ; + WRITE (Numeric->Upos, Int, Numeric->npiv+1) ; + WRITE (Numeric->Uilen, Int, Numeric->npiv+1) ; + WRITE (Numeric->Uip, Int, Numeric->npiv+1) ; + if (Numeric->scale != UMFPACK_SCALE_NONE) + { + WRITE (Numeric->Rs, double, Numeric->n_row) ; + } + if (Numeric->ulen > 0) + { + WRITE (Numeric->Upattern, Int, Numeric->ulen+1) ; + } + WRITE (Numeric->Memory, Unit, Numeric->size) ; + + /* close the file */ + fclose (f) ; + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_save_symbolic.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_save_symbolic.c new file mode 100644 index 0000000000..7908b83f23 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_save_symbolic.c @@ -0,0 +1,96 @@ +/* ========================================================================== */ +/* === UMFPACK_save_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Saves a Symbolic object to a file. It can later be read + back in via a call to umfpack_*_load_symbolic. +*/ + +#include "umf_internal.h" +#include "umf_valid_symbolic.h" + +#define WRITE(object,type,n) \ +{ \ + ASSERT (object != (type *) NULL) ; \ + if (fwrite (object, sizeof (type), n, f) != n) \ + { \ + fclose (f) ; \ + return (UMFPACK_ERROR_file_IO) ; \ + } \ +} + +/* ========================================================================== */ +/* === UMFPACK_save_symbolic ================================================ */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_save_symbolic +( + void *SymbolicHandle, + char *user_filename +) +{ + SymbolicType *Symbolic ; + char *filename ; + FILE *f ; + + /* get the Symbolic object */ + Symbolic = (SymbolicType *) SymbolicHandle ; + + /* make sure the Symbolic object is valid */ + if (!UMF_valid_symbolic (Symbolic)) + { + return (UMFPACK_ERROR_invalid_Symbolic_object) ; + } + + /* get the filename, or use the default name if filename is NULL */ + if (user_filename == (char *) NULL) + { + filename = "symbolic.umf" ; + } + else + { + filename = user_filename ; + } + f = fopen (filename, "wb") ; + if (!f) + { + return (UMFPACK_ERROR_file_IO) ; + } + + /* write the Symbolic object to the file, in binary */ + WRITE (Symbolic, SymbolicType, 1) ; + WRITE (Symbolic->Cperm_init, Int, Symbolic->n_col+1) ; + WRITE (Symbolic->Rperm_init, Int, Symbolic->n_row+1) ; + WRITE (Symbolic->Front_npivcol, Int, Symbolic->nfr+1) ; + WRITE (Symbolic->Front_parent, Int, Symbolic->nfr+1) ; + WRITE (Symbolic->Front_1strow, Int, Symbolic->nfr+1) ; + WRITE (Symbolic->Front_leftmostdesc, Int, Symbolic->nfr+1) ; + WRITE (Symbolic->Chain_start, Int, Symbolic->nchains+1) ; + WRITE (Symbolic->Chain_maxrows, Int, Symbolic->nchains+1) ; + WRITE (Symbolic->Chain_maxcols, Int, Symbolic->nchains+1) ; + WRITE (Symbolic->Cdeg, Int, Symbolic->n_col+1) ; + WRITE (Symbolic->Rdeg, Int, Symbolic->n_row+1) ; + if (Symbolic->esize > 0) + { + /* only when dense rows are present */ + WRITE (Symbolic->Esize, Int, Symbolic->esize) ; + } + if (Symbolic->prefer_diagonal) + { + /* only when diagonal pivoting is prefered */ + WRITE (Symbolic->Diagonal_map, Int, Symbolic->n_col+1) ; + } + + /* close the file */ + fclose (f) ; + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_scale.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_scale.c new file mode 100644 index 0000000000..90d0221ff1 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_scale.c @@ -0,0 +1,112 @@ +/* ========================================================================== */ +/* === UMFPACK_scale ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Applies the scale factors computed during numerical + factorization to a vector. See umfpack_scale.h for more details. + + The LU factorization is L*U = P*R*A*Q, where P and Q are permutation + matrices, and R is diagonal. This routine computes X = R * B using the + matrix R stored in the Numeric object. + + Returns FALSE if any argument is invalid, TRUE otherwise. + + If R not present in the Numeric object, then R = I and no floating-point + work is done. B is simply copied into X. +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" + +GLOBAL Int UMFPACK_scale +( + double Xx [ ], +#ifdef COMPLEX + double Xz [ ], +#endif + const double Bx [ ], +#ifdef COMPLEX + const double Bz [ ], +#endif + void *NumericHandle +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + NumericType *Numeric ; + Int n, i ; + double *Rs ; + + Numeric = (NumericType *) NumericHandle ; + if (!UMF_valid_numeric (Numeric)) + { + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + n = Numeric->n_row ; + Rs = Numeric->Rs ; + + if (!Xx || !Bx +#ifdef COMPLEX + || !Xz || !Bz +#endif + ) + { + return (UMFPACK_ERROR_argument_missing) ; + } + + /* ---------------------------------------------------------------------- */ + /* X = R*B or R\B */ + /* ---------------------------------------------------------------------- */ + + if (Rs != (double *) NULL) + { +#ifndef NRECIPROCAL + if (Numeric->do_recip) + { + /* multiply by the scale factors */ + for (i = 0 ; i < n ; i++) + { + Xx [i] = Bx [i] * Rs [i] ; +#ifdef COMPLEX + Xz [i] = Bz [i] * Rs [i] ; +#endif + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + Xx [i] = Bx [i] / Rs [i] ; +#ifdef COMPLEX + Xz [i] = Bz [i] / Rs [i] ; +#endif + } + } + } + else + { + /* no scale factors, just copy B into X */ + for (i = 0 ; i < n ; i++) + { + Xx [i] = Bx [i] ; +#ifdef COMPLEX + Xz [i] = Bz [i] ; +#endif + } + } + + return (UMFPACK_OK) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_solve.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_solve.c new file mode 100644 index 0000000000..30bf8121a4 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_solve.c @@ -0,0 +1,249 @@ +/* ========================================================================== */ +/* === UMFPACK_solve ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Solves a linear system using the numerical factorization + computed by UMFPACK_numeric. See umfpack_solve.h for more details. + + For umfpack_*_solve: + Dynamic memory usage: UMFPACK_solve calls UMF_malloc twice, for + workspace of size c*n*sizeof(double) + n*sizeof(Int), where c is + defined below. On return, all of this workspace is free'd via UMF_free. + + For umfpack_*_wsolve: + No dynamic memory usage. Input arrays are used for workspace instead. + Pattern is a workspace of size n Integers. The double array W must be + at least of size c*n, where c is defined below. + + If iterative refinement is requested, and Ax=b, A'x=b or A.'x=b is being + solved, and the matrix A is not singular, then c is 5 for the real version + and 10 for the complex version. Otherwise, c is 1 for the real version and + 4 for the complex version. +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" +#include "umf_solve.h" + +#ifndef WSOLVE +#include "umf_malloc.h" +#include "umf_free.h" +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif +#endif + +GLOBAL Int +#ifdef WSOLVE +UMFPACK_wsolve +#else +UMFPACK_solve +#endif +( + Int sys, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + double Xx [ ], +#ifdef COMPLEX + double Xz [ ], +#endif + const double Bx [ ], +#ifdef COMPLEX + const double Bz [ ], +#endif + void *NumericHandle, + const double Control [UMFPACK_CONTROL], + double User_Info [UMFPACK_INFO] +#ifdef WSOLVE + , Int Pattern [ ], + double W [ ] +#endif +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + NumericType *Numeric ; + Int n, i, irstep, status ; + double Info2 [UMFPACK_INFO], *Info, /* tstart, tend */ stats [2] ; +#ifndef WSOLVE + Int *Pattern, wsize ; + double *W ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get the amount of time used by the process so far */ + /* ---------------------------------------------------------------------- */ + + umfpack_tic (stats) ; + +#ifndef WSOLVE +#ifndef NDEBUG + init_count = UMF_malloc_count ; +#endif +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + irstep = GET_CONTROL (UMFPACK_IRSTEP, UMFPACK_DEFAULT_IRSTEP) ; + + if (User_Info != (double *) NULL) + { + /* return Info in user's array */ + Info = User_Info ; + /* clear the parts of Info that are set by UMFPACK_solve */ + for (i = UMFPACK_IR_TAKEN ; i <= UMFPACK_SOLVE_TIME ; i++) + { + Info [i] = EMPTY ; + } + } + else + { + /* no Info array passed - use local one instead */ + Info = Info2 ; + for (i = 0 ; i < UMFPACK_INFO ; i++) + { + Info [i] = EMPTY ; + } + } + + Info [UMFPACK_STATUS] = UMFPACK_OK ; + Info [UMFPACK_SOLVE_FLOPS] = 0 ; + + Numeric = (NumericType *) NumericHandle ; + if (!UMF_valid_numeric (Numeric)) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_Numeric_object ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + Info [UMFPACK_NROW] = Numeric->n_row ; + Info [UMFPACK_NCOL] = Numeric->n_col ; + + if (Numeric->n_row != Numeric->n_col) + { + /* only square systems can be handled */ + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_system ; + return (UMFPACK_ERROR_invalid_system) ; + } + n = Numeric->n_row ; + if (Numeric->nnzpiv < n + || SCALAR_IS_ZERO (Numeric->rcond) || SCALAR_IS_NAN (Numeric->rcond)) + { + /* turn off iterative refinement if A is singular */ + /* or if U has NaN's on the diagonal. */ + irstep = 0 ; + } + + if (!Xx || !Bx +#ifdef COMPLEX + || !Xz || !Bz +#endif + ) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + + if (sys >= UMFPACK_Pt_L) + { + /* no iterative refinement except for nonsingular Ax=b, A'x=b, A.'x=b */ + irstep = 0 ; + } + + /* ---------------------------------------------------------------------- */ + /* allocate or check the workspace */ + /* ---------------------------------------------------------------------- */ + +#ifdef WSOLVE + + if (!W || !Pattern) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + +#else + +#ifdef COMPLEX + if (irstep > 0) + { + wsize = 10*n ; /* W, X, Z, S, Y, B2 */ + } + else + { + wsize = 4*n ; /* W, X */ + } +#else + if (irstep > 0) + { + wsize = 5*n ; /* W, Z, S, Y, B2 */ + } + else + { + wsize = n ; /* W */ + } +#endif + + Pattern = (Int *) UMF_malloc (n, sizeof (Int)) ; + W = (double *) UMF_malloc (wsize, sizeof (double)) ; + if (!W || !Pattern) + { + DEBUGm4 (("out of memory: solve work\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + (void) UMF_free ((void *) W) ; + (void) UMF_free ((void *) Pattern) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + +#endif /* WSOLVE */ + + /* ---------------------------------------------------------------------- */ + /* solve the system */ + /* ---------------------------------------------------------------------- */ + + status = UMF_solve (sys, Ap, Ai, Ax, Xx, Bx, +#ifdef COMPLEX + Az, Xz, Bz, +#endif + Numeric, irstep, Info, Pattern, W) ; + + /* ---------------------------------------------------------------------- */ + /* free the workspace (if allocated) */ + /* ---------------------------------------------------------------------- */ + +#ifndef WSOLVE + (void) UMF_free ((void *) W) ; + (void) UMF_free ((void *) Pattern) ; + ASSERT (UMF_malloc_count == init_count) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get the time used by UMFPACK_*solve */ + /* ---------------------------------------------------------------------- */ + + Info [UMFPACK_STATUS] = status ; + if (status >= 0) + { + umfpack_toc (stats) ; + Info [UMFPACK_SOLVE_WALLTIME] = stats [0] ; + Info [UMFPACK_SOLVE_TIME] = stats [1] ; + } + + return (status) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_symbolic.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_symbolic.c new file mode 100644 index 0000000000..2125695978 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_symbolic.c @@ -0,0 +1,40 @@ +/* ========================================================================== */ +/* === UMFPACK_symbolic ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Performs a symbolic factorization. + See umfpack_symbolic.h for details. +*/ + +#include "umf_internal.h" + +GLOBAL Int UMFPACK_symbolic +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + void **SymbolicHandle, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) +{ + Int *Qinit = (Int *) NULL ; + return (UMFPACK_qsymbolic (n_row, n_col, Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + Qinit, SymbolicHandle, Control, Info)) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_tictoc.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_tictoc.c new file mode 100644 index 0000000000..706f3acb85 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_tictoc.c @@ -0,0 +1,107 @@ +/* ========================================================================== */ +/* === umfpack_tictoc ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Returns the time in seconds used by the process, and + the current wall clock time. BE CAREFUL: if you compare the run time of + UMFPACK with other sparse matrix packages, be sure to use the same timer. + See umfpack_tictoc.h for details. + + These routines conform to the POSIX standard. See umf_config.h for + more details. +*/ + +#include "umf_internal.h" + +#ifdef NO_TIMER + +/* -------------------------------------------------------------------------- */ +/* no timer used if -DNO_TIMER is defined at compile time */ +/* -------------------------------------------------------------------------- */ + +void umfpack_tic (double stats [2]) +{ + stats [0] = 0 ; + stats [1] = 0 ; +} + +void umfpack_toc (double stats [2]) +{ + stats [0] = 0 ; + stats [1] = 0 ; +} + +#else + +/* -------------------------------------------------------------------------- */ +/* timer routines, using either times() or clock() */ +/* -------------------------------------------------------------------------- */ + +#define TINY_TIME 1e-4 + +#ifndef NPOSIX + +#include +#include + +void umfpack_tic (double stats [2]) +{ + /* Return the current time */ + /* stats [0]: current wallclock time, in seconds */ + /* stats [1]: user + system time for the process, in seconds */ + + struct tms t ; + double ticks ; + + ticks = (double) sysconf (_SC_CLK_TCK) ; + stats [0] = (double) times (&t) / ticks ; + stats [1] = (double) (t.tms_utime + t.tms_stime) / ticks ; + + /* if time is tiny, just return zero */ + if (stats [0] < TINY_TIME) stats [0] = 0 ; + if (stats [1] < TINY_TIME) stats [1] = 0 ; +} + +#else + +/* Generic ANSI C: use the ANSI clock function. No wallclock time. */ + +#include + +void umfpack_tic (double stats [2]) +{ + stats [0] = 0 ; + stats [1] = ((double) (clock ( ))) / ((double) (CLOCKS_PER_SEC)) ; + if (stats [1] < TINY_TIME) stats [1] = 0 ; +} + +#endif + +/* -------------------------------------------------------------------------- */ + +void umfpack_toc (double stats [2]) +{ + /* Return the current time since the last call to umfpack_tic. */ + /* On input, stats holds the values returned by umfpack_tic. */ + /* On ouput, stats holds the time since the last umfpack_tic. */ + + double done [2] ; + umfpack_tic (done) ; + + stats [0] = done [0] - stats [0] ; + stats [1] = done [1] - stats [1] ; + + if (stats [0] < 0) stats [0] = 0 ; + if (stats [1] < 0) stats [1] = 0 ; + +} + +#endif diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_timer.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_timer.c new file mode 100644 index 0000000000..be0e7fc647 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_timer.c @@ -0,0 +1,85 @@ +/* ========================================================================== */ +/* === umfpack_timer ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Returns the time in seconds used by the process. BE + CAREFUL: if you compare the run time of UMFPACK with other sparse matrix + packages, be sure to use the same timer. See umfpack_timer.h for details. + This was the timer used internally by UMFPACK Version 4.0. See + umfpack_tictoc.h, which is the timer now used internally by UMFPACK V4.1. +*/ + +#ifdef NO_TIMER + +/* -------------------------------------------------------------------------- */ +/* no timer used if -DNO_TIMER is defined at compile time */ +/* -------------------------------------------------------------------------- */ + +double umfpack_timer ( void ) +{ + return (0) ; +} + +#else + +#ifdef GETRUSAGE + +/* -------------------------------------------------------------------------- */ +/* use getrusage for accurate process times (and no overflow) */ +/* -------------------------------------------------------------------------- */ + +/* + This works under Solaris, SGI Irix, Linux, IBM RS 6000 (AIX), and Compaq + Alpha. It might work on other Unix systems, too. Includes both the "user + time" and the "system time". The system time is the time spent by the + operating system on behalf of the process, and thus should be charged to + the process. +*/ + +#include +#include + +double umfpack_timer ( void ) +{ + struct rusage ru ; + double user_time, sys_time ; + + (void) getrusage (RUSAGE_SELF, &ru) ; + + user_time = + ru.ru_utime.tv_sec /* user time (seconds) */ + + 1e-6 * ru.ru_utime.tv_usec ; /* user time (microseconds) */ + + sys_time = + ru.ru_stime.tv_sec /* system time (seconds) */ + + 1e-6 * ru.ru_stime.tv_usec ; /* system time (microseconds) */ + + return (user_time + sys_time) ; +} + +#else + +/* -------------------------------------------------------------------------- */ +/* Generic ANSI C: use the ANSI clock function */ +/* -------------------------------------------------------------------------- */ + +/* This is portable, but may overflow. On Sun Solaris, when compiling in */ +/* 32-bit mode, the overflow occurs in only 2147 seconds (about 36 minutes). */ + +#include + +double umfpack_timer ( void ) +{ + return (((double) (clock ( ))) / ((double) (CLOCKS_PER_SEC))) ; +} + +#endif +#endif diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_transpose.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_transpose.c new file mode 100644 index 0000000000..7dedecb6be --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_transpose.c @@ -0,0 +1,109 @@ +/* ========================================================================== */ +/* === UMFPACK_transpose ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User callable. Computes a permuted transpose, R = (A (P,Q))' in MATLAB + notation. See umfpack_transpose.h for details. A and R can be rectangular. + The matrix A may be singular. + The complex version can do transpose (') or array transpose (.'). + + Dynamic memory usage: A single call to UMF_malloc is made, for a workspace + of size max (n_row,n_col,1) * sizeof(Int). This is then free'd on return, + via UMF_free. +*/ + +#include "umf_internal.h" +#include "umf_transpose.h" +#include "umf_malloc.h" +#include "umf_free.h" + +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif + +/* ========================================================================== */ + +GLOBAL Int UMFPACK_transpose +( + Int n_row, + Int n_col, + const Int Ap [ ], /* size n_col+1 */ + const Int Ai [ ], /* size nz = Ap [n_col] */ + const double Ax [ ], /* size nz, if present */ +#ifdef COMPLEX + const double Az [ ], /* size nz, if present */ +#endif + + const Int P [ ], /* P [k] = i means original row i is kth row in A(P,Q)*/ + /* P is identity if not present */ + /* size n_row, if present */ + + const Int Q [ ], /* Q [k] = j means original col j is kth col in A(P,Q)*/ + /* Q is identity if not present */ + /* size n_col, if present */ + + Int Rp [ ], /* size n_row+1 */ + Int Ri [ ], /* size nz */ + double Rx [ ] /* size nz, if present */ +#ifdef COMPLEX + , double Rz [ ] /* size nz, if present */ + , Int do_conjugate /* if true, then to conjugate transpose */ + /* otherwise, do array transpose */ +#endif +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int status, *W, nn ; + +#ifndef NDEBUG + init_count = UMF_malloc_count ; + UMF_dump_start ( ) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* allocate workspace */ + /* ---------------------------------------------------------------------- */ + + nn = MAX (n_row, n_col) ; + nn = MAX (nn, 1) ; + W = (Int *) UMF_malloc (nn, sizeof (Int)) ; + if (!W) + { + DEBUGm4 (("out of memory: transpose work\n")) ; + ASSERT (UMF_malloc_count == init_count) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + ASSERT (UMF_malloc_count == init_count + 1) ; + + /* ---------------------------------------------------------------------- */ + /* C = (A (P,Q))' or (A (P,Q)).' */ + /* ---------------------------------------------------------------------- */ + + status = UMF_transpose (n_row, n_col, Ap, Ai, Ax, P, Q, n_col, Rp, Ri, Rx, + W, TRUE +#ifdef COMPLEX + , Az, Rz, do_conjugate +#endif + ) ; + + /* ---------------------------------------------------------------------- */ + /* free the workspace */ + /* ---------------------------------------------------------------------- */ + + (void) UMF_free ((void *) W) ; + ASSERT (UMF_malloc_count == init_count) ; + + return (status) ; +} diff --git a/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_triplet_to_col.c b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_triplet_to_col.c new file mode 100644 index 0000000000..a868231a81 --- /dev/null +++ b/deal.II/contrib/umfpack/UMFPACK/Source/umfpack_triplet_to_col.c @@ -0,0 +1,229 @@ +/* ========================================================================== */ +/* === UMFPACK_triplet_to_col =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.3 (Jan. 16, 2004), Copyright (c) 2004 by Timothy A. */ +/* Davis. All Rights Reserved. See ../README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User callable. Converts triplet input to column-oriented form. Duplicate + entries may exist (they are summed in the output). The columns of the + column-oriented form are in sorted order. The input is not modified. + Returns 1 if OK, 0 if an error occured. See umfpack_triplet_to_col.h for + details. + + If Map is present (a non-NULL pointer to an Int array of size nz), then on + output it holds the position of the triplets in the column-form matrix. + That is, suppose p = Map [k], and the k-th triplet is i=Ti[k], j=Tj[k], and + aij=Tx[k]. Then i=Ai[p], and aij will have been summed into Ax[p]. Also, + Ap[j] <= p < Ap[j+1]. The Map array is not computed if it is (Int *) NULL. + + Dynamic memory usage: + + If numerical values are present, then one (two for complex version) + workspace of size (nz+1)*sizeof(double) is allocated via UMF_malloc. + Next, 4 calls to UMF_malloc are made to obtain workspace of size + ((nz+1) + (n_row+1) + n_row + MAX (n_row,n_col)) * sizeof(Int). All of + this workspace (4 to 6 objects) are free'd via UMF_free on return. + + For the complex version, additional space is allocated. + + An extra array of size nz*sizeof(Int) is allocated if Map is present. +*/ + +#include "umf_internal.h" +#include "umf_malloc.h" +#include "umf_free.h" +#include "umf_triplet.h" + +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif + +/* ========================================================================== */ + +GLOBAL Int UMFPACK_triplet_to_col +( + Int n_row, + Int n_col, + Int nz, + const Int Ti [ ], /* size nz */ + const Int Tj [ ], /* size nz */ + const double Tx [ ], /* size nz */ +#ifdef COMPLEX + const double Tz [ ], /* size nz */ +#endif + Int Ap [ ], /* size n_col + 1 */ + Int Ai [ ], /* size nz */ + double Ax [ ] /* size nz */ +#ifdef COMPLEX + , double Az [ ] /* size nz */ +#endif + , Int Map [ ] /* size nz */ +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int *RowCount, *Rp, *Rj, *W, nn, do_values, do_map, *Map2, status ; + double *Rx, *Rz ; + +#ifndef NDEBUG + UMF_dump_start ( ) ; + init_count = UMF_malloc_count ; +#endif + + /* ---------------------------------------------------------------------- */ + /* check inputs */ + /* ---------------------------------------------------------------------- */ + + if (!Ai || !Ap || !Ti || !Tj) + { + return (UMFPACK_ERROR_argument_missing) ; + } + + if (n_row <= 0 || n_col <= 0) /* must be > 0 */ + { + return (UMFPACK_ERROR_n_nonpositive) ; + } + + if (nz < 0) /* nz must be >= 0 (singular matrices are OK) */ + { + return (UMFPACK_ERROR_invalid_matrix) ; + } + + nn = MAX (n_row, n_col) ; + + /* ---------------------------------------------------------------------- */ + /* allocate workspace */ + /* ---------------------------------------------------------------------- */ + + Rx = (double *) NULL ; + Rz = (double *) NULL ; + +#ifdef COMPLEX + do_values = Ax && Tx && Az && Tz ; + if (do_values) + { + Rx = (double *) UMF_malloc (nz+1, sizeof (double)) ; + Rz = (double *) UMF_malloc (nz+1, sizeof (double)) ; + if (!Rx || !Rz) + { + DEBUGm4 (("out of memory: triplet work (complex)\n")) ; + (void) UMF_free ((void *) Rx) ; + (void) UMF_free ((void *) Rz) ; + ASSERT (UMF_malloc_count == init_count) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + } +#else + do_values = Ax && Tx ; + if (do_values) + { + Rx = (double *) UMF_malloc (nz+1, sizeof (double)) ; + if (!Rx) + { + DEBUGm4 (("out of memory: triplet work (real)\n")) ; + ASSERT (UMF_malloc_count == init_count) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + } +#endif + + do_map = (Map != (Int *) NULL) ; + Map2 = (Int *) NULL ; + if (do_map) + { + DEBUG0 (("Do map:\n")) ; + Map2 = (Int *) UMF_malloc (nz+1, sizeof (Int)) ; + if (!Map2) + { + DEBUGm4 (("out of memory: triplet map\n")) ; + (void) UMF_free ((void *) Rx) ; + (void) UMF_free ((void *) Rz) ; + ASSERT (UMF_malloc_count == init_count) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + } + + Rj = (Int *) UMF_malloc (nz+1, sizeof (Int)) ; + Rp = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + RowCount = (Int *) UMF_malloc (n_row, sizeof (Int)) ; + W = (Int *) UMF_malloc (nn, sizeof (Int)) ; + if (!Rj || !Rp || !RowCount || !W) + { + DEBUGm4 (("out of memory: triplet work (int)\n")) ; + (void) UMF_free ((void *) Rx) ; + (void) UMF_free ((void *) Rz) ; + (void) UMF_free ((void *) Map2) ; + (void) UMF_free ((void *) Rp) ; + (void) UMF_free ((void *) Rj) ; + (void) UMF_free ((void *) RowCount) ; + (void) UMF_free ((void *) W) ; + ASSERT (UMF_malloc_count == init_count) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + ASSERT (UMF_malloc_count == init_count + 4 + + (Rx != (double *) NULL) + (Rz != (double *) NULL) + do_map) ; + + /* ---------------------------------------------------------------------- */ + /* convert from triplet to column form */ + /* ---------------------------------------------------------------------- */ + + if (do_map) + { + if (do_values) + { + status = UMF_triplet_map_x (n_row, n_col, nz, Ti, Tj, Ap, Ai, Rp, + Rj, W, RowCount, Tx, Ax, Rx +#ifdef COMPLEX + , Tz, Az, Rz +#endif + , Map, Map2) ; + } + else + { + status = UMF_triplet_map_nox (n_row, n_col, nz, Ti, Tj, Ap, Ai, Rp, + Rj, W, RowCount, Map, Map2) ; + } + } + else + { + if (do_values) + { + status = UMF_triplet_nomap_x (n_row, n_col, nz, Ti, Tj, Ap, Ai, Rp, + Rj, W, RowCount , Tx, Ax, Rx +#ifdef COMPLEX + , Tz, Az, Rz +#endif + ) ; + } + else + { + status = UMF_triplet_nomap_nox (n_row, n_col, nz, Ti, Tj, Ap, Ai, + Rp, Rj, W, RowCount) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* free the workspace */ + /* ---------------------------------------------------------------------- */ + + (void) UMF_free ((void *) Rx) ; + (void) UMF_free ((void *) Rz) ; + (void) UMF_free ((void *) Map2) ; + (void) UMF_free ((void *) Rp) ; + (void) UMF_free ((void *) Rj) ; + (void) UMF_free ((void *) RowCount) ; + (void) UMF_free ((void *) W) ; + ASSERT (UMF_malloc_count == init_count) ; + + return (status) ; +} -- 2.39.5