From 3560c8ee166f48ff431831f6036f16f919485a6e Mon Sep 17 00:00:00 2001 From: Francesco Lannutti Date: Mon, 12 Mar 2012 19:20:20 +0100 Subject: [PATCH] Added UMFPACK as new experimental linear solver, (#1 new files) --- src/include/ngspice/umfpack.h | 436 +++ src/include/ngspice/umfpack_col_to_triplet.h | 110 + src/include/ngspice/umfpack_defaults.h | 69 + src/include/ngspice/umfpack_free_numeric.h | 67 + src/include/ngspice/umfpack_free_symbolic.h | 67 + src/include/ngspice/umfpack_get_determinant.h | 196 + src/include/ngspice/umfpack_get_lunz.h | 137 + src/include/ngspice/umfpack_get_numeric.h | 254 ++ src/include/ngspice/umfpack_get_symbolic.h | 337 ++ src/include/ngspice/umfpack_global.h | 22 + src/include/ngspice/umfpack_load_numeric.h | 95 + src/include/ngspice/umfpack_load_symbolic.h | 95 + src/include/ngspice/umfpack_numeric.h | 543 +++ src/include/ngspice/umfpack_qsymbolic.h | 234 ++ src/include/ngspice/umfpack_report_control.h | 76 + src/include/ngspice/umfpack_report_info.h | 86 + src/include/ngspice/umfpack_report_matrix.h | 203 ++ src/include/ngspice/umfpack_report_numeric.h | 112 + src/include/ngspice/umfpack_report_perm.h | 112 + src/include/ngspice/umfpack_report_status.h | 90 + src/include/ngspice/umfpack_report_symbolic.h | 111 + src/include/ngspice/umfpack_report_triplet.h | 153 + src/include/ngspice/umfpack_report_vector.h | 133 + src/include/ngspice/umfpack_save_numeric.h | 90 + src/include/ngspice/umfpack_save_symbolic.h | 90 + src/include/ngspice/umfpack_scale.h | 112 + src/include/ngspice/umfpack_solve.h | 301 ++ src/include/ngspice/umfpack_symbolic.h | 517 +++ src/include/ngspice/umfpack_tictoc.h | 60 + src/include/ngspice/umfpack_timer.h | 39 + src/include/ngspice/umfpack_transpose.h | 216 ++ src/include/ngspice/umfpack_triplet_to_col.h | 263 ++ src/include/ngspice/umfpack_wsolve.h | 172 + src/maths/UMFPACK/Makefile.am | 352 ++ src/maths/UMFPACK/amd_1.c | 181 + src/maths/UMFPACK/amd_2.c | 1842 ++++++++++ src/maths/UMFPACK/amd_aat.c | 185 + src/maths/UMFPACK/amd_control.c | 64 + src/maths/UMFPACK/amd_defaults.c | 38 + src/maths/UMFPACK/amd_dump.c | 180 + src/maths/UMFPACK/amd_global.c | 84 + src/maths/UMFPACK/amd_info.c | 120 + src/maths/UMFPACK/amd_internal.h | 350 ++ src/maths/UMFPACK/amd_order.c | 200 ++ src/maths/UMFPACK/amd_post_tree.c | 121 + src/maths/UMFPACK/amd_postorder.c | 207 ++ src/maths/UMFPACK/amd_preprocess.c | 119 + src/maths/UMFPACK/amd_valid.c | 93 + src/maths/UMFPACK/cholmod_blas.h | 456 +++ src/maths/UMFPACK/umf_analyze.c | 705 ++++ src/maths/UMFPACK/umf_analyze.h | 23 + src/maths/UMFPACK/umf_apply_order.c | 44 + src/maths/UMFPACK/umf_apply_order.h | 14 + src/maths/UMFPACK/umf_assemble.c | 1216 +++++++ src/maths/UMFPACK/umf_assemble.h | 17 + src/maths/UMFPACK/umf_blas3_update.c | 176 + src/maths/UMFPACK/umf_blas3_update.h | 10 + src/maths/UMFPACK/umf_build_tuples.c | 160 + src/maths/UMFPACK/umf_build_tuples.h | 11 + src/maths/UMFPACK/umf_cholmod.c | 239 ++ src/maths/UMFPACK/umf_cholmod.h | 38 + src/maths/UMFPACK/umf_colamd.c | 3139 +++++++++++++++++ src/maths/UMFPACK/umf_colamd.h | 255 ++ src/maths/UMFPACK/umf_config.h | 326 ++ src/maths/UMFPACK/umf_create_element.c | 593 ++++ src/maths/UMFPACK/umf_create_element.h | 12 + src/maths/UMFPACK/umf_dump.c | 1205 +++++++ src/maths/UMFPACK/umf_dump.h | 189 + src/maths/UMFPACK/umf_extend_front.c | 393 +++ src/maths/UMFPACK/umf_extend_front.h | 11 + src/maths/UMFPACK/umf_free.c | 46 + src/maths/UMFPACK/umf_free.h | 10 + src/maths/UMFPACK/umf_fsize.c | 70 + src/maths/UMFPACK/umf_fsize.h | 15 + src/maths/UMFPACK/umf_garbage_collection.c | 696 ++++ src/maths/UMFPACK/umf_garbage_collection.h | 14 + src/maths/UMFPACK/umf_get_memory.c | 223 ++ src/maths/UMFPACK/umf_get_memory.h | 15 + src/maths/UMFPACK/umf_grow_front.c | 294 ++ src/maths/UMFPACK/umf_grow_front.h | 14 + src/maths/UMFPACK/umf_init_front.c | 268 ++ src/maths/UMFPACK/umf_init_front.h | 11 + src/maths/UMFPACK/umf_internal.h | 738 ++++ src/maths/UMFPACK/umf_is_permutation.c | 56 + src/maths/UMFPACK/umf_is_permutation.h | 13 + src/maths/UMFPACK/umf_kernel.c | 300 ++ src/maths/UMFPACK/umf_kernel.h | 18 + src/maths/UMFPACK/umf_kernel_init.c | 1066 ++++++ src/maths/UMFPACK/umf_kernel_init.h | 18 + src/maths/UMFPACK/umf_kernel_wrapup.c | 467 +++ src/maths/UMFPACK/umf_kernel_wrapup.h | 12 + src/maths/UMFPACK/umf_local_search.c | 1955 ++++++++++ src/maths/UMFPACK/umf_local_search.h | 12 + src/maths/UMFPACK/umf_lsolve.c | 152 + src/maths/UMFPACK/umf_lsolve.h | 12 + src/maths/UMFPACK/umf_ltsolve.c | 226 ++ src/maths/UMFPACK/umf_ltsolve.h | 19 + src/maths/UMFPACK/umf_malloc.c | 92 + src/maths/UMFPACK/umf_malloc.h | 25 + src/maths/UMFPACK/umf_mem_alloc_element.c | 83 + src/maths/UMFPACK/umf_mem_alloc_element.h | 17 + src/maths/UMFPACK/umf_mem_alloc_head_block.c | 55 + src/maths/UMFPACK/umf_mem_alloc_head_block.h | 11 + src/maths/UMFPACK/umf_mem_alloc_tail_block.c | 134 + src/maths/UMFPACK/umf_mem_alloc_tail_block.h | 11 + src/maths/UMFPACK/umf_mem_free_tail_block.c | 143 + src/maths/UMFPACK/umf_mem_free_tail_block.h | 11 + src/maths/UMFPACK/umf_mem_init_memoryspace.c | 65 + src/maths/UMFPACK/umf_mem_init_memoryspace.h | 10 + src/maths/UMFPACK/umf_realloc.c | 76 + src/maths/UMFPACK/umf_realloc.h | 12 + src/maths/UMFPACK/umf_report_perm.c | 86 + src/maths/UMFPACK/umf_report_perm.h | 14 + src/maths/UMFPACK/umf_report_vector.c | 111 + src/maths/UMFPACK/umf_report_vector.h | 15 + src/maths/UMFPACK/umf_row_search.c | 837 +++++ src/maths/UMFPACK/umf_row_search.h | 32 + src/maths/UMFPACK/umf_scale.c | 82 + src/maths/UMFPACK/umf_scale.h | 12 + src/maths/UMFPACK/umf_scale_column.c | 434 +++ src/maths/UMFPACK/umf_scale_column.h | 11 + src/maths/UMFPACK/umf_set_stats.c | 134 + src/maths/UMFPACK/umf_set_stats.h | 24 + src/maths/UMFPACK/umf_singletons.c | 935 +++++ src/maths/UMFPACK/umf_singletons.h | 32 + src/maths/UMFPACK/umf_solve.c | 1396 ++++++++ src/maths/UMFPACK/umf_solve.h | 25 + src/maths/UMFPACK/umf_start_front.c | 284 ++ src/maths/UMFPACK/umf_start_front.h | 13 + src/maths/UMFPACK/umf_store_lu.c | 1057 ++++++ src/maths/UMFPACK/umf_store_lu.h | 17 + src/maths/UMFPACK/umf_symbolic_usage.c | 46 + src/maths/UMFPACK/umf_symbolic_usage.h | 15 + src/maths/UMFPACK/umf_transpose.c | 401 +++ src/maths/UMFPACK/umf_transpose.h | 27 + src/maths/UMFPACK/umf_triplet.c | 429 +++ src/maths/UMFPACK/umf_triplet.h | 85 + src/maths/UMFPACK/umf_tuple_lengths.c | 136 + src/maths/UMFPACK/umf_tuple_lengths.h | 12 + src/maths/UMFPACK/umf_usolve.c | 227 ++ src/maths/UMFPACK/umf_usolve.h | 12 + src/maths/UMFPACK/umf_utsolve.c | 332 ++ src/maths/UMFPACK/umf_utsolve.h | 20 + src/maths/UMFPACK/umf_valid_numeric.c | 47 + src/maths/UMFPACK/umf_valid_numeric.h | 10 + src/maths/UMFPACK/umf_valid_symbolic.c | 48 + src/maths/UMFPACK/umf_valid_symbolic.h | 10 + src/maths/UMFPACK/umf_version.h | 878 +++++ src/maths/UMFPACK/umfpack_col_to_triplet.c | 72 + src/maths/UMFPACK/umfpack_defaults.c | 71 + src/maths/UMFPACK/umfpack_free_numeric.c | 57 + src/maths/UMFPACK/umfpack_free_symbolic.c | 56 + src/maths/UMFPACK/umfpack_get_determinant.c | 308 ++ src/maths/UMFPACK/umfpack_get_lunz.c | 55 + src/maths/UMFPACK/umfpack_get_numeric.c | 1057 ++++++ src/maths/UMFPACK/umfpack_get_symbolic.c | 191 + src/maths/UMFPACK/umfpack_global.c | 130 + src/maths/UMFPACK/umfpack_load_numeric.c | 161 + src/maths/UMFPACK/umfpack_load_symbolic.c | 165 + src/maths/UMFPACK/umfpack_numeric.c | 793 +++++ src/maths/UMFPACK/umfpack_qsymbolic.c | 2752 +++++++++++++++ src/maths/UMFPACK/umfpack_report_control.c | 379 ++ src/maths/UMFPACK/umfpack_report_info.c | 627 ++++ src/maths/UMFPACK/umfpack_report_matrix.c | 203 ++ src/maths/UMFPACK/umfpack_report_numeric.c | 663 ++++ src/maths/UMFPACK/umfpack_report_perm.c | 44 + src/maths/UMFPACK/umfpack_report_status.c | 123 + src/maths/UMFPACK/umfpack_report_symbolic.c | 243 ++ src/maths/UMFPACK/umfpack_report_triplet.c | 99 + src/maths/UMFPACK/umfpack_report_vector.c | 43 + src/maths/UMFPACK/umfpack_save_numeric.c | 96 + src/maths/UMFPACK/umfpack_save_symbolic.c | 96 + src/maths/UMFPACK/umfpack_scale.c | 158 + src/maths/UMFPACK/umfpack_solve.c | 245 ++ src/maths/UMFPACK/umfpack_symbolic.c | 38 + src/maths/UMFPACK/umfpack_tictoc.c | 121 + src/maths/UMFPACK/umfpack_timer.c | 100 + src/maths/UMFPACK/umfpack_transpose.c | 108 + src/maths/UMFPACK/umfpack_triplet_to_col.c | 225 ++ src/maths/UMFPACK/umfpacksmp.c | 701 ++++ 180 files changed, 44917 insertions(+) create mode 100644 src/include/ngspice/umfpack.h create mode 100644 src/include/ngspice/umfpack_col_to_triplet.h create mode 100644 src/include/ngspice/umfpack_defaults.h create mode 100644 src/include/ngspice/umfpack_free_numeric.h create mode 100644 src/include/ngspice/umfpack_free_symbolic.h create mode 100644 src/include/ngspice/umfpack_get_determinant.h create mode 100644 src/include/ngspice/umfpack_get_lunz.h create mode 100644 src/include/ngspice/umfpack_get_numeric.h create mode 100644 src/include/ngspice/umfpack_get_symbolic.h create mode 100644 src/include/ngspice/umfpack_global.h create mode 100644 src/include/ngspice/umfpack_load_numeric.h create mode 100644 src/include/ngspice/umfpack_load_symbolic.h create mode 100644 src/include/ngspice/umfpack_numeric.h create mode 100644 src/include/ngspice/umfpack_qsymbolic.h create mode 100644 src/include/ngspice/umfpack_report_control.h create mode 100644 src/include/ngspice/umfpack_report_info.h create mode 100644 src/include/ngspice/umfpack_report_matrix.h create mode 100644 src/include/ngspice/umfpack_report_numeric.h create mode 100644 src/include/ngspice/umfpack_report_perm.h create mode 100644 src/include/ngspice/umfpack_report_status.h create mode 100644 src/include/ngspice/umfpack_report_symbolic.h create mode 100644 src/include/ngspice/umfpack_report_triplet.h create mode 100644 src/include/ngspice/umfpack_report_vector.h create mode 100644 src/include/ngspice/umfpack_save_numeric.h create mode 100644 src/include/ngspice/umfpack_save_symbolic.h create mode 100644 src/include/ngspice/umfpack_scale.h create mode 100644 src/include/ngspice/umfpack_solve.h create mode 100644 src/include/ngspice/umfpack_symbolic.h create mode 100644 src/include/ngspice/umfpack_tictoc.h create mode 100644 src/include/ngspice/umfpack_timer.h create mode 100644 src/include/ngspice/umfpack_transpose.h create mode 100644 src/include/ngspice/umfpack_triplet_to_col.h create mode 100644 src/include/ngspice/umfpack_wsolve.h create mode 100644 src/maths/UMFPACK/Makefile.am create mode 100644 src/maths/UMFPACK/amd_1.c create mode 100644 src/maths/UMFPACK/amd_2.c create mode 100644 src/maths/UMFPACK/amd_aat.c create mode 100644 src/maths/UMFPACK/amd_control.c create mode 100644 src/maths/UMFPACK/amd_defaults.c create mode 100644 src/maths/UMFPACK/amd_dump.c create mode 100644 src/maths/UMFPACK/amd_global.c create mode 100644 src/maths/UMFPACK/amd_info.c create mode 100644 src/maths/UMFPACK/amd_internal.h create mode 100644 src/maths/UMFPACK/amd_order.c create mode 100644 src/maths/UMFPACK/amd_post_tree.c create mode 100644 src/maths/UMFPACK/amd_postorder.c create mode 100644 src/maths/UMFPACK/amd_preprocess.c create mode 100644 src/maths/UMFPACK/amd_valid.c create mode 100644 src/maths/UMFPACK/cholmod_blas.h create mode 100644 src/maths/UMFPACK/umf_analyze.c create mode 100644 src/maths/UMFPACK/umf_analyze.h create mode 100644 src/maths/UMFPACK/umf_apply_order.c create mode 100644 src/maths/UMFPACK/umf_apply_order.h create mode 100644 src/maths/UMFPACK/umf_assemble.c create mode 100644 src/maths/UMFPACK/umf_assemble.h create mode 100644 src/maths/UMFPACK/umf_blas3_update.c create mode 100644 src/maths/UMFPACK/umf_blas3_update.h create mode 100644 src/maths/UMFPACK/umf_build_tuples.c create mode 100644 src/maths/UMFPACK/umf_build_tuples.h create mode 100644 src/maths/UMFPACK/umf_cholmod.c create mode 100644 src/maths/UMFPACK/umf_cholmod.h create mode 100644 src/maths/UMFPACK/umf_colamd.c create mode 100644 src/maths/UMFPACK/umf_colamd.h create mode 100644 src/maths/UMFPACK/umf_config.h create mode 100644 src/maths/UMFPACK/umf_create_element.c create mode 100644 src/maths/UMFPACK/umf_create_element.h create mode 100644 src/maths/UMFPACK/umf_dump.c create mode 100644 src/maths/UMFPACK/umf_dump.h create mode 100644 src/maths/UMFPACK/umf_extend_front.c create mode 100644 src/maths/UMFPACK/umf_extend_front.h create mode 100644 src/maths/UMFPACK/umf_free.c create mode 100644 src/maths/UMFPACK/umf_free.h create mode 100644 src/maths/UMFPACK/umf_fsize.c create mode 100644 src/maths/UMFPACK/umf_fsize.h create mode 100644 src/maths/UMFPACK/umf_garbage_collection.c create mode 100644 src/maths/UMFPACK/umf_garbage_collection.h create mode 100644 src/maths/UMFPACK/umf_get_memory.c create mode 100644 src/maths/UMFPACK/umf_get_memory.h create mode 100644 src/maths/UMFPACK/umf_grow_front.c create mode 100644 src/maths/UMFPACK/umf_grow_front.h create mode 100644 src/maths/UMFPACK/umf_init_front.c create mode 100644 src/maths/UMFPACK/umf_init_front.h create mode 100644 src/maths/UMFPACK/umf_internal.h create mode 100644 src/maths/UMFPACK/umf_is_permutation.c create mode 100644 src/maths/UMFPACK/umf_is_permutation.h create mode 100644 src/maths/UMFPACK/umf_kernel.c create mode 100644 src/maths/UMFPACK/umf_kernel.h create mode 100644 src/maths/UMFPACK/umf_kernel_init.c create mode 100644 src/maths/UMFPACK/umf_kernel_init.h create mode 100644 src/maths/UMFPACK/umf_kernel_wrapup.c create mode 100644 src/maths/UMFPACK/umf_kernel_wrapup.h create mode 100644 src/maths/UMFPACK/umf_local_search.c create mode 100644 src/maths/UMFPACK/umf_local_search.h create mode 100644 src/maths/UMFPACK/umf_lsolve.c create mode 100644 src/maths/UMFPACK/umf_lsolve.h create mode 100644 src/maths/UMFPACK/umf_ltsolve.c create mode 100644 src/maths/UMFPACK/umf_ltsolve.h create mode 100644 src/maths/UMFPACK/umf_malloc.c create mode 100644 src/maths/UMFPACK/umf_malloc.h create mode 100644 src/maths/UMFPACK/umf_mem_alloc_element.c create mode 100644 src/maths/UMFPACK/umf_mem_alloc_element.h create mode 100644 src/maths/UMFPACK/umf_mem_alloc_head_block.c create mode 100644 src/maths/UMFPACK/umf_mem_alloc_head_block.h create mode 100644 src/maths/UMFPACK/umf_mem_alloc_tail_block.c create mode 100644 src/maths/UMFPACK/umf_mem_alloc_tail_block.h create mode 100644 src/maths/UMFPACK/umf_mem_free_tail_block.c create mode 100644 src/maths/UMFPACK/umf_mem_free_tail_block.h create mode 100644 src/maths/UMFPACK/umf_mem_init_memoryspace.c create mode 100644 src/maths/UMFPACK/umf_mem_init_memoryspace.h create mode 100644 src/maths/UMFPACK/umf_realloc.c create mode 100644 src/maths/UMFPACK/umf_realloc.h create mode 100644 src/maths/UMFPACK/umf_report_perm.c create mode 100644 src/maths/UMFPACK/umf_report_perm.h create mode 100644 src/maths/UMFPACK/umf_report_vector.c create mode 100644 src/maths/UMFPACK/umf_report_vector.h create mode 100644 src/maths/UMFPACK/umf_row_search.c create mode 100644 src/maths/UMFPACK/umf_row_search.h create mode 100644 src/maths/UMFPACK/umf_scale.c create mode 100644 src/maths/UMFPACK/umf_scale.h create mode 100644 src/maths/UMFPACK/umf_scale_column.c create mode 100644 src/maths/UMFPACK/umf_scale_column.h create mode 100644 src/maths/UMFPACK/umf_set_stats.c create mode 100644 src/maths/UMFPACK/umf_set_stats.h create mode 100644 src/maths/UMFPACK/umf_singletons.c create mode 100644 src/maths/UMFPACK/umf_singletons.h create mode 100644 src/maths/UMFPACK/umf_solve.c create mode 100644 src/maths/UMFPACK/umf_solve.h create mode 100644 src/maths/UMFPACK/umf_start_front.c create mode 100644 src/maths/UMFPACK/umf_start_front.h create mode 100644 src/maths/UMFPACK/umf_store_lu.c create mode 100644 src/maths/UMFPACK/umf_store_lu.h create mode 100644 src/maths/UMFPACK/umf_symbolic_usage.c create mode 100644 src/maths/UMFPACK/umf_symbolic_usage.h create mode 100644 src/maths/UMFPACK/umf_transpose.c create mode 100644 src/maths/UMFPACK/umf_transpose.h create mode 100644 src/maths/UMFPACK/umf_triplet.c create mode 100644 src/maths/UMFPACK/umf_triplet.h create mode 100644 src/maths/UMFPACK/umf_tuple_lengths.c create mode 100644 src/maths/UMFPACK/umf_tuple_lengths.h create mode 100644 src/maths/UMFPACK/umf_usolve.c create mode 100644 src/maths/UMFPACK/umf_usolve.h create mode 100644 src/maths/UMFPACK/umf_utsolve.c create mode 100644 src/maths/UMFPACK/umf_utsolve.h create mode 100644 src/maths/UMFPACK/umf_valid_numeric.c create mode 100644 src/maths/UMFPACK/umf_valid_numeric.h create mode 100644 src/maths/UMFPACK/umf_valid_symbolic.c create mode 100644 src/maths/UMFPACK/umf_valid_symbolic.h create mode 100644 src/maths/UMFPACK/umf_version.h create mode 100644 src/maths/UMFPACK/umfpack_col_to_triplet.c create mode 100644 src/maths/UMFPACK/umfpack_defaults.c create mode 100644 src/maths/UMFPACK/umfpack_free_numeric.c create mode 100644 src/maths/UMFPACK/umfpack_free_symbolic.c create mode 100644 src/maths/UMFPACK/umfpack_get_determinant.c create mode 100644 src/maths/UMFPACK/umfpack_get_lunz.c create mode 100644 src/maths/UMFPACK/umfpack_get_numeric.c create mode 100644 src/maths/UMFPACK/umfpack_get_symbolic.c create mode 100644 src/maths/UMFPACK/umfpack_global.c create mode 100644 src/maths/UMFPACK/umfpack_load_numeric.c create mode 100644 src/maths/UMFPACK/umfpack_load_symbolic.c create mode 100644 src/maths/UMFPACK/umfpack_numeric.c create mode 100644 src/maths/UMFPACK/umfpack_qsymbolic.c create mode 100644 src/maths/UMFPACK/umfpack_report_control.c create mode 100644 src/maths/UMFPACK/umfpack_report_info.c create mode 100644 src/maths/UMFPACK/umfpack_report_matrix.c create mode 100644 src/maths/UMFPACK/umfpack_report_numeric.c create mode 100644 src/maths/UMFPACK/umfpack_report_perm.c create mode 100644 src/maths/UMFPACK/umfpack_report_status.c create mode 100644 src/maths/UMFPACK/umfpack_report_symbolic.c create mode 100644 src/maths/UMFPACK/umfpack_report_triplet.c create mode 100644 src/maths/UMFPACK/umfpack_report_vector.c create mode 100644 src/maths/UMFPACK/umfpack_save_numeric.c create mode 100644 src/maths/UMFPACK/umfpack_save_symbolic.c create mode 100644 src/maths/UMFPACK/umfpack_scale.c create mode 100644 src/maths/UMFPACK/umfpack_solve.c create mode 100644 src/maths/UMFPACK/umfpack_symbolic.c create mode 100644 src/maths/UMFPACK/umfpack_tictoc.c create mode 100644 src/maths/UMFPACK/umfpack_timer.c create mode 100644 src/maths/UMFPACK/umfpack_transpose.c create mode 100644 src/maths/UMFPACK/umfpack_triplet_to_col.c create mode 100644 src/maths/UMFPACK/umfpacksmp.c diff --git a/src/include/ngspice/umfpack.h b/src/include/ngspice/umfpack.h new file mode 100644 index 000000000..c56e6cbef --- /dev/null +++ b/src/include/ngspice/umfpack.h @@ -0,0 +1,436 @@ +/* ========================================================================== */ +/* === umfpack.h ============================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 + +/* -------------------------------------------------------------------------- */ +/* Make it easy for C++ programs to include UMFPACK */ +/* -------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* define UF_long */ +#include "ngspice/UFconfig.h" + +/* -------------------------------------------------------------------------- */ +/* size of Info and Control arrays */ +/* -------------------------------------------------------------------------- */ + +/* These might be larger in future versions, since there are only 3 unused + * entries in Info, and no unused entries in Control. */ + +#define UMFPACK_INFO 90 +#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" +#include "umfpack_get_determinant.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" + +/* AMD */ +#include "ngspice/amd.h" + +/* global function pointers */ +#include "umfpack_global.h" + +/* -------------------------------------------------------------------------- */ +/* Version, copyright, and license */ +/* -------------------------------------------------------------------------- */ + +#define UMFPACK_VERSION "UMFPACK V5.5.2 (Dec 7, 2011)" + +#define UMFPACK_COPYRIGHT \ +"UMFPACK: Copyright (c) 2005-2011 by Timothy A. Davis. All Rights Reserved.\n" + +#define UMFPACK_LICENSE_PART1 \ +"\nUMFPACK License:\n" \ +"\n" \ +" UMFPACK is available under alternate licenses,\n" \ +" contact T. Davis for details.\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 library is free software; you can redistribute it and/or\n" \ +" modify it under the terms of the GNU General Public\n" \ +" License as published by the Free Software Foundation; either\n" \ +" version 2 of the License, or (at your option) any later version.\n" \ +"\n" \ +" This library is distributed in the hope that it will be useful,\n" \ +" but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \ +" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" \ +" General Public License for more details.\n" \ +"\n" \ +" You should have received a copy of the GNU General Public\n" \ +" License along with this library; if not, write to the Free Software\n" \ +" Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301\n" \ +" USA\n" \ + +#define UMFPACK_LICENSE_PART2 \ +"\n" \ +" Permission is hereby granted to use or copy this program under the\n" \ +" terms of the GNU GPL, provided that the Copyright, this License,\n" \ +" and the Availability of the original version is retained on all copies.\n" \ +" User documentation of any code that uses this code or any modified\n" \ +" version of this code must cite the Copyright, this License, the\n" \ +" Availability note, and \"Used by permission.\" Permission to modify\n" \ +" the code and to distribute modified code is granted, provided the\n" \ +" Copyright, this License, and the Availability note are retained,\n" \ +" and a notice that the code was modified is included.\n" + +#define UMFPACK_LICENSE_PART3 \ +"\n" \ +"Availability: http://www.cise.ufl.edu/research/sparse/umfpack\n" \ +"\n" + +/* UMFPACK Version 4.5 and later will include the following definitions. + * As an example, to test if the version you are using is 4.5 or later: + * + * #ifdef UMFPACK_VER + * if (UMFPACK_VER >= UMFPACK_VER_CODE (4,5)) ... + * #endif + * + * This also works during compile-time: + * + * #if defined(UMFPACK_VER) && (UMFPACK >= UMFPACK_VER_CODE (4,5)) + * printf ("This is version 4.5 or later\n") ; + * #else + * printf ("This is an early version\n") ; + * #endif + * + * Versions 4.4 and earlier of UMFPACK do not include a #define'd version + * number, although they do include the UMFPACK_VERSION string, defined + * above. + */ + +#define UMFPACK_DATE "Dec 7, 2011" +#define UMFPACK_VER_CODE(main,sub) ((main) * 1000 + (sub)) +#define UMFPACK_MAIN_VERSION 5 +#define UMFPACK_SUB_VERSION 5 +#define UMFPACK_SUBSUB_VERSION 2 +#define UMFPACK_VER UMFPACK_VER_CODE(UMFPACK_MAIN_VERSION,UMFPACK_SUB_VERSION) + +/* -------------------------------------------------------------------------- */ +/* contents of Info */ +/* -------------------------------------------------------------------------- */ + +/* Note that umfpack_report.m must coincide with these definitions. S is + * the submatrix of A after removing row/col singletons and empty rows/cols. */ + +/* 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 (UF_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 */ +#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 */ + +/* 51:55 unused */ + +/* statistcs for singleton pruning */ +#define UMFPACK_COL_SINGLETONS 56 /* # of column singletons */ +#define UMFPACK_ROW_SINGLETONS 57 /* # of row singletons */ +#define UMFPACK_N2 58 /* size of S */ +#define UMFPACK_S_SYMMETRIC 59 /* 1 if S square and symmetricly perm.*/ + +/* 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. */ + +/* -------------------------------------------------------------------------- */ +/* 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, or unsym. */ +#define UMFPACK_ORDERING 10 /* ordering method to use */ +#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 */ +#define UMFPACK_SINGLETONS 11 /* singleton filter on if true */ + +/* 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 */ + +/* 9,12: unused */ + +/* -------------------------------------------------------------------------- */ + +/* 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_OBSOLETE 2 /* 2-by-2 is no longer available */ +#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)) */ + +/* Control [UMFPACK_ORDERING] and Info [UMFPACK_ORDERING_USED] are one of: */ +#define UMFPACK_ORDERING_CHOLMOD 0 /* use CHOLMOD (AMD/COLAMD then METIS)*/ +#define UMFPACK_ORDERING_AMD 1 /* use AMD/COLAMD */ +#define UMFPACK_ORDERING_GIVEN 2 /* user-provided Qinit */ +#define UMFPACK_ORDERING_METIS 3 /* use METIS */ +#define UMFPACK_ORDERING_BEST 4 /* try many orderings, pick best */ +#define UMFPACK_ORDERING_NONE 5 /* natural ordering */ +#define UMFPACK_ORDERING_USER 6 /* user-provided function */ +/* AMD/COLAMD means: use AMD for symmetric strategy, COLAMD for unsymmetric */ + +/* -------------------------------------------------------------------------- */ +/* default values of Control: */ +/* -------------------------------------------------------------------------- */ + +#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_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 +#define UMFPACK_DEFAULT_ORDERING UMFPACK_ORDERING_AMD +#define UMFPACK_DEFAULT_SINGLETONS TRUE + +/* 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) + +/* The following warnings were added in umfpack_*_get_determinant */ +#define UMFPACK_WARNING_determinant_underflow (2) +#define UMFPACK_WARNING_determinant_overflow (3) + +/* 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) +#define UMFPACK_ERROR_different_pattern (-11) +#define UMFPACK_ERROR_invalid_system (-13) +#define UMFPACK_ERROR_invalid_permutation (-15) +#define UMFPACK_ERROR_internal_error (-911) /* yes, call me if you get this! */ +#define UMFPACK_ERROR_file_IO (-17) + +#define UMFPACK_ERROR_ordering_failed (-18) + +/* -------------------------------------------------------------------------- */ +/* 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. */ + +#ifdef __cplusplus +} +#endif + +#endif /* UMFPACK_H */ diff --git a/src/include/ngspice/umfpack_col_to_triplet.h b/src/include/ngspice/umfpack_col_to_triplet.h new file mode 100644 index 000000000..948a65ecf --- /dev/null +++ b/src/include/ngspice/umfpack_col_to_triplet.h @@ -0,0 +1,110 @@ +/* ========================================================================== */ +/* === umfpack_col_to_triplet =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_col_to_triplet +( + int n_col, + const int Ap [ ], + int Tj [ ] +) ; + +UF_long umfpack_dl_col_to_triplet +( + UF_long n_col, + const UF_long Ap [ ], + UF_long Tj [ ] +) ; + +int umfpack_zi_col_to_triplet +( + int n_col, + const int Ap [ ], + int Tj [ ] +) ; + +UF_long umfpack_zl_col_to_triplet +( + UF_long n_col, + const UF_long Ap [ ], + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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/src/include/ngspice/umfpack_defaults.h b/src/include/ngspice/umfpack_defaults.h new file mode 100644 index 000000000..a588d4f65 --- /dev/null +++ b/src/include/ngspice/umfpack_defaults.h @@ -0,0 +1,69 @@ +/* ========================================================================== */ +/* === umfpack_defaults ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 UF_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 UF_long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + umfpack_zl_defaults (Control) ; + +Purpose: + + Sets the default control parameter settings. + +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/src/include/ngspice/umfpack_free_numeric.h b/src/include/ngspice/umfpack_free_numeric.h new file mode 100644 index 000000000..986a6ab0d --- /dev/null +++ b/src/include/ngspice/umfpack_free_numeric.h @@ -0,0 +1,67 @@ +/* ========================================================================== */ +/* === umfpack_free_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 UF_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 UF_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/src/include/ngspice/umfpack_free_symbolic.h b/src/include/ngspice/umfpack_free_symbolic.h new file mode 100644 index 000000000..ad3aeb607 --- /dev/null +++ b/src/include/ngspice/umfpack_free_symbolic.h @@ -0,0 +1,67 @@ +/* ========================================================================== */ +/* === umfpack_free_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 UF_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 UF_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/src/include/ngspice/umfpack_get_determinant.h b/src/include/ngspice/umfpack_get_determinant.h new file mode 100644 index 000000000..702c1f17b --- /dev/null +++ b/src/include/ngspice/umfpack_get_determinant.h @@ -0,0 +1,196 @@ +/* ========================================================================== */ +/* === UMFPACK_get_determinant ============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* UMFPACK_get_determinant contributed by David Bateman, Motorola, Paris. */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_get_determinant +( + double *Mx, + double *Ex, + void *NumericHandle, + double User_Info [UMFPACK_INFO] +) ; + +UF_long umfpack_dl_get_determinant +( + double *Mx, + double *Ex, + void *NumericHandle, + double User_Info [UMFPACK_INFO] +) ; + +int umfpack_zi_get_determinant +( + double *Mx, + double *Mz, + double *Ex, + void *NumericHandle, + double User_Info [UMFPACK_INFO] +) ; + +UF_long umfpack_zl_get_determinant +( + double *Mx, + double *Mz, + double *Ex, + void *NumericHandle, + double User_Info [UMFPACK_INFO] +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status ; + double Mx, Ex, Info [UMFPACK_INFO] ; + status = umfpack_di_get_determinant (&Mx, &Ex, Numeric, Info) ; + +double UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_long status ; + double Mx, Ex, Info [UMFPACK_INFO] ; + status = umfpack_dl_get_determinant (&Mx, &Ex, Numeric, Info) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status ; + double Mx, Mz, Ex, Info [UMFPACK_INFO] ; + status = umfpack_zi_get_determinant (&Mx, &Mz, &Ex, Numeric, Info) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_long status ; + double *Mx, *Mz, *Ex, Info [UMFPACK_INFO] ; + status = umfpack_zl_get_determinant (&Mx, &Mz, &Ex, Numeric, Info) ; + +packed complex int Syntax: + + Same as above, except Mz is NULL. + +Author: Contributed by David Bateman, Motorola, Paris + +Purpose: + + Using the LU factors and the permutation vectors contained in the Numeric + object, calculate the determinant of the matrix A. + + The value of the determinant can be returned in two forms, depending on + whether Ex is NULL or not. If Ex is NULL then the value of the determinant + is returned on Mx and Mz for the real and imaginary parts. However, to + avoid over- or underflows, the determinant can be split into a mantissa + and exponent, and the parts returned separately, in which case Ex is not + NULL. The actual determinant is then given by + + double det ; + det = Mx * pow (10.0, Ex) ; + + for the double case, or + + double det [2] ; + det [0] = Mx * pow (10.0, Ex) ; // real part + det [1] = Mz * pow (10.0, Ex) ; // imaginary part + + for the complex case. Information on if the determinant will or has + over or under-flowed is given by Info [UMFPACK_STATUS]. + + In the "packed complex" syntax, Mx [0] holds the real part and Mx [1] + holds the imaginary part. Mz is not used (it is NULL). + +Returns: + + Returns UMFPACK_OK if sucessful. Returns UMFPACK_ERROR_out_of_memory if + insufficient memory is available for the n_row integer workspace that + umfpack_*_get_determinant allocates to construct pivots from the + permutation vectors. Returns UMFPACK_ERROR_invalid_Numeric_object if the + Numeric object provided as input is invalid. Returns + UMFPACK_WARNING_singular_matrix if the determinant is zero. Returns + UMFPACK_WARNING_determinant_underflow or + UMFPACK_WARNING_determinant_overflow if the determinant has underflowed + overflowed (for the case when Ex is NULL), or will overflow if Ex is not + NULL and det is computed (see above) in the user program. + +Arguments: + + double *Mx ; Output argument (array of size 1, or size 2 if Mz is NULL) + double *Mz ; Output argument (optional) + double *Ex ; Output argument (optional) + + The determinant returned in mantissa/exponent form, as discussed above. + If Mz is NULL, then both the original and imaginary parts will be + returned in Mx. If Ex is NULL then the determinant is returned directly + in Mx and Mz (or Mx [0] and Mx [1] if Mz is NULL), rather than in + mantissa/exponent form. + + void *Numeric ; Input argument, not modified. + + Numeric must point to a valid Numeric object, computed by + umfpack_*_numeric. + + double Info [UMFPACK_INFO] ; Output argument. + + Contains information about the calculation of the determinant. 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_*_determinant: + + Info [UMFPACK_STATUS]: status code. This is also the return value, + whether or not Info is present. + + UMFPACK_OK + + The determinant was successfully found. + + UMFPACK_ERROR_out_of_memory + + Insufficient memory to solve the linear system. + + UMFPACK_ERROR_argument_missing + + Mx is missing (NULL). + + UMFPACK_ERROR_invalid_Numeric_object + + The Numeric object is not valid. + + UMFPACK_ERROR_invalid_system + + The matrix is rectangular. Only square systems can be + handled. + + UMFPACK_WARNING_singluar_matrix + + The determinant is zero or NaN. The matrix is singular. + + UMFPACK_WARNING_determinant_underflow + + When passing from mantissa/exponent form to the determinant + an underflow has or will occur. If the mantissa/exponent from + of obtaining the determinant is used, the underflow will occur + in the user program. If the single argument method of + obtaining the determinant is used, the underflow has already + occurred. + + UMFPACK_WARNING_determinant_overflow + + When passing from mantissa/exponent form to the determinant + an overflow has or will occur. If the mantissa/exponent from + of obtaining the determinant is used, the overflow will occur + in the user program. If the single argument method of + obtaining the determinant is used, the overflow has already + occurred. + + +*/ diff --git a/src/include/ngspice/umfpack_get_lunz.h b/src/include/ngspice/umfpack_get_lunz.h new file mode 100644 index 000000000..512cffa97 --- /dev/null +++ b/src/include/ngspice/umfpack_get_lunz.h @@ -0,0 +1,137 @@ +/* ========================================================================== */ +/* === umfpack_get_lunz ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 +) ; + +UF_long umfpack_dl_get_lunz +( + UF_long *lnz, + UF_long *unz, + UF_long *n_row, + UF_long *n_col, + UF_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 +) ; + +UF_long umfpack_zl_get_lunz +( + UF_long *lnz, + UF_long *unz, + UF_long *n_row, + UF_long *n_col, + UF_long *nz_udiag, + void *Numeric +) ; + +/* +double int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status, lnz, unz, n_row, n_col, nz_udiag ; + status = umfpack_di_get_lunz (&lnz, &unz, &n_row, &n_col, &nz_udiag, + Numeric) ; + +double UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_long status, lnz, unz, n_row, n_col, nz_udiag ; + status = umfpack_dl_get_lunz (&lnz, &unz, &n_row, &n_col, &nz_udiag, + Numeric) ; + +complex int Syntax: + + #include "umfpack.h" + void *Numeric ; + int status, lnz, unz, n_row, n_col, nz_udiag ; + status = umfpack_zi_get_lunz (&lnz, &unz, &n_row, &n_col, &nz_udiag, + Numeric) ; + +complex UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_long status, lnz, unz, n_row, n_col, nz_udiag ; + status = umfpack_zl_get_lunz (&lnz, &unz, &n_row, &n_col, &nz_udiag, + 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/src/include/ngspice/umfpack_get_numeric.h b/src/include/ngspice/umfpack_get_numeric.h new file mode 100644 index 000000000..45ba0e621 --- /dev/null +++ b/src/include/ngspice/umfpack_get_numeric.h @@ -0,0 +1,254 @@ +/* ========================================================================== */ +/* === umfpack_get_numeric ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 +) ; + +UF_long umfpack_dl_get_numeric +( + UF_long Lp [ ], + UF_long Lj [ ], + double Lx [ ], + UF_long Up [ ], + UF_long Ui [ ], + double Ux [ ], + UF_long P [ ], + UF_long Q [ ], + double Dx [ ], + UF_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 +) ; + +UF_long umfpack_zl_get_numeric +( + UF_long Lp [ ], + UF_long Lj [ ], + double Lx [ ], double Lz [ ], + UF_long Up [ ], + UF_long Ui [ ], + double Ux [ ], double Uz [ ], + UF_long P [ ], + UF_long Q [ ], + double Dx [ ], double Dz [ ], + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_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) ; + +packed complex int/UF_long Syntax: + + Same as above, except Lz, Uz, and Dz are all NULL. + +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. + + 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. Size 2*lnz for packed complex case. + 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 Lx 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 split + complex case) are passed to umfpack_*_report_matrix (using the + "row" form). + + If Lx is present and Lz is NULL, then both real + and imaginary parts are 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. Size 2*unz for packed complex case. + 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 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 + split complex case) are passed to umfpack_*_report_matrix (using the + "column" form). + + If Ux is present and Uz is NULL, then both real + and imaginary parts are 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. Size 2*n for + the packed complex case. + 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. + + If Dx is present and Dz is NULL, then both real + and imaginary parts are 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. + + This argument defines how the scale factors Rs are to be interpretted. + + If do_recip is TRUE (one), 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 FALSE (zero). + + 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|). + + void *Numeric ; Input argument, not modified. + + Numeric must point to a valid Numeric object, computed by + umfpack_*_numeric. +*/ diff --git a/src/include/ngspice/umfpack_get_symbolic.h b/src/include/ngspice/umfpack_get_symbolic.h new file mode 100644 index 000000000..a17e9af54 --- /dev/null +++ b/src/include/ngspice/umfpack_get_symbolic.h @@ -0,0 +1,337 @@ +/* ========================================================================== */ +/* === umfpack_get_symbolic ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 +) ; + +UF_long umfpack_dl_get_symbolic +( + UF_long *n_row, + UF_long *n_col, + UF_long *n1, + UF_long *nz, + UF_long *nfr, + UF_long *nchains, + UF_long P [ ], + UF_long Q [ ], + UF_long Front_npivcol [ ], + UF_long Front_parent [ ], + UF_long Front_1strow [ ], + UF_long Front_leftmostdesc [ ], + UF_long Chain_start [ ], + UF_long Chain_maxrows [ ], + UF_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 +) ; + +UF_long umfpack_zl_get_symbolic +( + UF_long *n_row, + UF_long *n_col, + UF_long *n1, + UF_long *nz, + UF_long *nfr, + UF_long *nchains, + UF_long P [ ], + UF_long Q [ ], + UF_long Front_npivcol [ ], + UF_long Front_parent [ ], + UF_long Front_1strow [ ], + UF_long Front_leftmostdesc [ ], + UF_long Chain_start [ ], + UF_long Chain_maxrows [ ], + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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. + + 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. If no pivoting occurs during + numerical factorization, P [k] = i also defines the final permutation + of umfpack_*_numeric, for the symmetric strategy. + + 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 strategy is + 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. 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/src/include/ngspice/umfpack_global.h b/src/include/ngspice/umfpack_global.h new file mode 100644 index 000000000..51c64dd30 --- /dev/null +++ b/src/include/ngspice/umfpack_global.h @@ -0,0 +1,22 @@ +/* ========================================================================== */ +/* === umfpack_global ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* prototypes for global variables, and basic operators for complex values */ + +#ifndef EXTERN +#define EXTERN extern +#endif + +EXTERN double (*umfpack_hypot) (double, double) ; +EXTERN int (*umfpack_divcomplex) (double, double, double, double, double *, double *) ; + +double umf_hypot (double x, double y) ; +int umf_divcomplex (double, double, double, double, double *, double *) ; + diff --git a/src/include/ngspice/umfpack_load_numeric.h b/src/include/ngspice/umfpack_load_numeric.h new file mode 100644 index 000000000..0b84c9997 --- /dev/null +++ b/src/include/ngspice/umfpack_load_numeric.h @@ -0,0 +1,95 @@ +/* ========================================================================== */ +/* === umfpack_load_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_load_numeric +( + void **Numeric, + char *filename +) ; + +UF_long umfpack_dl_load_numeric +( + void **Numeric, + char *filename +) ; + +int umfpack_zi_load_numeric +( + void **Numeric, + char *filename +) ; + +UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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/src/include/ngspice/umfpack_load_symbolic.h b/src/include/ngspice/umfpack_load_symbolic.h new file mode 100644 index 000000000..8bd572894 --- /dev/null +++ b/src/include/ngspice/umfpack_load_symbolic.h @@ -0,0 +1,95 @@ +/* ========================================================================== */ +/* === umfpack_load_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_load_symbolic +( + void **Symbolic, + char *filename +) ; + +UF_long umfpack_dl_load_symbolic +( + void **Symbolic, + char *filename +) ; + +int umfpack_zi_load_symbolic +( + void **Symbolic, + char *filename +) ; + +UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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/src/include/ngspice/umfpack_numeric.h b/src/include/ngspice/umfpack_numeric.h new file mode 100644 index 000000000..5d8bb8dd2 --- /dev/null +++ b/src/include/ngspice/umfpack_numeric.h @@ -0,0 +1,543 @@ +/* ========================================================================== */ +/* === umfpack_numeric ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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] +) ; + +UF_long umfpack_dl_numeric +( + const UF_long Ap [ ], + const UF_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] +) ; + +UF_long umfpack_zl_numeric +( + const UF_long Ap [ ], + const UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Symbolic, *Numeric ; + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Symbolic, *Numeric ; + UF_long *Ap, *Ai, status ; + double *Ax, *Az, Control [UMFPACK_CONTROL], Info [UMFPACK_INFO] ; + status = umfpack_zl_numeric (Ap, Ai, Ax, Az, Symbolic, &Numeric, + Control, Info) ; + +packed complex Syntax: + + Same as above, except that Az is NULL. + +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]. + Size 2*nz for packed complex case. + + 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)]. + + If Az is NULL, then both real + and imaginary parts are 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. 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]: + If diagonal pivoting is attempted (the symmetric + strategy is 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]: 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]: + + 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 when UMFPACK is used + as a mexFunction MATLAB, since mxMalloc aborts the mexFunction if it + fails. This issue does not affect the use of UMFPACK as a part of + the built-in x=A\b in MATLAB 6.5 and later. + + 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]: + + 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]: + + 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). + + 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 strategy is used. + + 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/src/include/ngspice/umfpack_qsymbolic.h b/src/include/ngspice/umfpack_qsymbolic.h new file mode 100644 index 000000000..9aa6ff052 --- /dev/null +++ b/src/include/ngspice/umfpack_qsymbolic.h @@ -0,0 +1,234 @@ +/* ========================================================================== */ +/* === umfpack_qsymbolic ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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] +) ; + +UF_long umfpack_dl_qsymbolic +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_long Ai [ ], + const double Ax [ ], + const UF_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] +) ; + +UF_long umfpack_zl_qsymbolic +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_long Ai [ ], + const double Ax [ ], const double Az [ ], + const UF_long Qinit [ ], + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +int umfpack_di_fsymbolic +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], + + /* user-provided ordering function */ + int (*user_ordering) /* TRUE if OK, FALSE otherwise */ + ( + /* inputs, not modified on output */ + int, /* nrow */ + int, /* ncol */ + int, /* sym: if TRUE and nrow==ncol do A+A', else do A'A */ + int *, /* Ap, size ncol+1 */ + int *, /* Ai, size nz */ + /* output */ + int *, /* size ncol, fill-reducing permutation */ + /* input/output */ + void *, /* user_params (ignored by UMFPACK) */ + double * /* user_info[0..2], optional output for symmetric case. + user_info[0]: max column count for L=chol(A+A') + user_info[1]: nnz (L) + user_info[2]: flop count for chol(A+A'), if A real */ + ), + void *user_params, /* passed to user_ordering function */ + + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +UF_long umfpack_dl_fsymbolic +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_long Ai [ ], + const double Ax [ ], + + int (*user_ordering) (UF_long, UF_long, UF_long, + UF_long *, UF_long *, UF_long *, void *, double *), + void *user_params, + + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +int umfpack_zi_fsymbolic +( + int n_row, + int n_col, + const int Ap [ ], + const int Ai [ ], + const double Ax [ ], const double Az [ ], + + int (*user_ordering) (int, int, int, int *, int *, int *, void *, double *), + void *user_params, + + void **Symbolic, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) ; + +UF_long umfpack_zl_fsymbolic +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_long Ai [ ], + const double Ax [ ], const double Az [ ], + + int (*user_ordering) (UF_long, UF_long, UF_long, + UF_long *, UF_long *, UF_long *, void *, double *), + void *user_params, + + 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 UF_long Syntax: + + #include "umfpack.h" + void *Symbolic ; + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Symbolic ; + UF_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) ; + +packed complex Syntax: + + Same as above, except Az is NULL. + +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 + strategy will preserve Qinit, unless the matrix is structurally singular. + + See umfpack_*_symbolic for more information. Note that Ax and Ax are + optional. The may be NULL. + + *** 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. + + double Control [UMFPACK_CONTROL] ; Input argument, not modified. + + If Qinit is not NULL, then only two strategies are recognized: + the unsymmetric strategy and the symmetric strategy. + If Control [UMFPACK_STRATEGY] is UMFPACK_STRATEGY_SYMMETRIC, + then the symmetric strategy is used. Otherwise the unsymmetric + strategy is used. +*/ diff --git a/src/include/ngspice/umfpack_report_control.h b/src/include/ngspice/umfpack_report_control.h new file mode 100644 index 000000000..361a353d8 --- /dev/null +++ b/src/include/ngspice/umfpack_report_control.h @@ -0,0 +1,76 @@ +/* ========================================================================== */ +/* === umfpack_report_control =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 UF_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 UF_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/src/include/ngspice/umfpack_report_info.h b/src/include/ngspice/umfpack_report_info.h new file mode 100644 index 000000000..5822721eb --- /dev/null +++ b/src/include/ngspice/umfpack_report_info.h @@ -0,0 +1,86 @@ +/* ========================================================================== */ +/* === umfpack_report_info ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 UF_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 UF_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/src/include/ngspice/umfpack_report_matrix.h b/src/include/ngspice/umfpack_report_matrix.h new file mode 100644 index 000000000..0d17e2df3 --- /dev/null +++ b/src/include/ngspice/umfpack_report_matrix.h @@ -0,0 +1,203 @@ +/* ========================================================================== */ +/* === umfpack_report_matrix ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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] +) ; + +UF_long umfpack_dl_report_matrix +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_long Ai [ ], + const double Ax [ ], + UF_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] +) ; + +UF_long umfpack_zl_report_matrix +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_long Ai [ ], + const double Ax [ ], const double Az [ ], + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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) ; + +packed complex Syntax: + + Same as above, except Az is NULL. + +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]. + Size 2*nz for packed complex case. + + 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 + (see below if Az is NULL). + + 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 (see below if Az is NULL). + + No numerical values are printed if Ax is 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. + + If Az is NULL, then both real + and imaginary parts are 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/src/include/ngspice/umfpack_report_numeric.h b/src/include/ngspice/umfpack_report_numeric.h new file mode 100644 index 000000000..ff4eb117a --- /dev/null +++ b/src/include/ngspice/umfpack_report_numeric.h @@ -0,0 +1,112 @@ +/* ========================================================================== */ +/* === umfpack_report_numeric =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_numeric +( + void *Numeric, + const double Control [UMFPACK_CONTROL] +) ; + +UF_long umfpack_dl_report_numeric +( + void *Numeric, + const double Control [UMFPACK_CONTROL] +) ; + +int umfpack_zi_report_numeric +( + void *Numeric, + const double Control [UMFPACK_CONTROL] +) ; + +UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + double Control [UMFPACK_CONTROL] ; + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + double Control [UMFPACK_CONTROL] ; + UF_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/src/include/ngspice/umfpack_report_perm.h b/src/include/ngspice/umfpack_report_perm.h new file mode 100644 index 000000000..4810e32f7 --- /dev/null +++ b/src/include/ngspice/umfpack_report_perm.h @@ -0,0 +1,112 @@ +/* ========================================================================== */ +/* === umfpack_report_perm ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_perm +( + int np, + const int Perm [ ], + const double Control [UMFPACK_CONTROL] +) ; + +UF_long umfpack_dl_report_perm +( + UF_long np, + const UF_long Perm [ ], + const double Control [UMFPACK_CONTROL] +) ; + +int umfpack_zi_report_perm +( + int np, + const int Perm [ ], + const double Control [UMFPACK_CONTROL] +) ; + +UF_long umfpack_zl_report_perm +( + UF_long np, + const UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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/src/include/ngspice/umfpack_report_status.h b/src/include/ngspice/umfpack_report_status.h new file mode 100644 index 000000000..24376ae72 --- /dev/null +++ b/src/include/ngspice/umfpack_report_status.h @@ -0,0 +1,90 @@ +/* ========================================================================== */ +/* === umfpack_report_status ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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], + UF_long status +) ; + +void umfpack_zi_report_status +( + const double Control [UMFPACK_CONTROL], + int status +) ; + +void umfpack_zl_report_status +( + const double Control [UMFPACK_CONTROL], + UF_long status +) ; + +/* +double int Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + int status ; + umfpack_di_report_status (Control, status) ; + +double UF_long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + UF_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 UF_long Syntax: + + #include "umfpack.h" + double Control [UMFPACK_CONTROL] ; + UF_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 occurred + 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/src/include/ngspice/umfpack_report_symbolic.h b/src/include/ngspice/umfpack_report_symbolic.h new file mode 100644 index 000000000..eb6118283 --- /dev/null +++ b/src/include/ngspice/umfpack_report_symbolic.h @@ -0,0 +1,111 @@ +/* ========================================================================== */ +/* === umfpack_report_symbolic ============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_symbolic +( + void *Symbolic, + const double Control [UMFPACK_CONTROL] +) ; + +UF_long umfpack_dl_report_symbolic +( + void *Symbolic, + const double Control [UMFPACK_CONTROL] +) ; + +int umfpack_zi_report_symbolic +( + void *Symbolic, + const double Control [UMFPACK_CONTROL] +) ; + +UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Symbolic ; + double Control [UMFPACK_CONTROL] ; + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Symbolic ; + double Control [UMFPACK_CONTROL] ; + UF_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/src/include/ngspice/umfpack_report_triplet.h b/src/include/ngspice/umfpack_report_triplet.h new file mode 100644 index 000000000..4e7a605b8 --- /dev/null +++ b/src/include/ngspice/umfpack_report_triplet.h @@ -0,0 +1,153 @@ +/* ========================================================================== */ +/* === umfpack_report_triplet =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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] +) ; + +UF_long umfpack_dl_report_triplet +( + UF_long n_row, + UF_long n_col, + UF_long nz, + const UF_long Ti [ ], + const UF_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] +) ; + +UF_long umfpack_zl_report_triplet +( + UF_long n_row, + UF_long n_col, + UF_long nz, + const UF_long Ti [ ], + const UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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) ; + +packed complex Syntax: + + Same as above, except Tz is NULL. + +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. + Size 2*nz for packed complex case. + 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 is + not present ((double *) NULL), then the numerical values are + not printed. + + If Tx is present and Tz is NULL, then both real + and imaginary parts are 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/src/include/ngspice/umfpack_report_vector.h b/src/include/ngspice/umfpack_report_vector.h new file mode 100644 index 000000000..2c4c386e0 --- /dev/null +++ b/src/include/ngspice/umfpack_report_vector.h @@ -0,0 +1,133 @@ +/* ========================================================================== */ +/* === umfpack_report_vector ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_report_vector +( + int n, + const double X [ ], + const double Control [UMFPACK_CONTROL] +) ; + +UF_long umfpack_dl_report_vector +( + UF_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] +) ; + +UF_long umfpack_zl_report_vector +( + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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. + + Note that all complex routines in UMFPACK V4.4 and later 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 packed 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 Version 4.3, this routine was the only + one that allowed for packed complex 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. + + 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/src/include/ngspice/umfpack_save_numeric.h b/src/include/ngspice/umfpack_save_numeric.h new file mode 100644 index 000000000..708e5819a --- /dev/null +++ b/src/include/ngspice/umfpack_save_numeric.h @@ -0,0 +1,90 @@ +/* ========================================================================== */ +/* === umfpack_save_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_save_numeric +( + void *Numeric, + char *filename +) ; + +UF_long umfpack_dl_save_numeric +( + void *Numeric, + char *filename +) ; + +int umfpack_zi_save_numeric +( + void *Numeric, + char *filename +) ; + +UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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. + +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/src/include/ngspice/umfpack_save_symbolic.h b/src/include/ngspice/umfpack_save_symbolic.h new file mode 100644 index 000000000..2dffdd64e --- /dev/null +++ b/src/include/ngspice/umfpack_save_symbolic.h @@ -0,0 +1,90 @@ +/* ========================================================================== */ +/* === umfpack_save_symbolic================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_save_symbolic +( + void *Symbolic, + char *filename +) ; + +UF_long umfpack_dl_save_symbolic +( + void *Symbolic, + char *filename +) ; + +int umfpack_zi_save_symbolic +( + void *Symbolic, + char *filename +) ; + +UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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. + +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/src/include/ngspice/umfpack_scale.h b/src/include/ngspice/umfpack_scale.h new file mode 100644 index 000000000..62fc32a85 --- /dev/null +++ b/src/include/ngspice/umfpack_scale.h @@ -0,0 +1,112 @@ +/* ========================================================================== */ +/* === umfpack_scale ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +int umfpack_di_scale +( + double X [ ], + const double B [ ], + void *Numeric +) ; + +UF_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 +) ; + +UF_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 UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + double *Bx, *Bz, *Xx, *Xz ; + status = umfpack_zl_scale (Xx, Xz, Bx, Bz, Numeric) ; + +packed complex Syntax: + + Same as above, except both Xz and Bz are NULL. + +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 and Bx for the complex version). + +Arguments: + + double X [n_row] ; Output argument. + or: + double Xx [n_row] ; Output argument, real part. + Size 2*n_row for packed complex case. + double Xz [n_row] ; Output argument, imaginary part. + + The output vector X. If either Xz or Bz are NULL, the vector + X is in packed complex form, with the kth entry in Xx [2*k] and + Xx [2*k+1], and likewise for B. + + double B [n_row] ; Input argument, not modified. + or: + double Bx [n_row] ; Input argument, not modified, real part. + Size 2*n_row for packed complex case. + double Bz [n_row] ; Input argument, not modified, imaginary part. + + The input vector B. See above if either Xz or Bz are NULL. + + void *Numeric ; Input argument, not modified. + + Numeric must point to a valid Numeric object, computed by + umfpack_*_numeric. + +*/ diff --git a/src/include/ngspice/umfpack_solve.h b/src/include/ngspice/umfpack_solve.h new file mode 100644 index 000000000..09320914c --- /dev/null +++ b/src/include/ngspice/umfpack_solve.h @@ -0,0 +1,301 @@ +/* ========================================================================== */ +/* === umfpack_solve ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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] +) ; + +UF_long umfpack_dl_solve +( + UF_long sys, + const UF_long Ap [ ], + const UF_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] +) ; + +UF_long umfpack_zl_solve +( + UF_long sys, + const UF_long Ap [ ], + const UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_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) ; + +packed complex Syntax: + + Same as above, Xz, Bz, and Az are NULL. + +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). + + Int Ap [n+1] ; Input argument, not modified. + Int Ai [nz] ; Input argument, not modified. + double Ax [nz] ; Input argument, not modified. + Size 2*nz for packed complex case. + 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. + + If Az, Xz, or Bz are NULL, then both real + and imaginary parts are 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 + Size 2*n for packed complex case. + 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. + + If Az, Xz, or Bz are NULL, then both real + and imaginary parts are 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. + Size 2*n for packed complex case. + 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. + + If Az, Xz, or Bz are NULL, then both real + and imaginary parts are 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 occurred. 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 and Xx for the complex versions) arguments + are always required. Info and Control are not required. Ap, + Ai, Ax 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. + + 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/src/include/ngspice/umfpack_symbolic.h b/src/include/ngspice/umfpack_symbolic.h new file mode 100644 index 000000000..a061cd9b4 --- /dev/null +++ b/src/include/ngspice/umfpack_symbolic.h @@ -0,0 +1,517 @@ +/* ========================================================================== */ +/* === umfpack_symbolic ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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] +) ; + +UF_long umfpack_dl_symbolic +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_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] +) ; + +UF_long umfpack_zl_symbolic +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Symbolic ; + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Symbolic ; + UF_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) ; + +packed complex Syntax: + + Same as above, except Az is NULL. + +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, AMD or METIS) and a symbolic factorization. This is required + before the matrix can be numerically factorized with umfpack_*_numeric. + If you wish to bypass the COLAMD/AMD/METIS pre-ordering and provide your own + ordering, use umfpack_*_qsymbolic instead. If you wish to pass in a + pointer to a user-provided ordering function, use umfpack_*_fsymbolic. + + 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. May be NULL. + Size 2*nz for packed complex case. + + 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 for gathering statistics + about how many nonzeros are placed on the diagonal by the fill-reducing + ordering. + + double Az [nz] ; Optional input argument, not modified, for complex + versions. May be NULL. + + 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)]. + + If Az is NULL, then both real + and imaginary parts are 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 for statistics 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. 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. + + 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 + 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. + + Control [UMFPACK_ORDERING]: The ordering method to use: + UMFPACK_ORDERING_CHOLMOD try AMD/COLAMD, then METIS if needed + UMFPACK_ORDERING_AMD just AMD or COLAMD + UMFPACK_ORDERING_GIVEN just Qinit (umfpack_*_qsymbolic only) + UMFPACK_ORDERING_NONE no fill-reducing ordering + UMFPACK_ORDERING_METIS just METIS(A+A') or METIS(A'A) + UMFPACK_ORDERING_BEST try AMD/COLAMD, METIS, and NESDIS + UMFPACK_ORDERING_USER just user function (*_fsymbolic only) + + Control [UMFPACK_SINGLETONS]: If false (0), then singletons are + not removed prior to factorization. Default: true (1). + + 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. + Assuming the block size is large enough (8 or so), this parameter + has a modest effect on performance. + + 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). + + 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 UF_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 strategy is 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. + + Info [UMFPACK_SYMBOLIC_WALLTIME]: The wallclock time taken, in seconds. + + Info [UMFPACK_STRATEGY_USED]: The ordering strategy used: + UMFPACK_STRATEGY_SYMMETRIC or UMFPACK_STRATEGY_UNSYMMETRIC + + Info [UMFPACK_ORDERING_USED]: The ordering method used: + UMFPACK_ORDERING_AMD + UMFPACK_ORDERING_GIVEN + UMFPACK_ORDERING_NONE + UMFPACK_ORDERING_METIS + UMFPACK_ORDERING_USER + + 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 + strategy. + + 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. + + 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/src/include/ngspice/umfpack_tictoc.h b/src/include/ngspice/umfpack_tictoc.h new file mode 100644 index 000000000..da10426b7 --- /dev/null +++ b/src/include/ngspice/umfpack_tictoc.h @@ -0,0 +1,60 @@ +/* ========================================================================== */ +/* === umfpack_tictoc ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/include/ngspice/umfpack_timer.h b/src/include/ngspice/umfpack_timer.h new file mode 100644 index 000000000..413a1779c --- /dev/null +++ b/src/include/ngspice/umfpack_timer.h @@ -0,0 +1,39 @@ +/* ========================================================================== */ +/* === umfpack_timer ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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. + See umfpack_tic and umfpack_toc (the file umfpack_tictoc.h) for the timer + used internally by UMFPACK. + + 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/src/include/ngspice/umfpack_transpose.h b/src/include/ngspice/umfpack_transpose.h new file mode 100644 index 000000000..b12bb0bd8 --- /dev/null +++ b/src/include/ngspice/umfpack_transpose.h @@ -0,0 +1,216 @@ +/* ========================================================================== */ +/* === umfpack_transpose ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 [ ] +) ; + +UF_long umfpack_dl_transpose +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_long Ai [ ], + const double Ax [ ], + const UF_long P [ ], + const UF_long Q [ ], + UF_long Rp [ ], + UF_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 +) ; + +UF_long umfpack_zl_transpose +( + UF_long n_row, + UF_long n_col, + const UF_long Ap [ ], + const UF_long Ai [ ], + const double Ax [ ], const double Az [ ], + const UF_long P [ ], + const UF_long Q [ ], + UF_long Rp [ ], + UF_long Ri [ ], + double Rx [ ], double Rz [ ], + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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) ; + +packed complex Syntax: + + Same as above, except Az are Rz are NULL. + +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. + +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]. + Size 2*nz if Az or Rz are NULL. + 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. + This is not an error conditions; you are able to transpose + and permute just the pattern of a matrix. + + If Az or Rz are NULL, then both real + and imaginary parts are 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. + Size 2*nz if Az or Rz are NULL. + 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. + + If Az or Rz are NULL, then both real + and imaginary parts are 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 and Rx are present, then the linear + algebraic transpose is computed (complex conjugate). If false, the + array transpose is computed instead. +*/ diff --git a/src/include/ngspice/umfpack_triplet_to_col.h b/src/include/ngspice/umfpack_triplet_to_col.h new file mode 100644 index 000000000..cb45278e1 --- /dev/null +++ b/src/include/ngspice/umfpack_triplet_to_col.h @@ -0,0 +1,263 @@ +/* ========================================================================== */ +/* === umfpack_triplet_to_col =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 [ ] +) ; + +UF_long umfpack_dl_triplet_to_col +( + UF_long n_row, + UF_long n_col, + UF_long nz, + const UF_long Ti [ ], + const UF_long Tj [ ], + const double Tx [ ], + UF_long Ap [ ], + UF_long Ai [ ], + double Ax [ ], + UF_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 [ ] +) ; + +UF_long umfpack_zl_triplet_to_col +( + UF_long n_row, + UF_long n_col, + UF_long nz, + const UF_long Ti [ ], + const UF_long Tj [ ], + const double Tx [ ], const double Tz [ ], + UF_long Ap [ ], + UF_long Ai [ ], + double Ax [ ], double Az [ ], + UF_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 UF_long Syntax: + + #include "umfpack.h" + UF_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) ; + +UF_long Syntax: + + #include "umfpack.h" + UF_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) ; + +packed complex Syntax: + + Same as above, except Tz and Az are NULL. + +Purpose: + + Converts a sparse matrix from "triplet" form to compressed-column form. + Analogous to A = spconvert (Ti, Tj, Tx + Tz*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. + Size 2*nz if Tz or Az are NULL. + 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. Ax is computed only if both Ax and Tx are present + (not (double *) NULL). This is not error condition; the routine can + create just the pattern of the output matrix from the pattern of the + triplets. + + If Az or Tz are NULL, then both real + and imaginary parts are 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. Size 2*nz if Tz or Az are NULL. + 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. 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. + + If Az or Tz are NULL, then both real + and imaginary parts are 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 + split 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/src/include/ngspice/umfpack_wsolve.h b/src/include/ngspice/umfpack_wsolve.h new file mode 100644 index 000000000..38d15baee --- /dev/null +++ b/src/include/ngspice/umfpack_wsolve.h @@ -0,0 +1,172 @@ +/* ========================================================================== */ +/* === umfpack_wsolve ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 [ ] +) ; + +UF_long umfpack_dl_wsolve +( + UF_long sys, + const UF_long Ap [ ], + const UF_long Ai [ ], + const double Ax [ ], + double X [ ], + const double B [ ], + void *Numeric, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO], + UF_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 [ ] +) ; + +UF_long umfpack_zl_wsolve +( + UF_long sys, + const UF_long Ap [ ], + const UF_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], + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_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 UF_long Syntax: + + #include "umfpack.h" + void *Numeric ; + UF_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) ; + +packed complex Syntax: + + Same as above, except Az, Xz, and Bz are NULL. + +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. + Size 2*nz in packed complex case. + 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. + Size 2*n in packed complex case. + double Xz [n] ; Output argument, imaginary part + double Bx [n] ; Input argument, not modified, real part. + Size 2*n in packed complex case. + 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/src/maths/UMFPACK/Makefile.am b/src/maths/UMFPACK/Makefile.am new file mode 100644 index 000000000..fa406bb87 --- /dev/null +++ b/src/maths/UMFPACK/Makefile.am @@ -0,0 +1,352 @@ +## Process this file with automake to produce Makefile.in + +#THIS LIBRARY IS VERY COMPLICATED TO BUILD + +noinst_LTLIBRARIES = \ + libUMFPACK_AMD.la \ + libUMFPACK_global.la \ + libUMFPACK_CONJ_DINT.la \ + libUMFPACK_DOMAP_DINT.la \ + libUMFPACK_DOVALUES_DINT.la \ + libUMFPACK_DOMAP_DOVALUES_DINT.la \ + libUMFPACK_DFIXQ_DINT.la \ + libUMFPACK_DDROP_DINT.la \ + libUMFPACK_DWSOLVE_DINT.la \ + libUMFPACK_DINT.la \ + libUMFPACK_ZINT.la \ + libUMFPACK_CONJ_ZINT.la \ + libUMFPACK_DOMAP_ZINT.la \ + libUMFPACK_DOVALUES_ZINT.la \ + libUMFPACK_DOMAP_DOVALUES_ZINT.la \ + libUMFPACK_DFIXQ_ZINT.la \ + libUMFPACK_DDROP_ZINT.la \ + libUMFPACK_DWSOLVE_ZINT.la \ + libUMFPACK.la + + + + +libUMFPACK_AMD_la_SOURCES = \ + amd_1.c \ + amd_2.c \ + amd_aat.c \ + amd_control.c \ + amd_defaults.c \ + amd_dump.c \ + amd_global.c \ + amd_info.c \ + amd_order.c \ + amd_postorder.c \ + amd_post_tree.c \ + amd_preprocess.c \ + amd_valid.c + +libUMFPACK_AMD_la_CPPFLAGS = -I$(top_srcdir)/src/include + + +libUMFPACK_global_la_SOURCES = \ + umfpack_global.c \ + umfpack_tictoc.c \ + umfpack_timer.c \ + umf_triplet.c + +libUMFPACK_global_la_CPPFLAGS = -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_global_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_CONJ_DINT_la_SOURCES = \ + umf_ltsolve.c \ + umf_utsolve.c + +libUMFPACK_CONJ_DINT_la_CPPFLAGS = -DDINT -DCONJUGATE_SOLVE -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_CONJ_DINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DOMAP_DINT_la_SOURCES = \ + umf_triplet.c + +libUMFPACK_DOMAP_DINT_la_CPPFLAGS = -DDINT -DDO_MAP -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DOMAP_DINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DOVALUES_DINT_la_SOURCES = \ + umf_triplet.c + +libUMFPACK_DOVALUES_DINT_la_CPPFLAGS = -DDINT -DDO_VALUES -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DOVALUES_DINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DOMAP_DOVALUES_DINT_la_SOURCES = \ + umf_triplet.c + +libUMFPACK_DOMAP_DOVALUES_DINT_la_CPPFLAGS = -DDINT -DDO_MAP -DDO_VALUES -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DOMAP_DOVALUES_DINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DFIXQ_DINT_la_SOURCES = \ + umf_assemble.c + +libUMFPACK_DFIXQ_DINT_la_CPPFLAGS = -DDINT -DFIXQ -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DFIXQ_DINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DDROP_DINT_la_SOURCES = \ + umf_store_lu.c + +libUMFPACK_DDROP_DINT_la_CPPFLAGS = -DDINT -DDROP -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DDROP_DINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DWSOLVE_DINT_la_SOURCES = \ + umfpack_solve.c + +libUMFPACK_DWSOLVE_DINT_la_CPPFLAGS = -DDINT -DWSOLVE -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DWSOLVE_DINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DINT_la_SOURCES = \ + umf_analyze.c \ + umf_apply_order.c \ + umf_assemble.c \ + umf_blas3_update.c \ + umf_build_tuples.c \ + umf_cholmod.c \ + umf_colamd.c \ + umf_create_element.c \ + umf_dump.c \ + umf_extend_front.c \ + umf_free.c \ + umf_fsize.c \ + umf_garbage_collection.c \ + umf_get_memory.c \ + umf_grow_front.c \ + umf_init_front.c \ + umf_is_permutation.c \ + umf_kernel.c \ + umf_kernel_init.c \ + umf_kernel_wrapup.c \ + umf_local_search.c \ + umf_lsolve.c \ + umf_ltsolve.c \ + umf_malloc.c \ + umf_mem_alloc_element.c \ + umf_mem_alloc_head_block.c \ + umf_mem_alloc_tail_block.c \ + umf_mem_free_tail_block.c \ + umf_mem_init_memoryspace.c \ + umfpack_col_to_triplet.c \ + umfpack_defaults.c \ + umfpack_free_numeric.c \ + umfpack_free_symbolic.c \ + umfpack_get_determinant.c \ + umfpack_get_lunz.c \ + umfpack_get_numeric.c \ + umfpack_get_symbolic.c \ + umfpack_load_numeric.c \ + umfpack_load_symbolic.c \ + umfpack_numeric.c \ + umfpack_qsymbolic.c \ + umfpack_report_control.c \ + umfpack_report_info.c \ + umfpack_report_matrix.c \ + umfpack_report_numeric.c \ + umfpack_report_perm.c \ + umfpack_report_status.c \ + umfpack_report_symbolic.c \ + umfpack_report_triplet.c \ + umfpack_report_vector.c \ + umfpack_save_numeric.c \ + umfpack_save_symbolic.c \ + umfpack_scale.c \ + umfpack_solve.c \ + umfpack_symbolic.c \ + umfpack_transpose.c \ + umfpack_triplet_to_col.c \ + umf_realloc.c \ + umf_report_perm.c \ + umf_report_vector.c \ + umf_row_search.c \ + umf_scale.c \ + umf_scale_column.c \ + umf_set_stats.c \ + umf_singletons.c \ + umf_solve.c \ + umf_start_front.c \ + umf_store_lu.c \ + umf_symbolic_usage.c \ + umf_transpose.c \ + umf_tuple_lengths.c \ + umf_usolve.c \ + umf_utsolve.c \ + umf_valid_numeric.c \ + umf_valid_symbolic.c + +libUMFPACK_DINT_la_CPPFLAGS = -DDINT -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + + + +libUMFPACK_ZINT_la_SOURCES = \ + umf_assemble.c \ + umf_blas3_update.c \ + umf_build_tuples.c \ + umf_create_element.c \ + umf_dump.c \ + umf_extend_front.c \ + umf_garbage_collection.c \ + umf_get_memory.c \ + umf_init_front.c \ + umf_is_permutation.c \ + umf_kernel.c \ + umf_kernel_init.c \ + umf_kernel_wrapup.c \ + umf_local_search.c \ + umf_lsolve.c \ + umf_ltsolve.c \ + umf_mem_alloc_element.c \ + umf_mem_alloc_head_block.c \ + umf_mem_alloc_tail_block.c \ + umf_mem_free_tail_block.c \ + umf_mem_init_memoryspace.c \ + umfpack_col_to_triplet.c \ + umfpack_defaults.c \ + umfpack_free_numeric.c \ + umfpack_free_symbolic.c \ + umfpack_get_determinant.c \ + umfpack_get_lunz.c \ + umfpack_get_numeric.c \ + umfpack_get_symbolic.c \ + umfpack_load_numeric.c \ + umfpack_load_symbolic.c \ + umfpack_numeric.c \ + umfpack_qsymbolic.c \ + umfpack_report_control.c \ + umfpack_report_info.c \ + umfpack_report_matrix.c \ + umfpack_report_numeric.c \ + umfpack_report_perm.c \ + umfpack_report_status.c \ + umfpack_report_symbolic.c \ + umfpack_report_triplet.c \ + umfpack_report_vector.c \ + umfpack_save_numeric.c \ + umfpack_save_symbolic.c \ + umfpack_scale.c \ + umfpack_solve.c \ + umfpack_symbolic.c \ + umfpack_transpose.c \ + umfpack_triplet_to_col.c \ + umf_report_vector.c \ + umf_row_search.c \ + umf_scale.c \ + umf_scale_column.c \ + umf_set_stats.c \ + umf_solve.c \ + umf_start_front.c \ + umf_store_lu.c \ + umf_symbolic_usage.c \ + umf_transpose.c \ + umf_tuple_lengths.c \ + umf_usolve.c \ + umf_utsolve.c \ + umf_valid_numeric.c \ + umf_valid_symbolic.c + +libUMFPACK_ZINT_la_CPPFLAGS = -DZINT -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_ZINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_CONJ_ZINT_la_SOURCES = \ + umf_ltsolve.c \ + umf_utsolve.c + +libUMFPACK_CONJ_ZINT_la_CPPFLAGS = -DZINT -DCONJUGATE_SOLVE -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_CONJ_ZINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DOMAP_ZINT_la_SOURCES = \ + umf_triplet.c + +libUMFPACK_DOMAP_ZINT_la_CPPFLAGS = -DZINT -DDO_MAP -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DOMAP_ZINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DOVALUES_ZINT_la_SOURCES = \ + umf_triplet.c + +libUMFPACK_DOVALUES_ZINT_la_CPPFLAGS = -DZINT -DDO_VALUES -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DOVALUES_ZINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DOMAP_DOVALUES_ZINT_la_SOURCES = \ + umf_triplet.c + +libUMFPACK_DOMAP_DOVALUES_ZINT_la_CPPFLAGS = -DZINT -DDO_MAP -DDO_VALUES -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DOMAP_DOVALUES_ZINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DFIXQ_ZINT_la_SOURCES = \ + umf_assemble.c + +libUMFPACK_DFIXQ_ZINT_la_CPPFLAGS = -DZINT -DFIXQ -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DFIXQ_ZINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DDROP_ZINT_la_SOURCES = \ + umf_store_lu.c + +libUMFPACK_DDROP_ZINT_la_CPPFLAGS = -DZINT -DDROP -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DDROP_ZINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + +libUMFPACK_DWSOLVE_ZINT_la_SOURCES = \ + umfpack_solve.c + +libUMFPACK_DWSOLVE_ZINT_la_CPPFLAGS = -DZINT -DWSOLVE -DNCHOLMOD -I$(top_srcdir)/src/include + +libUMFPACK_DWSOLVE_ZINT_la_LDFLAGS = -L/usr/lib/atlas-corei7sse3 -lf77blas -latlas + + + + +libUMFPACK_la_SOURCES = \ + umfpacksmp.c + +libUMFPACK_la_LIBADD = \ + libUMFPACK_AMD.la \ + libUMFPACK_global.la \ + libUMFPACK_CONJ_DINT.la \ + libUMFPACK_DOMAP_DINT.la \ + libUMFPACK_DOVALUES_DINT.la \ + libUMFPACK_DOMAP_DOVALUES_DINT.la \ + libUMFPACK_DFIXQ_DINT.la \ + libUMFPACK_DDROP_DINT.la \ + libUMFPACK_DWSOLVE_DINT.la \ + libUMFPACK_DINT.la \ + libUMFPACK_ZINT.la \ + libUMFPACK_CONJ_ZINT.la \ + libUMFPACK_DOMAP_ZINT.la \ + libUMFPACK_DOVALUES_ZINT.la \ + libUMFPACK_DOMAP_DOVALUES_ZINT.la \ + libUMFPACK_DFIXQ_ZINT.la \ + libUMFPACK_DDROP_ZINT.la \ + libUMFPACK_DWSOLVE_ZINT.la + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/maths/UMFPACK/amd_1.c b/src/maths/UMFPACK/amd_1.c new file mode 100644 index 000000000..30cb27724 --- /dev/null +++ b/src/maths/UMFPACK/amd_1.c @@ -0,0 +1,181 @@ +/* ========================================================================= */ +/* === AMD_1 =============================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +/* AMD_1: Construct A+A' for a sparse matrix A and perform the AMD ordering. + * + * The n-by-n sparse matrix A can be unsymmetric. It is stored in MATLAB-style + * compressed-column form, with sorted row indices in each column, and no + * duplicate entries. Diagonal entries may be present, but they are ignored. + * Row indices of column j of A are stored in Ai [Ap [j] ... Ap [j+1]-1]. + * Ap [0] must be zero, and nz = Ap [n] is the number of entries in A. The + * size of the matrix, n, must be greater than or equal to zero. + * + * This routine must be preceded by a call to AMD_aat, which computes the + * number of entries in each row/column in A+A', excluding the diagonal. + * Len [j], on input, is the number of entries in row/column j of A+A'. This + * routine constructs the matrix A+A' and then calls AMD_2. No error checking + * is performed (this was done in AMD_valid). + */ + +#include "amd_internal.h" + +GLOBAL void AMD_1 +( + Int n, /* n > 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) == AMD_OK) ; + + /* 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/src/maths/UMFPACK/amd_2.c b/src/maths/UMFPACK/amd_2.c new file mode 100644 index 000000000..97b4f7a36 --- /dev/null +++ b/src/maths/UMFPACK/amd_2.c @@ -0,0 +1,1842 @@ +/* ========================================================================= */ +/* === AMD_2 =============================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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" + +/* ========================================================================= */ +/* === clear_flag ========================================================== */ +/* ========================================================================= */ + +static Int clear_flag (Int wflg, Int wbig, Int W [ ], Int n) +{ + Int x ; + if (wflg < 2 || 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 */ + return (wflg) ; +} + + +/* ========================================================================= */ +/* === AMD_2 =============================================================== */ +/* ========================================================================= */ + +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 UF_long versions are provided. In the descriptions below + * and integer is and int or UF_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, 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, UF_long_max - n for the + * UF_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 ; + + mindeg = 0 ; + ncmpa = 0 ; + nel = 0 ; + lemax = 0 ; + + /* 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 ; + } + /* Note: if alpha is NaN, this is undefined: */ + if (alpha < 0) + { + /* only remove completely dense rows/columns */ + dense = n-2 ; + } + else + { + dense = alpha * sqrt ((double) n) ; + } + dense = MAX (16, dense) ; + dense = MIN (n, dense) ; + AMD_DEBUG1 (("\n\nAMD (debug), alpha %g, aggr. "ID"\n", + alpha, aggressive)) ; + + for (i = 0 ; i < n ; i++) + { + Last [i] = EMPTY ; + Head [i] = EMPTY ; + Next [i] = EMPTY ; + /* if separate 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" initial\n", nel)) ; + AMD_dump (n, Pe, Iw, Len, iwlen, pfree, Nv, Next, Last, + Head, Elen, Degree, W, -1) ; +#endif + + /* initialize wflg */ + wbig = Int_MAX - n ; + wflg = clear_flag (0, wbig, W, n) ; + + /* --------------------------------------------------------------------- */ + /* initialize degree lists and eliminate dense and empty rows */ + /* --------------------------------------------------------------------- */ + + ndense = 0 ; + + 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 */ + + wflg = clear_flag (wflg, wbig, W, n) ; + +/* ========================================================================= */ +/* 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] + Len [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 separate 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 ; + wflg = clear_flag (wflg, wbig, W, n) ; + /* 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 separate 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) ; + /* skip 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 ; + } + +/* ========================================================================= */ +/* POST-ORDERING */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- + * 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.) + * ------------------------------------------------------------------------- */ + + /* restore Pe */ + for (i = 0 ; i < n ; i++) + { + Pe [i] = FLIP (Pe [i]) ; + } + + /* restore Elen, for output information, and for postordering */ + for (i = 0 ; i < n ; 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/src/maths/UMFPACK/amd_aat.c b/src/maths/UMFPACK/amd_aat.c new file mode 100644 index 000000000..4f02b755f --- /dev/null +++ b/src/maths/UMFPACK/amd_aat.c @@ -0,0 +1,185 @@ +/* ========================================================================= */ +/* === AMD_aat ============================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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). Assumes the input + * matrix has no errors, with sorted columns and no duplicates + * (AMD_valid (n, n, Ap, Ai) must be AMD_OK, but this condition is not + * checked). + */ + +#include "amd_internal.h" + +GLOBAL size_t 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 ; + double sym ; + size_t nzaat ; + +#ifndef NDEBUG + AMD_debug_init ("AMD AAT") ; + for (k = 0 ; k < n ; k++) Tp [k] = EMPTY ; + ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ; +#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 = (2 * (double) 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) = %g\n", + (double) 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/src/maths/UMFPACK/amd_control.c b/src/maths/UMFPACK/amd_control.c new file mode 100644 index 000000000..c2aec9f0b --- /dev/null +++ b/src/maths/UMFPACK/amd_control.c @@ -0,0 +1,64 @@ +/* ========================================================================= */ +/* === AMD_control ========================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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 version %d.%d.%d, %s: approximate minimum degree ordering\n" + " dense row parameter: %g\n", AMD_MAIN_VERSION, AMD_SUB_VERSION, + AMD_SUBSUB_VERSION, AMD_DATE, 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")) ; + } + else + { + PRINTF ((" aggressive absorption: no\n")) ; + } + + PRINTF ((" size of AMD integer: %d\n\n", sizeof (Int))) ; +} diff --git a/src/maths/UMFPACK/amd_defaults.c b/src/maths/UMFPACK/amd_defaults.c new file mode 100644 index 000000000..ffe3f4bd5 --- /dev/null +++ b/src/maths/UMFPACK/amd_defaults.c @@ -0,0 +1,38 @@ +/* ========================================================================= */ +/* === AMD_defaults ======================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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" + +/* ========================================================================= */ +/* === AMD defaults ======================================================== */ +/* ========================================================================= */ + +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/src/maths/UMFPACK/amd_dump.c b/src/maths/UMFPACK/amd_dump.c new file mode 100644 index 000000000..89d67b875 --- /dev/null +++ b/src/maths/UMFPACK/amd_dump.c @@ -0,0 +1,180 @@ +/* ========================================================================= */ +/* === AMD_dump ============================================================ */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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) ; + AMD_DEBUG3 (("\nAMD dump, pfree: "ID"\n", pfree)) ; + 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 + len <= 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 + len <= 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": \n", 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/src/maths/UMFPACK/amd_global.c b/src/maths/UMFPACK/amd_global.c new file mode 100644 index 000000000..93f2b4518 --- /dev/null +++ b/src/maths/UMFPACK/amd_global.c @@ -0,0 +1,84 @@ +/* ========================================================================= */ +/* === amd_global ========================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/amd */ +/* ------------------------------------------------------------------------- */ + +#include + +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#include "matrix.h" +#endif + +#ifndef NULL +#define NULL 0 +#endif + +/* ========================================================================= */ +/* === Default AMD memory manager ========================================== */ +/* ========================================================================= */ + +/* The user can redefine these global pointers at run-time to change the memory + * manager used by AMD. AMD only uses malloc and free; realloc and calloc are + * include for completeness, in case another package wants to use the same + * memory manager as AMD. + * + * If compiling as a MATLAB mexFunction, the default memory manager is mxMalloc. + * You can also compile AMD as a standard ANSI-C library and link a mexFunction + * against it, and then redefine these pointers at run-time, in your + * mexFunction. + * + * If -DNMALLOC is defined at compile-time, no memory manager is specified at + * compile-time. You must then define these functions at run-time, before + * calling AMD, for AMD to work properly. + */ + +#ifndef NMALLOC +#ifdef MATLAB_MEX_FILE +/* MATLAB mexFunction: */ +void *(*amd_malloc) (size_t) = mxMalloc ; +void (*amd_free) (void *) = mxFree ; +void *(*amd_realloc) (void *, size_t) = mxRealloc ; +void *(*amd_calloc) (size_t, size_t) = mxCalloc ; +#else +/* standard ANSI-C: */ +void *(*amd_malloc) (size_t) = malloc ; +void (*amd_free) (void *) = free ; +void *(*amd_realloc) (void *, size_t) = realloc ; +void *(*amd_calloc) (size_t, size_t) = calloc ; +#endif +#else +/* no memory manager defined at compile-time; you MUST define one at run-time */ +void *(*amd_malloc) (size_t) = NULL ; +void (*amd_free) (void *) = NULL ; +void *(*amd_realloc) (void *, size_t) = NULL ; +void *(*amd_calloc) (size_t, size_t) = NULL ; +#endif + +/* ========================================================================= */ +/* === Default AMD printf routine ========================================== */ +/* ========================================================================= */ + +/* The user can redefine this global pointer at run-time to change the printf + * routine used by AMD. If NULL, no printing occurs. + * + * If -DNPRINT is defined at compile-time, stdio.h is not included. Printing + * can then be enabled at run-time by setting amd_printf to a non-NULL function. + */ + +#ifndef NPRINT +#ifdef MATLAB_MEX_FILE +int (*amd_printf) (const char *, ...) = mexPrintf ; +#else +#include +int (*amd_printf) (const char *, ...) = printf ; +#endif +#else +int (*amd_printf) (const char *, ...) = NULL ; +#endif diff --git a/src/maths/UMFPACK/amd_info.c b/src/maths/UMFPACK/amd_info.c new file mode 100644 index 000000000..0a842adf0 --- /dev/null +++ b/src/maths/UMFPACK/amd_info.c @@ -0,0 +1,120 @@ +/* ========================================================================= */ +/* === AMD_info ============================================================ */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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 ; + + PRINTF (("\nAMD version %d.%d.%d, %s, results:\n", + AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION, AMD_DATE)) ; + + 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 ((" 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 if (Info [AMD_STATUS] == AMD_OK_BUT_JUMBLED) + { + PRINTF (("OK, but jumbled\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/src/maths/UMFPACK/amd_internal.h b/src/maths/UMFPACK/amd_internal.h new file mode 100644 index 000000000..32ff0b4a9 --- /dev/null +++ b/src/maths/UMFPACK/amd_internal.h @@ -0,0 +1,350 @@ +/* ========================================================================= */ +/* === amd_internal.h ====================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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 (it is included in UMFPACK, however). All others + * should use amd.h instead. + * + * The following compile-time definitions affect how AMD is compiled. + * + * -DNPRINT + * + * Disable all printing. stdio.h will not be included. Printing can + * be re-enabled at run-time by setting the global pointer amd_printf + * to printf (or mexPrintf for a MATLAB mexFunction). + * + * -DNMALLOC + * + * No memory manager is defined at compile-time. You MUST define the + * function pointers amd_malloc, amd_free, amd_realloc, and + * amd_calloc at run-time for AMD to work properly. + */ + +/* ========================================================================= */ +/* === NDEBUG ============================================================== */ +/* ========================================================================= */ + +/* + * Turning on debugging takes some work (see below). If you do not edit this + * file, then debugging is always turned off, regardless of whether or not + * -DNDEBUG is specified in your compiler options. + * + * If AMD is being compiled as a mexFunction, then MATLAB_MEX_FILE is defined, + * and mxAssert is used instead of assert. If debugging is not enabled, no + * MATLAB include files or functions are used. Thus, the AMD library libamd.a + * can be safely used in either a stand-alone C program or in another + * mexFunction, without any change. + */ + +/* + 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: size_t, malloc, free, realloc, and calloc */ +#include + +#if !defined(NPRINT) || !defined(NDEBUG) +/* from stdio.h: printf. Not included if NPRINT is defined at compile time. + * fopen and fscanf are used when debugging. */ +#include +#endif + +/* 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 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 + +/* largest value of size_t */ +#ifndef SIZE_T_MAX +#define SIZE_T_MAX ((size_t) (-1)) +#endif + +/* ------------------------------------------------------------------------- */ +/* integer type for AMD: int or UF_long */ +/* ------------------------------------------------------------------------- */ + +/* define UF_long */ +#include + +#if defined (DLONG) || defined (ZLONG) + +#define Int UF_long +#define ID UF_long_id +#define Int_MAX UF_long_max + +#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_preprocess amd_l_preprocess + +#else + +#define Int int +#define ID "%d" +#define Int_MAX INT_MAX + +#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_preprocess amd_preprocess + +#endif + +/* ========================================================================= */ +/* === PRINTF macro ======================================================== */ +/* ========================================================================= */ + +/* All output goes through the PRINTF macro. */ +#define PRINTF(params) { if (amd_printf != NULL) (void) amd_printf params ; } + +/* ------------------------------------------------------------------------- */ +/* AMD routine definitions (user-callable) */ +/* ------------------------------------------------------------------------- */ + +#include + +/* ------------------------------------------------------------------------- */ +/* AMD routine definitions (not user-callable) */ +/* ------------------------------------------------------------------------- */ + +GLOBAL size_t 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_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_preprocess +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + Int Rp [ ], + Int Ri [ ], + Int W [ ], + Int Flag [ ] +) ; + +/* ------------------------------------------------------------------------- */ +/* debugging definitions */ +/* ------------------------------------------------------------------------- */ + +#ifndef NDEBUG + +/* from assert.h: assert macro */ +#include + +#ifndef EXTERN +#define EXTERN extern +#endif + +EXTERN 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 + +/* Use mxAssert if AMD is compiled into a mexFunction */ +#ifdef MATLAB_MEX_FILE +#define ASSERT(expression) (mxAssert ((expression), "")) +#else +#define ASSERT(expression) (assert (expression)) +#endif + +#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 + +/* no debugging */ +#define ASSERT(expression) +#define AMD_DEBUG0(params) +#define AMD_DEBUG1(params) +#define AMD_DEBUG2(params) +#define AMD_DEBUG3(params) +#define AMD_DEBUG4(params) + +#endif diff --git a/src/maths/UMFPACK/amd_order.c b/src/maths/UMFPACK/amd_order.c new file mode 100644 index 000000000..d3f6853d1 --- /dev/null +++ b/src/maths/UMFPACK/amd_order.c @@ -0,0 +1,200 @@ +/* ========================================================================= */ +/* === AMD_order =========================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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" + +/* ========================================================================= */ +/* === AMD_order =========================================================== */ +/* ========================================================================= */ + +GLOBAL Int AMD_order +( + Int n, + const Int Ap [ ], + const Int Ai [ ], + Int P [ ], + double Control [ ], + double Info [ ] +) +{ + Int *Len, *S, nz, i, *Pinv, info, status, *Rp, *Ri, *Cp, *Ci, ok ; + size_t nzaat, slen ; + double mem = 0 ; + +#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) ; + } + + /* check if n or nz will cause size_t overflow */ + if (((size_t) n) >= SIZE_T_MAX / sizeof (Int) + || ((size_t) nz) >= SIZE_T_MAX / sizeof (Int)) + { + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; /* problem too large */ + } + + /* check the input matrix: AMD_OK, AMD_INVALID, or AMD_OK_BUT_JUMBLED */ + status = AMD_valid (n, n, Ap, Ai) ; + + if (status == AMD_INVALID) + { + if (info) Info [AMD_STATUS] = AMD_INVALID ; + return (AMD_INVALID) ; /* matrix is invalid */ + } + + /* allocate two size-n integer workspaces */ + Len = amd_malloc (n * sizeof (Int)) ; + Pinv = amd_malloc (n * sizeof (Int)) ; + mem += n ; + mem += n ; + if (!Len || !Pinv) + { + /* :: out of memory :: */ + amd_free (Len) ; + amd_free (Pinv) ; + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; + } + + if (status == AMD_OK_BUT_JUMBLED) + { + /* sort the input matrix and remove duplicate entries */ + AMD_DEBUG1 (("Matrix is jumbled\n")) ; + Rp = amd_malloc ((n+1) * sizeof (Int)) ; + Ri = amd_malloc (MAX (nz,1) * sizeof (Int)) ; + mem += (n+1) ; + mem += MAX (nz,1) ; + if (!Rp || !Ri) + { + /* :: out of memory :: */ + amd_free (Rp) ; + amd_free (Ri) ; + amd_free (Len) ; + amd_free (Pinv) ; + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; + } + /* use Len and Pinv as workspace to create R = A' */ + AMD_preprocess (n, Ap, Ai, Rp, Ri, Len, Pinv) ; + Cp = Rp ; + Ci = Ri ; + } + else + { + /* order the input matrix as-is. No need to compute R = A' first */ + Rp = NULL ; + Ri = NULL ; + Cp = (Int *) Ap ; + Ci = (Int *) Ai ; + } + + /* --------------------------------------------------------------------- */ + /* determine the symmetry and count off-diagonal nonzeros in A+A' */ + /* --------------------------------------------------------------------- */ + + nzaat = AMD_aat (n, Cp, Ci, Len, P, Info) ; + AMD_DEBUG1 (("nzaat: %g\n", (double) nzaat)) ; + ASSERT ((MAX (nz-n, 0) <= nzaat) && (nzaat <= 2 * (size_t) nz)) ; + + /* --------------------------------------------------------------------- */ + /* allocate workspace for matrix, elbow room, and 6 size-n vectors */ + /* --------------------------------------------------------------------- */ + + S = NULL ; + slen = nzaat ; /* space for matrix */ + ok = ((slen + nzaat/5) >= slen) ; /* check for size_t overflow */ + slen += nzaat/5 ; /* add elbow room */ + for (i = 0 ; ok && i < 7 ; i++) + { + ok = ((slen + n) > slen) ; /* check for size_t overflow */ + slen += n ; /* size-n elbow room, 6 size-n work */ + } + mem += slen ; + ok = ok && (slen < SIZE_T_MAX / sizeof (Int)) ; /* check for overflow */ + ok = ok && (slen < Int_MAX) ; /* S[i] for Int i must be OK */ + if (ok) + { + S = amd_malloc (slen * sizeof (Int)) ; + } + AMD_DEBUG1 (("slen %g\n", (double) slen)) ; + if (!S) + { + /* :: out of memory :: (or problem too large) */ + amd_free (Rp) ; + amd_free (Ri) ; + amd_free (Len) ; + amd_free (Pinv) ; + if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; + return (AMD_OUT_OF_MEMORY) ; + } + if (info) + { + /* memory usage, in bytes. */ + Info [AMD_MEMORY] = mem * sizeof (Int) ; + } + + /* --------------------------------------------------------------------- */ + /* order the matrix */ + /* --------------------------------------------------------------------- */ + + AMD_1 (n, Cp, Ci, P, Pinv, Len, slen, S, Control, Info) ; + + /* --------------------------------------------------------------------- */ + /* free the workspace */ + /* --------------------------------------------------------------------- */ + + amd_free (Rp) ; + amd_free (Ri) ; + amd_free (Len) ; + amd_free (Pinv) ; + amd_free (S) ; + if (info) Info [AMD_STATUS] = status ; + return (status) ; /* successful ordering */ +} diff --git a/src/maths/UMFPACK/amd_post_tree.c b/src/maths/UMFPACK/amd_post_tree.c new file mode 100644 index 000000000..b4e063d52 --- /dev/null +++ b/src/maths/UMFPACK/amd_post_tree.c @@ -0,0 +1,121 @@ +/* ========================================================================= */ +/* === AMD_post_tree ======================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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/src/maths/UMFPACK/amd_postorder.c b/src/maths/UMFPACK/amd_postorder.c new file mode 100644 index 000000000..4adcea3c0 --- /dev/null +++ b/src/maths/UMFPACK/amd_postorder.c @@ -0,0 +1,207 @@ +/* ========================================================================= */ +/* === AMD_postorder ======================================================= */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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/src/maths/UMFPACK/amd_preprocess.c b/src/maths/UMFPACK/amd_preprocess.c new file mode 100644 index 000000000..86ea07f86 --- /dev/null +++ b/src/maths/UMFPACK/amd_preprocess.c @@ -0,0 +1,119 @@ +/* ========================================================================= */ +/* === AMD_preprocess ====================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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. The input matrix can have + * duplicate entries and/or unsorted columns (AMD_valid (n,Ap,Ai) must not be + * AMD_INVALID). + * + * This input condition is NOT checked. This routine is not user-callable. + */ + +#include "amd_internal.h" + +/* ========================================================================= */ +/* === AMD_preprocess ====================================================== */ +/* ========================================================================= */ + +/* AMD_preprocess does not check its input for errors or allocate workspace. + * On input, the condition (AMD_valid (n,n,Ap,Ai) != AMD_INVALID) must hold. + */ + +GLOBAL void AMD_preprocess +( + 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 ; + + ASSERT (AMD_valid (n, n, Ap, Ai) != AMD_INVALID) ; + + /* --------------------------------------------------------------------- */ + /* 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 + ASSERT (AMD_valid (n, n, Rp, Ri) == AMD_OK) ; + for (j = 0 ; j < n ; j++) + { + ASSERT (W [j] == Rp [j+1]) ; + } +#endif +} diff --git a/src/maths/UMFPACK/amd_valid.c b/src/maths/UMFPACK/amd_valid.c new file mode 100644 index 000000000..4d05925c2 --- /dev/null +++ b/src/maths/UMFPACK/amd_valid.c @@ -0,0 +1,93 @@ +/* ========================================================================= */ +/* === AMD_valid =========================================================== */ +/* ========================================================================= */ + +/* ------------------------------------------------------------------------- */ +/* AMD, Copyright (c) Timothy A. Davis, */ +/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */ +/* email: davis at 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. + * Ai [0 ... nz-1] must be in the range 0 to n_row-1. + * + * If any of the above conditions hold, AMD_INVALID is returned. If the + * following condition holds, AMD_OK_BUT_JUMBLED is returned (a warning, + * not an error): + * + * row indices in Ai [Ap [j] ... Ap [j+1]-1] are not sorted in ascending + * order, and/or duplicate entries exist. + * + * Otherwise, AMD_OK is returned. + * + * In v1.2 and earlier, this function returned TRUE if the matrix was valid + * (now returns AMD_OK), or FALSE otherwise (now returns AMD_INVALID or + * AMD_OK_BUT_JUMBLED). + */ + +#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, result = AMD_OK ; + + if (n_row < 0 || n_col < 0 || Ap == NULL || Ai == NULL) + { + return (AMD_INVALID) ; + } + 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 (AMD_INVALID) ; + } + 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 (AMD_INVALID) ; + } + ilast = EMPTY ; + for (p = p1 ; p < p2 ; p++) + { + i = Ai [p] ; + AMD_DEBUG3 (("row: "ID"\n", i)) ; + if (i < 0 || i >= n_row) + { + /* row index out of range */ + AMD_DEBUG0 (("index out of range, col "ID" row "ID"\n", j, i)); + return (AMD_INVALID) ; + } + if (i <= ilast) + { + /* row index unsorted, or duplicate entry present */ + AMD_DEBUG1 (("index unsorted/dupl col "ID" row "ID"\n", j, i)); + result = AMD_OK_BUT_JUMBLED ; + } + ilast = i ; + } + } + return (result) ; +} diff --git a/src/maths/UMFPACK/cholmod_blas.h b/src/maths/UMFPACK/cholmod_blas.h new file mode 100644 index 000000000..b03de64fe --- /dev/null +++ b/src/maths/UMFPACK/cholmod_blas.h @@ -0,0 +1,456 @@ +/* ========================================================================== */ +/* === Include/cholmod_blas.h =============================================== */ +/* ========================================================================== */ + +/* ----------------------------------------------------------------------------- + * CHOLMOD/Include/cholmod_blas.h. + * Copyright (C) 2005-2006, Univ. of Florida. Author: Timothy A. Davis + * CHOLMOD/Include/cholmod_blas.h is licensed under Version 2.1 of the GNU + * Lesser General Public License. See lesser.txt for a text of the license. + * CHOLMOD is also available under other licenses; contact authors for details. + * http://www.cise.ufl.edu/research/sparse + * -------------------------------------------------------------------------- */ + +/* This does not need to be included in the user's program. */ + +#ifndef CHOLMOD_BLAS_H +#define CHOLMOD_BLAS_H + +/* ========================================================================== */ +/* === Architecture ========================================================= */ +/* ========================================================================== */ + +#if defined (__sun) || defined (MSOL2) || defined (ARCH_SOL2) +#define CHOLMOD_SOL2 +#define CHOLMOD_ARCHITECTURE "Sun Solaris" + +#elif defined (__sgi) || defined (MSGI) || defined (ARCH_SGI) +#define CHOLMOD_SGI +#define CHOLMOD_ARCHITECTURE "SGI Irix" + +#elif defined (__linux) || defined (MGLNX86) || defined (ARCH_GLNX86) +#define CHOLMOD_LINUX +#define CHOLMOD_ARCHITECTURE "Linux" + +#elif defined (__APPLE__) +#define CHOLMOD_MAC +#define CHOLMOD_ARCHITECTURE "Mac" + +#elif defined (_AIX) || defined (MIBM_RS) || defined (ARCH_IBM_RS) +#define CHOLMOD_AIX +#define CHOLMOD_ARCHITECTURE "IBM AIX" +/* recent reports from IBM AIX seem to indicate that this is not needed: */ +/* #define BLAS_NO_UNDERSCORE */ + +#elif defined (__alpha) || defined (MALPHA) || defined (ARCH_ALPHA) +#define CHOLMOD_ALPHA +#define CHOLMOD_ARCHITECTURE "Compaq Alpha" + +#elif defined (_WIN32) || defined (WIN32) || defined (_WIN64) || defined (WIN64) +#if defined (__MINGW32__) || defined (__MINGW32__) +#define CHOLMOD_MINGW +#elif defined (__CYGWIN32__) || defined (__CYGWIN32__) +#define CHOLMOD_CYGWIN +#else +#define CHOLMOD_WINDOWS +#define BLAS_NO_UNDERSCORE +#endif +#define CHOLMOD_ARCHITECTURE "Microsoft Windows" + +#elif defined (__hppa) || defined (__hpux) || defined (MHPUX) || defined (ARCH_HPUX) +#define CHOLMOD_HP +#define CHOLMOD_ARCHITECTURE "HP Unix" +#define BLAS_NO_UNDERSCORE + +#elif defined (__hp700) || defined (MHP700) || defined (ARCH_HP700) +#define CHOLMOD_HP +#define CHOLMOD_ARCHITECTURE "HP 700 Unix" +#define BLAS_NO_UNDERSCORE + +#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 CHOLMOD_ARCHITECTURE "unknown" +#endif + +/* ========================================================================== */ +/* === BLAS and LAPACK names ================================================ */ +/* ========================================================================== */ + +/* Prototypes for the various versions of the BLAS. */ + +/* Determine if the 64-bit Sun Performance BLAS is to be used */ +#if defined(CHOLMOD_SOL2) && !defined(NSUNPERF) && defined(BLAS64) +#define SUN64 +#endif + +#ifdef SUN64 + +#define BLAS_DTRSV dtrsv_64_ +#define BLAS_DGEMV dgemv_64_ +#define BLAS_DTRSM dtrsm_64_ +#define BLAS_DGEMM dgemm_64_ +#define BLAS_DSYRK dsyrk_64_ +#define BLAS_DGER dger_64_ +#define BLAS_DSCAL dscal_64_ +#define LAPACK_DPOTRF dpotrf_64_ + +#define BLAS_ZTRSV ztrsv_64_ +#define BLAS_ZGEMV zgemv_64_ +#define BLAS_ZTRSM ztrsm_64_ +#define BLAS_ZGEMM zgemm_64_ +#define BLAS_ZHERK zherk_64_ +#define BLAS_ZGER zgeru_64_ +#define BLAS_ZSCAL zscal_64_ +#define LAPACK_ZPOTRF zpotrf_64_ + +#elif defined (BLAS_NO_UNDERSCORE) + +#define BLAS_DTRSV dtrsv +#define BLAS_DGEMV dgemv +#define BLAS_DTRSM dtrsm +#define BLAS_DGEMM dgemm +#define BLAS_DSYRK dsyrk +#define BLAS_DGER dger +#define BLAS_DSCAL dscal +#define LAPACK_DPOTRF dpotrf + +#define BLAS_ZTRSV ztrsv +#define BLAS_ZGEMV zgemv +#define BLAS_ZTRSM ztrsm +#define BLAS_ZGEMM zgemm +#define BLAS_ZHERK zherk +#define BLAS_ZGER zgeru +#define BLAS_ZSCAL zscal +#define LAPACK_ZPOTRF zpotrf + +#else + +#define BLAS_DTRSV dtrsv_ +#define BLAS_DGEMV dgemv_ +#define BLAS_DTRSM dtrsm_ +#define BLAS_DGEMM dgemm_ +#define BLAS_DSYRK dsyrk_ +#define BLAS_DGER dger_ +#define BLAS_DSCAL dscal_ +#define LAPACK_DPOTRF dpotrf_ + +#define BLAS_ZTRSV ztrsv_ +#define BLAS_ZGEMV zgemv_ +#define BLAS_ZTRSM ztrsm_ +#define BLAS_ZGEMM zgemm_ +#define BLAS_ZHERK zherk_ +#define BLAS_ZGER zgeru_ +#define BLAS_ZSCAL zscal_ +#define LAPACK_ZPOTRF zpotrf_ + +#endif + +/* ========================================================================== */ +/* === BLAS and LAPACK integer arguments ==================================== */ +/* ========================================================================== */ + +/* Compile CHOLMOD, UMFPACK, and SPQR with -DBLAS64 if you have a BLAS that + * uses 64-bit integers */ + +#if defined (LONGBLAS) || defined (BLAS64) +#define BLAS_INT UF_long +#else +#define BLAS_INT int +#endif + +/* If the BLAS integer is smaller than the basic CHOLMOD integer, then we need + * to check for integer overflow when converting from Int to BLAS_INT. If + * any integer overflows, the externally-defined BLAS_OK variable is + * set to FALSE. BLAS_OK should be set to TRUE before calling any + * BLAS_* macro. + */ + +#define CHECK_BLAS_INT (sizeof (BLAS_INT) < sizeof (Int)) +#define EQ(K,k) (((BLAS_INT) K) == ((Int) k)) + +/* ========================================================================== */ +/* === BLAS and LAPACK prototypes and macros ================================ */ +/* ========================================================================== */ + +void BLAS_DGEMV (char *trans, BLAS_INT *m, BLAS_INT *n, double *alpha, + double *A, BLAS_INT *lda, double *X, BLAS_INT *incx, double *beta, + double *Y, BLAS_INT *incy) ; + +#define BLAS_dgemv(trans,m,n,alpha,A,lda,X,incx,beta,Y,incy) \ +{ \ + BLAS_INT M = m, N = n, LDA = lda, INCX = incx, INCY = incy ; \ + if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \ + EQ (INCX,incx) && EQ (INCY,incy))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_DGEMV (trans, &M, &N, alpha, A, &LDA, X, &INCX, beta, Y, &INCY) ; \ + } \ +} + +void BLAS_ZGEMV (char *trans, BLAS_INT *m, BLAS_INT *n, double *alpha, + double *A, BLAS_INT *lda, double *X, BLAS_INT *incx, double *beta, + double *Y, BLAS_INT *incy) ; + +#define BLAS_zgemv(trans,m,n,alpha,A,lda,X,incx,beta,Y,incy) \ +{ \ + BLAS_INT M = m, N = n, LDA = lda, INCX = incx, INCY = incy ; \ + if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \ + EQ (INCX,incx) && EQ (INCY,incy))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_ZGEMV (trans, &M, &N, alpha, A, &LDA, X, &INCX, beta, Y, &INCY) ; \ + } \ +} + +void BLAS_DTRSV (char *uplo, char *trans, char *diag, BLAS_INT *n, double *A, + BLAS_INT *lda, double *X, BLAS_INT *incx) ; + +#define BLAS_dtrsv(uplo,trans,diag,n,A,lda,X,incx) \ +{ \ + BLAS_INT N = n, LDA = lda, INCX = incx ; \ + if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_DTRSV (uplo, trans, diag, &N, A, &LDA, X, &INCX) ; \ + } \ +} + +void BLAS_ZTRSV (char *uplo, char *trans, char *diag, BLAS_INT *n, double *A, + BLAS_INT *lda, double *X, BLAS_INT *incx) ; + +#define BLAS_ztrsv(uplo,trans,diag,n,A,lda,X,incx) \ +{ \ + BLAS_INT N = n, LDA = lda, INCX = incx ; \ + if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (LDA,lda) && EQ (INCX,incx))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_ZTRSV (uplo, trans, diag, &N, A, &LDA, X, &INCX) ; \ + } \ +} + +void BLAS_DTRSM (char *side, char *uplo, char *transa, char *diag, BLAS_INT *m, + BLAS_INT *n, double *alpha, double *A, BLAS_INT *lda, double *B, + BLAS_INT *ldb) ; + +#define BLAS_dtrsm(side,uplo,transa,diag,m,n,alpha,A,lda,B,ldb) \ +{ \ + BLAS_INT M = m, N = n, LDA = lda, LDB = ldb ; \ + if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \ + EQ (LDB,ldb))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_DTRSM (side, uplo, transa, diag, &M, &N, alpha, A, &LDA, B, &LDB);\ + } \ +} + +void BLAS_ZTRSM (char *side, char *uplo, char *transa, char *diag, BLAS_INT *m, + BLAS_INT *n, double *alpha, double *A, BLAS_INT *lda, double *B, + BLAS_INT *ldb) ; + +#define BLAS_ztrsm(side,uplo,transa,diag,m,n,alpha,A,lda,B,ldb) \ +{ \ + BLAS_INT M = m, N = n, LDA = lda, LDB = ldb ; \ + if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \ + EQ (LDB,ldb))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_ZTRSM (side, uplo, transa, diag, &M, &N, alpha, A, &LDA, B, &LDB);\ + } \ +} + +void BLAS_DGEMM (char *transa, char *transb, BLAS_INT *m, BLAS_INT *n, + BLAS_INT *k, double *alpha, double *A, BLAS_INT *lda, double *B, + BLAS_INT *ldb, double *beta, double *C, BLAS_INT *ldc) ; + +#define BLAS_dgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta,C,ldc) \ +{ \ + BLAS_INT M = m, N = n, K = k, LDA = lda, LDB = ldb, LDC = ldc ; \ + if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (K,k) && \ + EQ (LDA,lda) && EQ (LDB,ldb) && EQ (LDC,ldc))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_DGEMM (transa, transb, &M, &N, &K, alpha, A, &LDA, B, &LDB, beta, \ + C, &LDC) ; \ + } \ +} + +void BLAS_ZGEMM (char *transa, char *transb, BLAS_INT *m, BLAS_INT *n, + BLAS_INT *k, double *alpha, double *A, BLAS_INT *lda, double *B, + BLAS_INT *ldb, double *beta, double *C, BLAS_INT *ldc) ; + +#define BLAS_zgemm(transa,transb,m,n,k,alpha,A,lda,B,ldb,beta,C,ldc) \ +{ \ + BLAS_INT M = m, N = n, K = k, LDA = lda, LDB = ldb, LDC = ldc ; \ + if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (K,k) && \ + EQ (LDA,lda) && EQ (LDB,ldb) && EQ (LDC,ldc))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_ZGEMM (transa, transb, &M, &N, &K, alpha, A, &LDA, B, &LDB, beta, \ + C, &LDC) ; \ + } \ +} + +void BLAS_DSYRK (char *uplo, char *trans, BLAS_INT *n, BLAS_INT *k, + double *alpha, double *A, BLAS_INT *lda, double *beta, double *C, + BLAS_INT *ldc) ; + +#define BLAS_dsyrk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc) \ +{ \ + BLAS_INT N = n, K = k, LDA = lda, LDC = ldc ; \ + if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (K,k) && EQ (LDA,lda) && \ + EQ (LDC,ldc))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_DSYRK (uplo, trans, &N, &K, alpha, A, &LDA, beta, C, &LDC) ; \ + } \ +} \ + +void BLAS_ZHERK (char *uplo, char *trans, BLAS_INT *n, BLAS_INT *k, + double *alpha, double *A, BLAS_INT *lda, double *beta, double *C, + BLAS_INT *ldc) ; + +#define BLAS_zherk(uplo,trans,n,k,alpha,A,lda,beta,C,ldc) \ +{ \ + BLAS_INT N = n, K = k, LDA = lda, LDC = ldc ; \ + if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (K,k) && EQ (LDA,lda) && \ + EQ (LDC,ldc))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_ZHERK (uplo, trans, &N, &K, alpha, A, &LDA, beta, C, &LDC) ; \ + } \ +} \ + +void LAPACK_DPOTRF (char *uplo, BLAS_INT *n, double *A, BLAS_INT *lda, + BLAS_INT *info) ; + +#define LAPACK_dpotrf(uplo,n,A,lda,info) \ +{ \ + BLAS_INT N = n, LDA = lda, INFO = 1 ; \ + if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (LDA,lda))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + LAPACK_DPOTRF (uplo, &N, A, &LDA, &INFO) ; \ + } \ + info = INFO ; \ +} + +void LAPACK_ZPOTRF (char *uplo, BLAS_INT *n, double *A, BLAS_INT *lda, + BLAS_INT *info) ; + +#define LAPACK_zpotrf(uplo,n,A,lda,info) \ +{ \ + BLAS_INT N = n, LDA = lda, INFO = 1 ; \ + if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (LDA,lda))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + LAPACK_ZPOTRF (uplo, &N, A, &LDA, &INFO) ; \ + } \ + info = INFO ; \ +} + +/* ========================================================================== */ + +void BLAS_DSCAL (BLAS_INT *n, double *alpha, double *Y, BLAS_INT *incy) ; + +#define BLAS_dscal(n,alpha,Y,incy) \ +{ \ + BLAS_INT N = n, INCY = incy ; \ + if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (INCY,incy))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_DSCAL (&N, alpha, Y, &INCY) ; \ + } \ +} + +void BLAS_ZSCAL (BLAS_INT *n, double *alpha, double *Y, BLAS_INT *incy) ; + +#define BLAS_zscal(n,alpha,Y,incy) \ +{ \ + BLAS_INT N = n, INCY = incy ; \ + if (CHECK_BLAS_INT && !(EQ (N,n) && EQ (INCY,incy))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_ZSCAL (&N, alpha, Y, &INCY) ; \ + } \ +} + +void BLAS_DGER (BLAS_INT *m, BLAS_INT *n, double *alpha, + double *X, BLAS_INT *incx, double *Y, BLAS_INT *incy, + double *A, BLAS_INT *lda) ; + +#define BLAS_dger(m,n,alpha,X,incx,Y,incy,A,lda) \ +{ \ + BLAS_INT M = m, N = n, LDA = lda, INCX = incx, INCY = incy ; \ + if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \ + EQ (INCX,incx) && EQ (INCY,incy))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_DGER (&M, &N, alpha, X, &INCX, Y, &INCY, A, &LDA) ; \ + } \ +} + +void BLAS_ZGER (BLAS_INT *m, BLAS_INT *n, double *alpha, + double *X, BLAS_INT *incx, double *Y, BLAS_INT *incy, + double *A, BLAS_INT *lda) ; + +#define BLAS_zgeru(m,n,alpha,X,incx,Y,incy,A,lda) \ +{ \ + BLAS_INT M = m, N = n, LDA = lda, INCX = incx, INCY = incy ; \ + if (CHECK_BLAS_INT && !(EQ (M,m) && EQ (N,n) && EQ (LDA,lda) && \ + EQ (INCX,incx) && EQ (INCY,incy))) \ + { \ + BLAS_OK = FALSE ; \ + } \ + if (!CHECK_BLAS_INT || BLAS_OK) \ + { \ + BLAS_ZGER (&M, &N, alpha, X, &INCX, Y, &INCY, A, &LDA) ; \ + } \ +} + +#endif diff --git a/src/maths/UMFPACK/umf_analyze.c b/src/maths/UMFPACK/umf_analyze.c new file mode 100644 index 000000000..5d13199eb --- /dev/null +++ b/src/maths/UMFPACK/umf_analyze.c @@ -0,0 +1,705 @@ +/* ========================================================================== */ +/* === UMF_analyze ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 nonzero column. + The matrix is assumed to be valid. + + Empty rows and columns have already been removed. + +*/ + +#include "umf_internal.h" +#include "umf_analyze.h" +#include "umf_apply_order.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 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 of j */ + /* (or are empty) */ + + /* row knext has first column 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/src/maths/UMFPACK/umf_analyze.h b/src/maths/UMFPACK/umf_analyze.h new file mode 100644 index 000000000..733ba03e1 --- /dev/null +++ b/src/maths/UMFPACK/umf_analyze.h @@ -0,0 +1,23 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_apply_order.c b/src/maths/UMFPACK/umf_apply_order.c new file mode 100644 index 000000000..6192af4c2 --- /dev/null +++ b/src/maths/UMFPACK/umf_apply_order.c @@ -0,0 +1,44 @@ +/* ========================================================================== */ +/* === UMF_apply_order ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Apply post-ordering of supernodal elimination tree. +*/ + +#include "umf_internal.h" +#include "umf_apply_order.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/src/maths/UMFPACK/umf_apply_order.h b/src/maths/UMFPACK/umf_apply_order.h new file mode 100644 index 000000000..dd7e682e5 --- /dev/null +++ b/src/maths/UMFPACK/umf_apply_order.h @@ -0,0 +1,14 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_assemble.c b/src/maths/UMFPACK/umf_assemble.c new file mode 100644 index 000000000..04a3de334 --- /dev/null +++ b/src/maths/UMFPACK/umf_assemble.c @@ -0,0 +1,1216 @@ +/* ========================================================================== */ +/* === UMF_assemble ========================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/UF_long version, for a total of 8 + * versions.*/ + +#include "umf_internal.h" +#include "umf_assemble.h" +#include "umf_mem_free_tail_block.h" + +/* ========================================================================== */ +/* === row_assemble ========================================================= */ +/* ========================================================================== */ + +PRIVATE void row_assemble +( + Int row, + NumericType *Numeric, + WorkType *Work +) +{ + + Entry *S, *Fcblock, *Frow ; + 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 ; + +#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 +) +{ + + Entry *S, *Fcblock, *Fcol ; + 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 ; + +#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/src/maths/UMFPACK/umf_assemble.h b/src/maths/UMFPACK/umf_assemble.h new file mode 100644 index 000000000..e976b4854 --- /dev/null +++ b/src/maths/UMFPACK/umf_assemble.h @@ -0,0 +1,17 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_blas3_update.c b/src/maths/UMFPACK/umf_blas3_update.c new file mode 100644 index 000000000..c4e9ee75a --- /dev/null +++ b/src/maths/UMFPACK/umf_blas3_update.c @@ -0,0 +1,176 @@ +/* ========================================================================== */ +/* === UMF_blas3_update ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" +#include "umf_blas3_update.h" + +GLOBAL void UMF_blas3_update +( + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Entry *L, *U, *C, *LU ; + Int i, j, s, k, m, n, d, nb, dc ; + +#ifndef NBLAS + Int blas_ok = TRUE ; +#else +#define blas_ok FALSE +#endif + + 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) + { + +#ifndef NBLAS + BLAS_GER (m, n, L, U, C, d) ; +#endif + + if (!blas_ok) + { + /* 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 + { + + /* triangular solve to update the U block */ + +#ifndef NBLAS + BLAS_TRSM_RIGHT (n, k, LU, nb, U, dc) ; +#endif + + if (!blas_ok) + { + /* use plain C code if no BLAS at compile time, or if integer + * overflow has occurred */ + 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) */ + +#ifndef NBLAS + BLAS_GEMM (m, n, k, L, U, dc, C, d) ; +#endif + + if (!blas_ok) + { + /* use plain C code if no BLAS at compile time, or if integer + * overflow has occurred */ + + 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++ ; + } + } + } + } + } + } + +#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/src/maths/UMFPACK/umf_blas3_update.h b/src/maths/UMFPACK/umf_blas3_update.h new file mode 100644 index 000000000..c77b9ac62 --- /dev/null +++ b/src/maths/UMFPACK/umf_blas3_update.h @@ -0,0 +1,10 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_blas3_update +( + WorkType *Work +) ; diff --git a/src/maths/UMFPACK/umf_build_tuples.c b/src/maths/UMFPACK/umf_build_tuples.c new file mode 100644 index 000000000..c2ab3243f --- /dev/null +++ b/src/maths/UMFPACK/umf_build_tuples.c @@ -0,0 +1,160 @@ +/* ========================================================================== */ +/* === UMF_build_tuples ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_build_tuples.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/src/maths/UMFPACK/umf_build_tuples.h b/src/maths/UMFPACK/umf_build_tuples.h new file mode 100644 index 000000000..0b0410c9e --- /dev/null +++ b/src/maths/UMFPACK/umf_build_tuples.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_build_tuples +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/src/maths/UMFPACK/umf_cholmod.c b/src/maths/UMFPACK/umf_cholmod.c new file mode 100644 index 000000000..c93a89127 --- /dev/null +++ b/src/maths/UMFPACK/umf_cholmod.c @@ -0,0 +1,239 @@ +/* ========================================================================== */ +/* === umf_cholmod ========================================================== */ +/* ========================================================================== */ + +/* umfpack_cholmod: user-defined ordering function to interface UMFPACK + * to CHOLMOD. + * + * This routine is an example of a user-provided ordering function for UMFPACK. + * + * This function can be passed to umfpack_*_fsymbolic as the + * user_ordering function pointer. + */ + +#include "umf_internal.h" +#include "umf_cholmod.h" + +#ifndef NCHOLMOD +#include "cholmod.h" +#endif + +#if defined (DINT) || defined (ZINT) +#define CHOLMOD_start cholmod_start +#define CHOLMOD_transpose cholmod_transpose +#define CHOLMOD_analyze cholmod_analyze +#define CHOLMOD_free_sparse cholmod_free_sparse +#define CHOLMOD_free_factor cholmod_free_factor +#define CHOLMOD_finish cholmod_finish +#define CHOLMOD_print_common cholmod_print_common +#else +#define CHOLMOD_start cholmod_l_start +#define CHOLMOD_transpose cholmod_l_transpose +#define CHOLMOD_analyze cholmod_l_analyze +#define CHOLMOD_free_sparse cholmod_l_free_sparse +#define CHOLMOD_free_factor cholmod_l_free_factor +#define CHOLMOD_finish cholmod_l_finish +#define CHOLMOD_print_common cholmod_l_print_common +#endif + +int UMF_cholmod +( + /* inputs */ + Int nrow, /* A is nrow-by-ncol */ + Int ncol, /* A is nrow-by-ncol */ + Int symmetric, /* if true and nrow=ncol do A+A', else do A'A */ + Int Ap [ ], /* column pointers, size ncol+1 */ + Int Ai [ ], /* row indices, size nz = Ap [ncol] */ + /* output */ + Int Perm [ ], /* fill-reducing permutation, size ncol */ + /* user-defined */ + void *user_params, /* Int array of size 3 */ + double user_info [3] /* [0]: max col count for L=chol(P(A+A')P') + [1]: nnz (L) + [2]: flop count for chol, if A real */ +) +{ +#ifndef NCHOLMOD + double dmax, flops, c, lnz ; + cholmod_sparse Amatrix, *A, *AT, *S ; + cholmod_factor *L ; + cholmod_common cm ; + Int *P, *ColCount ; + Int k, ordering_option, print_level, *params ; + + params = (Int *) user_params ; + ordering_option = params [0] ; + print_level = params [1] - 1 ; + params [2] = -1 ; + + if (Ap == NULL || Ai == NULL || Perm == NULL || nrow < 0 || ncol < 0) + { + /* invalid inputs */ + return (FALSE) ; + } + if (nrow != ncol) + { + /* force symmetric to be false */ + symmetric = FALSE ; + } + + /* start CHOLMOD */ + CHOLMOD_start (&cm) ; + cm.supernodal = CHOLMOD_SIMPLICIAL ; + cm.print = print_level ; + + /* adjust cm based on ordering_option */ + switch (ordering_option) + { + + default: + case UMFPACK_ORDERING_AMD: + /* AMD on A+A' if symmetric, COLAMD on A otherwise */ + cm.nmethods = 1 ; + cm.method [0].ordering = symmetric ? CHOLMOD_AMD : CHOLMOD_COLAMD ; + cm.postorder = TRUE ; + break ; + + case UMFPACK_ORDERING_METIS: + /* metis on A+A' if symmetric, A'A otherwise */ + cm.nmethods = 1 ; + cm.method [0].ordering = CHOLMOD_METIS ; + cm.postorder = TRUE ; + break ; + + case UMFPACK_ORDERING_NONE: + case UMFPACK_ORDERING_GIVEN: + case UMFPACK_ORDERING_USER: + /* no ordering. No input permutation here, and no user + function, so all these are the same as "none". */ + cm.nmethods = 1 ; + cm.method [0].ordering = CHOLMOD_NATURAL ; + cm.postorder = FALSE ; + break ; + + case UMFPACK_ORDERING_BEST: + /* try AMD, METIS and NESDIS on A+A', or COLAMD(A), METIS(A'A), + and NESDIS (A'A) */ + cm.nmethods = 3 ; + cm.method [0].ordering = symmetric ? CHOLMOD_AMD : CHOLMOD_COLAMD ; + cm.method [1].ordering = CHOLMOD_METIS ; + cm.method [2].ordering = CHOLMOD_NESDIS ; + cm.postorder = TRUE ; + break ; + + case UMFPACK_ORDERING_CHOLMOD: + /* no change to CHOLMOD defaults: + Do not use given permutation, since it's not provided. + Try AMD. If fill-in and flop count are low, use AMD. + Otherwise, try METIS and take the best of AMD and METIS. + cm.method [0].ordering = CHOLMOD_GIVEN + cm.method [1].ordering = CHOLMOD_AMD + cm.method [2].ordering = CHOLMOD_METIS + cm.nmethods = 2 if METIS installed, 3 otherwise ('given' is skipped) + */ + break ; + } + + /* use AMD memory management routines for CHOLMOD */ + cm.malloc_memory = amd_malloc ; + cm.realloc_memory = amd_realloc ; + cm.calloc_memory = amd_calloc ; + cm.free_memory = amd_free ; + + /* construct a CHOLMOD version of the input matrix A */ + A = &Amatrix ; + A->nrow = nrow ; /* A is nrow-by-ncol */ + A->ncol = ncol ; + A->nzmax = Ap [ncol] ; /* with nzmax entries */ + A->packed = TRUE ; /* there is no A->nz array */ + if (symmetric) + { + A->stype = 1 ; /* A is symmetric */ + } + else + { + A->stype = 0 ; /* A is unsymmetric */ + } + A->itype = CHOLMOD_INT ; + A->xtype = CHOLMOD_PATTERN ; + A->dtype = CHOLMOD_DOUBLE ; + A->nz = NULL ; + A->p = Ap ; /* column pointers */ + A->i = Ai ; /* row indices */ + A->x = NULL ; /* no numerical values */ + A->z = NULL ; + A->sorted = FALSE ; /* columns of A might not be sorted */ + + if (symmetric) + { + /* CHOLMOD with order the symmetric matrix A */ + AT = NULL ; + S = A ; + } + else + { + /* S = A'. CHOLMOD will order S*S', which is A'*A */ + AT = CHOLMOD_transpose (A, 0, &cm) ; + S = AT ; + } + + /* order and analyze S or S*S' */ + L = CHOLMOD_analyze (S, &cm) ; + CHOLMOD_free_sparse (&AT, &cm) ; + if (L == NULL) + { + return (FALSE) ; + } + + /* determine the ordering used */ + switch (L->ordering) + { + + case CHOLMOD_AMD: + case CHOLMOD_COLAMD: + params [2] = UMFPACK_ORDERING_AMD ; + break ; + + case CHOLMOD_METIS: + case CHOLMOD_NESDIS: + params [2] = UMFPACK_ORDERING_METIS ; + break ; + + case CHOLMOD_GIVEN: + case CHOLMOD_NATURAL: + default: + params [2] = UMFPACK_ORDERING_NONE ; + break ; + } + + /* copy the permutation from L to the output and compute statistics */ + P = L->Perm ; + ColCount = L->ColCount ; + dmax = 1 ; + lnz = 0 ; + flops = 0 ; + for (k = 0 ; k < ncol ; k++) + { + Perm [k] = P [k] ; + c = ColCount [k] ; + if (c > dmax) dmax = c ; + lnz += c ; + flops += c*c ; + } + user_info [0] = dmax ; + user_info [1] = lnz ; + user_info [2] = flops ; + + CHOLMOD_free_factor (&L, &cm) ; + if (print_level > 0) + { + CHOLMOD_print_common ("for UMFPACK", &cm) ; + } + CHOLMOD_finish (&cm) ; + return (TRUE) ; +#else + /* CHOLMOD and its supporting packages (CAMD, CCOLAMD, COLAMD, metis-4.0) + not installed */ + return (FALSE) ; +#endif +} diff --git a/src/maths/UMFPACK/umf_cholmod.h b/src/maths/UMFPACK/umf_cholmod.h new file mode 100644 index 000000000..ee9703837 --- /dev/null +++ b/src/maths/UMFPACK/umf_cholmod.h @@ -0,0 +1,38 @@ +#include "ngspice/umfpack.h" +#include "ngspice/UFconfig.h" + +int umf_i_cholmod +( + /* inputs */ + int nrow, /* A is nrow-by-ncol */ + int ncol, /* A is nrow-by-ncol */ + int symmetric, /* if true and nrow=ncol do A+A', else do A'A */ + int Ap [ ], /* column pointers, size ncol+1 */ + int Ai [ ], /* row indices, size nz = Ap [ncol] */ + /* output */ + int Perm [ ], /* fill-reducing permutation, size ncol */ + /* user-defined */ + void *ignore, /* not needed */ + double user_info [3] /* [0]: max col count for L=chol(P(A+A')P') + [1]: nnz (L) + [2]: flop count for chol, if A real */ +) ; + + +int umf_l_cholmod +( + /* inputs */ + UF_long nrow, /* A is nrow-by-ncol */ + UF_long ncol, /* A is nrow-by-ncol */ + UF_long symmetric, /* if true and nrow=ncol do A+A', else do A'A */ + UF_long Ap [ ], /* column pointers, size ncol+1 */ + UF_long Ai [ ], /* row indices, size nz = Ap [ncol] */ + /* output */ + UF_long Perm [ ], /* fill-reducing permutation, size ncol */ + /* user-defined */ + void *ignore, /* not needed */ + double user_info [3] /* [0]: max col count for L=chol(P(A+A')P') + [1]: nnz (L) + [2]: flop count for chol, if A real */ +) ; + diff --git a/src/maths/UMFPACK/umf_colamd.c b/src/maths/UMFPACK/umf_colamd.c new file mode 100644 index 000000000..5a9b16840 --- /dev/null +++ b/src/maths/UMFPACK/umf_colamd.c @@ -0,0 +1,3139 @@ +/* ========================================================================== */ +/* === UMF_colamd =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_colamd.h b/src/maths/UMFPACK/umf_colamd.h new file mode 100644 index 000000000..51675a572 --- /dev/null +++ b/src/maths/UMFPACK/umf_colamd.h @@ -0,0 +1,255 @@ +/* ========================================================================== */ +/* === umf_colamd.h ========================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_config.h b/src/maths/UMFPACK/umf_config.h new file mode 100644 index 000000000..f324861d4 --- /dev/null +++ b/src/maths/UMFPACK/umf_config.h @@ -0,0 +1,326 @@ +/* ========================================================================== */ +/* === umf_config.h ========================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + This file controls the compile-time configuration of UMFPACK. Modify the + UFconfig/UFconfig.mk 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 -DNSUNPERF", for example). These flags are normally placed in + your UMFPACK_CONFIG string, defined in the UFconfig/UFconfig.mk file. + + All of these options, except for the timer, are for accessing the BLAS. + + -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. + + -DLONGBLAS + + -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. + + -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. + + 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. The -DNRECIPROCAL flag is forced on (more accurate, + slightly slower). The umfpack mexFunction always returns + L*U = P*(R\A)*Q. + + -DMATHWORKS + + This flag is turned on when compiling umfpack as a built-in routine + in MATLAB. The -DNRECIPROCAL flag is forced 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 ====================================== */ +/* ========================================================================== */ + +#if defined (UMF_WINDOWS) || defined (UMF_MINGW) +/* Windows isn't Unix. Profound. */ +#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. + 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) || defined (UMF_CYGWIN) \ +|| defined (UMF_MAC) +#define GETRUSAGE +#endif + + +/* ========================================================================== */ +/* === BLAS ================================================================= */ +/* ========================================================================== */ + +#define BLAS_OK blas_ok +#include "cholmod_blas.h" + + +/* -------------------------------------------------------------------------- */ +/* DGEMM */ +/* -------------------------------------------------------------------------- */ + +/* 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 */ +#ifdef COMPLEX +#define BLAS_GEMM(m,n,k,A,B,ldb,C,ldac) \ +{ \ + double alpha [2] = {-1,0}, beta [2] = {1,0} ; \ + BLAS_zgemm ("N", "T", m, n, k, alpha, (double *) A, ldac, \ + (double *) B, ldb, beta, (double *) C, ldac) ; \ +} +#else +#define BLAS_GEMM(m,n,k,A,B,ldb,C,ldac) \ +{ \ + double alpha = -1, beta = 1 ; \ + BLAS_dgemm ("N", "T", m, n, k, &alpha, A, ldac, B, ldb, &beta, C, ldac) ; \ +} +#endif + + +/* -------------------------------------------------------------------------- */ +/* GER */ +/* -------------------------------------------------------------------------- */ + +/* 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 */ +#ifdef COMPLEX +#define BLAS_GER(m,n,x,y,A,d) \ +{ \ + double alpha [2] = {-1,0} ; \ + BLAS_zgeru (m, n, alpha, (double *) x, 1, (double *) y, 1, \ + (double *) A, d) ; \ +} +#else +#define BLAS_GER(m,n,x,y,A,d) \ +{ \ + double alpha = -1 ; \ + BLAS_dger (m, n, &alpha, x, 1, y, 1, A, d) ; \ +} +#endif + + +/* -------------------------------------------------------------------------- */ +/* GEMV */ +/* -------------------------------------------------------------------------- */ + +/* 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 */ +#ifdef COMPLEX +#define BLAS_GEMV(m,n,A,x,y,d) \ +{ \ + double alpha [2] = {-1,0}, beta [2] = {1,0} ; \ + BLAS_zgemv ("N", m, n, alpha, (double *) A, d, (double *) x, 1, beta, \ + (double *) y, 1) ; \ +} +#else +#define BLAS_GEMV(m,n,A,x,y,d) \ +{ \ + double alpha = -1, beta = 1 ; \ + BLAS_dgemv ("N", m, n, &alpha, A, d, x, 1, &beta, y, 1) ; \ +} +#endif + + +/* -------------------------------------------------------------------------- */ +/* TRSV */ +/* -------------------------------------------------------------------------- */ + +/* 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 */ +#ifdef COMPLEX +#define BLAS_TRSV(m,A,b,d) \ +{ \ + BLAS_ztrsv ("L", "N", "U", m, (double *) A, d, (double *) b, 1) ; \ +} +#else +#define BLAS_TRSV(m,A,b,d) \ +{ \ + BLAS_dtrsv ("L", "N", "U", m, A, d, b, 1) ; \ +} +#endif + + +/* -------------------------------------------------------------------------- */ +/* TRSM */ +/* -------------------------------------------------------------------------- */ + +/* solve XL'=B where: + * B is m-by-n with leading dimension ldb + * A is n-by-n with leading dimension lda */ +#ifdef COMPLEX +#define BLAS_TRSM_RIGHT(m,n,A,lda,B,ldb) \ +{ \ + double alpha [2] = {1,0} ; \ + BLAS_ztrsm ("R", "L", "T", "U", m, n, alpha, (double *) A, lda, \ + (double *) B, ldb) ; \ +} +#else +#define BLAS_TRSM_RIGHT(m,n,A,lda,B,ldb) \ +{ \ + double alpha = 1 ; \ + BLAS_dtrsm ("R", "L", "T", "U", m, n, &alpha, A, lda, B, ldb) ; \ +} +#endif + + +/* -------------------------------------------------------------------------- */ +/* SCAL */ +/* -------------------------------------------------------------------------- */ + +/* x = s*x, where x is a stride-1 vector of length n */ +#ifdef COMPLEX +#define BLAS_SCAL(n,s,x) \ +{ \ + double alpha [2] ; \ + alpha [0] = REAL_COMPONENT (s) ; \ + alpha [1] = IMAG_COMPONENT (s) ; \ + BLAS_zscal (n, alpha, (double *) x, 1) ; \ +} +#else +#define BLAS_SCAL(n,s,x) \ +{ \ + double alpha = REAL_COMPONENT (s) ; \ + BLAS_dscal (n, &alpha, (double *) x, 1) ; \ +} +#endif diff --git a/src/maths/UMFPACK/umf_create_element.c b/src/maths/UMFPACK/umf_create_element.c new file mode 100644 index 000000000..bb487ecd1 --- /dev/null +++ b/src/maths/UMFPACK/umf_create_element.c @@ -0,0 +1,593 @@ +/* ========================================================================== */ +/* === UMF_create_element =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_create_element.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) ; + 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/src/maths/UMFPACK/umf_create_element.h b/src/maths/UMFPACK/umf_create_element.h new file mode 100644 index 000000000..b6025bb02 --- /dev/null +++ b/src/maths/UMFPACK/umf_create_element.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_create_element +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) ; diff --git a/src/maths/UMFPACK/umf_dump.c b/src/maths/UMFPACK/umf_dump.c new file mode 100644 index 000000000..1cbf0bd4f --- /dev/null +++ b/src/maths/UMFPACK/umf_dump.c @@ -0,0 +1,1205 @@ +/* ========================================================================== */ +/* === UMF_dump ============================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 */ +) +{ + Entry value ; + Entry *C ; + Int f, nrows, j, jj, len, e, deg, rowcol, 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 ; + 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++) + { + rowcol = Pattern [j] ; + value = + C [ (dumpwhich == 0) ? (f+nrows*j) : (j+nrows*f) ] ; + if (rowcol >= 0) + { + DEBUG8 (("\t\t"ID":", rowcol)) ; + EDEBUG8 (value) ; + DEBUG8 (("\n")) ; + if (dumpwhich == 0) + { + /* col must be in the range 0..n_col-1 */ + ASSERT (rowcol < n_col) ; + } + else + { + /* row must be in the range 0..n_row-1 */ + ASSERT (rowcol < n_row) ; + } + + if (nn <= UMF_DBMAX) + { + if (UMF_DBscatter [rowcol] != UMF_DBflag) + { + UMF_DBpacked [deg++] = rowcol ; + UMF_DBscatter [rowcol] = 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++) + { + rowcol = UMF_DBpacked [j] ; + DEBUG8 ((ID" ", rowcol)) ; + if (j % 20 == 19) DEBUG8 (("\n ")) ; + ASSERT (UMF_DBscatter [rowcol] == 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 ; +#ifdef COMPLEX + Int split = SPLIT (Az) ; +#endif + + 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 (split) + { + DEBUG6 ((" (%e+%ei) ", Ax [p], Az [p])) ; + } + else + { + DEBUG6 ((" (%e+%ei) ", Ax [2*p], Ax [2*p+1])) ; + } +#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 ; + + AMD_debug_init ("from umfpack") ; + + /* 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 ((" MATLAB: ")) ; +#ifdef MATLAB_MEX_FILE + DEBUG0 (("mexFunction.\n")) ; +#else +#ifdef MATHWORKS + DEBUG0 (("yes.\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 (UF_long) %u %u %u\n", + sizeof (UF_long), sizeof (UF_long) / sizeof (UF_long), UNITS (UF_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 nn +) +{ + Int row, col ; + if (Diagonal_map != (Int *) NULL) + { + DEBUG2 (("\nDump the Diagonal_map: nn "ID"\n", nn)) ; + for (col = 0 ; col < nn ; 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/src/maths/UMFPACK/umf_dump.h b/src/maths/UMFPACK/umf_dump.h new file mode 100644 index 000000000..f74d45657 --- /dev/null +++ b/src/maths/UMFPACK/umf_dump.h @@ -0,0 +1,189 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 nn +) ; + +#define UMF_DBMAX 50000 + +#ifndef EXTERN +#define EXTERN extern +#endif + +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/src/maths/UMFPACK/umf_extend_front.c b/src/maths/UMFPACK/umf_extend_front.c new file mode 100644 index 000000000..317c0c687 --- /dev/null +++ b/src/maths/UMFPACK/umf_extend_front.c @@ -0,0 +1,393 @@ +/* ========================================================================== */ +/* === UMF_extend_front ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Called by kernel. */ + +#include "umf_internal.h" +#include "umf_extend_front.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/src/maths/UMFPACK/umf_extend_front.h b/src/maths/UMFPACK/umf_extend_front.h new file mode 100644 index 000000000..6c51986cb --- /dev/null +++ b/src/maths/UMFPACK/umf_extend_front.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_extend_front +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/src/maths/UMFPACK/umf_free.c b/src/maths/UMFPACK/umf_free.c new file mode 100644 index 000000000..867ab223b --- /dev/null +++ b/src/maths/UMFPACK/umf_free.c @@ -0,0 +1,46 @@ +/* ========================================================================== */ +/* === UMF_free ============================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_free.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 AMD/Source/amd_global.c for the memory allocator selection */ + amd_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/src/maths/UMFPACK/umf_free.h b/src/maths/UMFPACK/umf_free.h new file mode 100644 index 000000000..cacf3317a --- /dev/null +++ b/src/maths/UMFPACK/umf_free.h @@ -0,0 +1,10 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void *UMF_free +( + void *p +) ; diff --git a/src/maths/UMFPACK/umf_fsize.c b/src/maths/UMFPACK/umf_fsize.c new file mode 100644 index 000000000..634b525dd --- /dev/null +++ b/src/maths/UMFPACK/umf_fsize.c @@ -0,0 +1,70 @@ +/* ========================================================================== */ +/* === UMF_fsize ============================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_fsize.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/src/maths/UMFPACK/umf_fsize.h b/src/maths/UMFPACK/umf_fsize.h new file mode 100644 index 000000000..1bd83d42f --- /dev/null +++ b/src/maths/UMFPACK/umf_fsize.h @@ -0,0 +1,15 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_garbage_collection.c b/src/maths/UMFPACK/umf_garbage_collection.c new file mode 100644 index 000000000..716270b55 --- /dev/null +++ b/src/maths/UMFPACK/umf_garbage_collection.c @@ -0,0 +1,696 @@ +/* ========================================================================== */ +/* === UMF_garbage_collection =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_garbage_collection.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/src/maths/UMFPACK/umf_garbage_collection.h b/src/maths/UMFPACK/umf_garbage_collection.h new file mode 100644 index 000000000..9d2d8b7bf --- /dev/null +++ b/src/maths/UMFPACK/umf_garbage_collection.h @@ -0,0 +1,14 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_get_memory.c b/src/maths/UMFPACK/umf_get_memory.c new file mode 100644 index 000000000..696955b80 --- /dev/null +++ b/src/maths/UMFPACK/umf_get_memory.c @@ -0,0 +1,223 @@ +/* ========================================================================== */ +/* === UMF_get_memory ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_get_memory.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 +) +{ + double nsize, bsize, tsize ; + Int i, minsize, newsize, newmem, costly, row, col, *Row_tlen, *Col_tlen, + n_row, n_col, *Row_degree, *Col_degree ; + Unit *mnew, *p ; + + /* ---------------------------------------------------------------------- */ + /* 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/src/maths/UMFPACK/umf_get_memory.h b/src/maths/UMFPACK/umf_get_memory.h new file mode 100644 index 000000000..317d97aa6 --- /dev/null +++ b/src/maths/UMFPACK/umf_get_memory.h @@ -0,0 +1,15 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_grow_front.c b/src/maths/UMFPACK/umf_grow_front.c new file mode 100644 index 000000000..611c3a107 --- /dev/null +++ b/src/maths/UMFPACK/umf_grow_front.c @@ -0,0 +1,294 @@ +/* ========================================================================== */ +/* === UMF_grow_front ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Current frontal matrix is too small. Make it bigger. */ + +#include "umf_internal.h" +#include "umf_grow_front.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 */ + /* ---------------------------------------------------------------------- */ + + double s ; + 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 ; + + /* ---------------------------------------------------------------------- */ + /* 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/src/maths/UMFPACK/umf_grow_front.h b/src/maths/UMFPACK/umf_grow_front.h new file mode 100644 index 000000000..807889791 --- /dev/null +++ b/src/maths/UMFPACK/umf_grow_front.h @@ -0,0 +1,14 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_init_front.c b/src/maths/UMFPACK/umf_init_front.c new file mode 100644 index 000000000..b309be49f --- /dev/null +++ b/src/maths/UMFPACK/umf_init_front.c @@ -0,0 +1,268 @@ +/* ========================================================================== */ +/* === UMF_init_front ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" +#include "umf_init_front.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, 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/src/maths/UMFPACK/umf_init_front.h b/src/maths/UMFPACK/umf_init_front.h new file mode 100644 index 000000000..9801b7280 --- /dev/null +++ b/src/maths/UMFPACK/umf_init_front.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_init_front +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/src/maths/UMFPACK/umf_internal.h b/src/maths/UMFPACK/umf_internal.h new file mode 100644 index 000000000..02bb1f7f1 --- /dev/null +++ b/src/maths/UMFPACK/umf_internal.h @@ -0,0 +1,738 @@ +/* ========================================================================== */ +/* === umf_internal.h ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_". +*/ + +#ifndef _UMF_INTERNAL +#define _UMF_INTERNAL + +/* -------------------------------------------------------------------------- */ +/* 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 (__APPLE__) +#define UMF_MAC +#define UMFPACK_ARCHITECTURE "Mac" + +#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) +#if defined (__MINGW32__) +#define UMF_MINGW +#elif defined (__CYGWIN32__) +#define UMF_CYGWIN +#else +#define UMF_WINDOWS +#endif +#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 */ +#include "amd_internal.h" + +/* -------------------------------------------------------------------------- */ +/* MATLAB include files */ +/* -------------------------------------------------------------------------- */ + +/* only used when compiling the UMFPACK mexFunction */ +#ifdef MATLAB_MEX_FILE +#include "matrix.h" +#include "mex.h" +#endif + +/* -------------------------------------------------------------------------- */ +/* Real/complex and int/UF_long definitions, double relops */ +/* -------------------------------------------------------------------------- */ + +#include "umf_version.h" + +/* -------------------------------------------------------------------------- */ +/* Compile-time configurations */ +/* -------------------------------------------------------------------------- */ + +#include "umf_config.h" + +/* -------------------------------------------------------------------------- */ +/* umfpack include file */ +/* -------------------------------------------------------------------------- */ + +#include "ngspice/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 */ +/* -------------------------------------------------------------------------- */ + +/* See AMD/Source/amd_global.c and AMD/Source/amd.h for the + * definition of the memory allocator used by UMFPACK. Versions 4.4 and + * earlier had their memory allocator definitions here. Other global + * function pointers for UMFPACK are located in umf_global.c. + * + * The MATLAB mexFunction uses MATLAB's memory manager and mexPrintf, while the + * C-callable AMD library uses the ANSI C malloc, free, realloc, and printf + * routines. + */ + +/* -------------------------------------------------------------------------- */ +/* 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 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" */ + 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 + +#ifndef EXTERN +#define EXTERN extern +#endif + +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 + +#endif diff --git a/src/maths/UMFPACK/umf_is_permutation.c b/src/maths/UMFPACK/umf_is_permutation.c new file mode 100644 index 000000000..689c65eef --- /dev/null +++ b/src/maths/UMFPACK/umf_is_permutation.c @@ -0,0 +1,56 @@ +/* ========================================================================== */ +/* === UMF_is_permutation =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_is_permutation.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/src/maths/UMFPACK/umf_is_permutation.h b/src/maths/UMFPACK/umf_is_permutation.h new file mode 100644 index 000000000..2468545ee --- /dev/null +++ b/src/maths/UMFPACK/umf_is_permutation.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_kernel.c b/src/maths/UMFPACK/umf_kernel.c new file mode 100644 index 000000000..6168829d8 --- /dev/null +++ b/src/maths/UMFPACK/umf_kernel.c @@ -0,0 +1,300 @@ +/* ========================================================================== */ +/* === UMF_kernel =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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.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/src/maths/UMFPACK/umf_kernel.h b/src/maths/UMFPACK/umf_kernel.h new file mode 100644 index 000000000..0f5d70d61 --- /dev/null +++ b/src/maths/UMFPACK/umf_kernel.h @@ -0,0 +1,18 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_kernel_init.c b/src/maths/UMFPACK/umf_kernel_init.c new file mode 100644 index 000000000..7c181f8e1 --- /dev/null +++ b/src/maths/UMFPACK/umf_kernel_init.c @@ -0,0 +1,1066 @@ +/* ========================================================================== */ +/* === UMF_kernel_init ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_kernel_init.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_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 ; + double s ; + Entry *Bx, *Bx2 ; + Int p, i, len, len_new, *Bi, *Bi2 ; + + /* 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 */ + /* ---------------------------------------------------------------------- */ + + Entry x, pivot_value ; + double unused = 0, rsmin, rsmax, rs, droptol ; + Entry *D, *C, *Lval, **Rpx ; + double *Rs ; + 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 ; + Element *ep ; + Unit *Memory ; +#ifdef COMPLEX + Int split = SPLIT (Az) ; +#endif +#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++) + { + Entry aij ; + double value ; + 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, Az, p, split) ; + 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) != AMD_OK) + { + /* 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) ; + +#ifndef NDEBUG + for (i = 0 ; i < nn ; i++) + { + Diagonal_map [i] = EMPTY ; + Diagonal_imap [i] = EMPTY ; + } +#endif + + for (k = 0 ; k < nn ; k++) + { + newrow = Symbolic->Diagonal_map [k] ; + Diagonal_map [k] = newrow ; + Diagonal_imap [newrow] = k ; + } + +#ifndef NDEBUG + for (i = 0 ; i < nn ; i++) + { + ASSERT (Diagonal_map [i] != EMPTY) ; + ASSERT (Diagonal_imap [i] != EMPTY) ; + } +#endif + } + + /* ---------------------------------------------------------------------- */ + /* 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, Az, pa, split) ; + + /* scale the value using the scale factors, Rs */ + if (do_scale) + { +#ifndef NRECIPROCAL + if (do_recip) + { + SCALE (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 lengthy) :: */ + DEBUGm4 (("bad U singleton row (too lengthy)\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 lengthy)\n")) ; + return (FALSE) ; /* pattern changed */ + } + Li [llen] = newrow ; + Lval [llen] = x ; + llen++ ; + } + } + + if (llen != lilen) + { + /* :: pattern change (singleton column too lengthy) :: */ + 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) ; + + /* 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, Az, pa, split) ; + + /* scale the value using the scale factors, Rs */ + if (do_scale) + { +#ifndef NRECIPROCAL + if (do_recip) + { + /* multiply by the reciprocal */ + SCALE (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 lengthy)\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 lengthy)\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) ; /* pattern changed */ + } + } + +#ifndef NDEBUG + if (prefer_diagonal) + { + Entry aij ; + Int *InvCperm, newcol ; + UMF_dump_diagonal_map (Diagonal_map, Diagonal_imap, nn) ; + 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, Az, p, split) ; + 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 (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/src/maths/UMFPACK/umf_kernel_init.h b/src/maths/UMFPACK/umf_kernel_init.h new file mode 100644 index 000000000..dc86d0477 --- /dev/null +++ b/src/maths/UMFPACK/umf_kernel_init.h @@ -0,0 +1,18 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_kernel_wrapup.c b/src/maths/UMFPACK/umf_kernel_wrapup.c new file mode 100644 index 000000000..d9c3cd5db --- /dev/null +++ b/src/maths/UMFPACK/umf_kernel_wrapup.c @@ -0,0 +1,467 @@ +/* ========================================================================== */ +/* === UMF_kernel_wrapup ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The matrix is factorized. Finish the LU data structure. */ + +#include "umf_internal.h" +#include "umf_kernel_wrapup.h" + +GLOBAL void UMF_kernel_wrapup +( + NumericType *Numeric, + SymbolicType *Symbolic, + WorkType *Work +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Entry pivot_value ; + double d ; + Entry *D ; + 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 ; + +#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/src/maths/UMFPACK/umf_kernel_wrapup.h b/src/maths/UMFPACK/umf_kernel_wrapup.h new file mode 100644 index 000000000..e3e8fbdfb --- /dev/null +++ b/src/maths/UMFPACK/umf_kernel_wrapup.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_kernel_wrapup +( + NumericType *Numeric, + SymbolicType *Symbolic, + WorkType *Work +) ; diff --git a/src/maths/UMFPACK/umf_local_search.c b/src/maths/UMFPACK/umf_local_search.c new file mode 100644 index 000000000..ecfb3881c --- /dev/null +++ b/src/maths/UMFPACK/umf_local_search.c @@ -0,0 +1,1955 @@ +/* ========================================================================== */ +/* === UMF_local_search ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_local_search.h" +#include "umf_row_search.h" +#include "umf_mem_free_tail_block.h" + +/* relaxed amalgamation control parameters are fixed at compile time */ +#define RELAX1 0.25 +#define SYM_RELAX1 0.0 +#define RELAX2 0.1 +#define RELAX3 0.125 + +/* ========================================================================== */ +/* === 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 */ + /* ---------------------------------------------------------------------- */ + + double relax1 ; + Entry *Flblock, *Fublock, *Fs, *Fcblock, *C, *Wx, *Wy, *Fu, *Flublock, + *Flu ; + 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 NBLAS + Int blas_ok = TRUE ; +#else +#define blas_ok FALSE +#endif + +#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 */ + +#ifndef NBLAS + BLAS_TRSV (fnpiv, Flublock, Flu, nb) ; +#endif + if (!blas_ok) + { + /* use plain C code if no BLAS, or on integer overflow */ + 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 ; + } + } + } + + /* ------------------------------------------------------------------ */ + /* 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 NBLAS + /* 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) + { + Diagonal_map = Work->Diagonal_map ; + Diagonal_imap = Work->Diagonal_imap ; + ASSERT (Diagonal_map != (Int *) NULL) ; + ASSERT (Diagonal_imap != (Int *) NULL) ; + +#ifndef NDEBUG + UMF_dump_diagonal_map (Diagonal_map, Diagonal_imap, Symbolic->n_col) ; +#endif + + 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->n_col) ; +#endif + } + + return (UMFPACK_OK) ; +} diff --git a/src/maths/UMFPACK/umf_local_search.h b/src/maths/UMFPACK/umf_local_search.h new file mode 100644 index 000000000..434d66f7f --- /dev/null +++ b/src/maths/UMFPACK/umf_local_search.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_local_search +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) ; diff --git a/src/maths/UMFPACK/umf_lsolve.c b/src/maths/UMFPACK/umf_lsolve.c new file mode 100644 index 000000000..7e1943c4e --- /dev/null +++ b/src/maths/UMFPACK/umf_lsolve.c @@ -0,0 +1,152 @@ +/* ========================================================================== */ +/* === UMF_lsolve =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_lsolve.h" + +GLOBAL double UMF_lsolve +( + NumericType *Numeric, + Entry X [ ], /* b on input, solution x on output */ + Int Pattern [ ] /* a work array of size n */ +) +{ + Entry xk ; + Entry *xp, *Lval ; + Int k, deg, *ip, j, row, *Lpos, *Lilen, *Lip, llen, lp, newLchain, + pos, npiv, n1, *Li ; + + /* ---------------------------------------------------------------------- */ + + 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/src/maths/UMFPACK/umf_lsolve.h b/src/maths/UMFPACK/umf_lsolve.h new file mode 100644 index 000000000..211adffe9 --- /dev/null +++ b/src/maths/UMFPACK/umf_lsolve.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL double UMF_lsolve +( + NumericType *Numeric, + Entry X [ ], + Int Pattern [ ] +) ; diff --git a/src/maths/UMFPACK/umf_ltsolve.c b/src/maths/UMFPACK/umf_ltsolve.c new file mode 100644 index 000000000..1792dfa91 --- /dev/null +++ b/src/maths/UMFPACK/umf_ltsolve.c @@ -0,0 +1,226 @@ +/* ========================================================================== */ +/* === UMF_ltsolve ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_ltsolve.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 */ +) +{ + Entry xk ; + Entry *xp, *Lval ; + Int k, deg, *ip, j, row, *Lpos, *Lilen, kstart, kend, *Lip, llen, + lp, pos, npiv, n1, *Li ; + + /* ---------------------------------------------------------------------- */ + + 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/src/maths/UMFPACK/umf_ltsolve.h b/src/maths/UMFPACK/umf_ltsolve.h new file mode 100644 index 000000000..02f77340e --- /dev/null +++ b/src/maths/UMFPACK/umf_ltsolve.h @@ -0,0 +1,19 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_malloc.c b/src/maths/UMFPACK/umf_malloc.c new file mode 100644 index 000000000..d7ff56135 --- /dev/null +++ b/src/maths/UMFPACK/umf_malloc.c @@ -0,0 +1,92 @@ +/* ========================================================================== */ +/* === UMF_malloc =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_malloc.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 AMD/Source/amd_global.c for the memory allocator selection */ + p = amd_malloc (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/src/maths/UMFPACK/umf_malloc.h b/src/maths/UMFPACK/umf_malloc.h new file mode 100644 index 000000000..8ee993407 --- /dev/null +++ b/src/maths/UMFPACK/umf_malloc.h @@ -0,0 +1,25 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#ifndef _UMF_MALLOC +#define _UMF_MALLOC + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) + +#ifndef EXTERN +#define EXTERN extern +#endif + +GLOBAL EXTERN Int UMF_malloc_count ; +#endif + +GLOBAL void *UMF_malloc +( + Int n_objects, + size_t size_of_object +) ; + +#endif diff --git a/src/maths/UMFPACK/umf_mem_alloc_element.c b/src/maths/UMFPACK/umf_mem_alloc_element.c new file mode 100644 index 000000000..cac0db880 --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_alloc_element.c @@ -0,0 +1,83 @@ +/* ========================================================================== */ +/* === UMF_mem_alloc_element ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_element.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/src/maths/UMFPACK/umf_mem_alloc_element.h b/src/maths/UMFPACK/umf_mem_alloc_element.h new file mode 100644 index 000000000..e0b259f5d --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_alloc_element.h @@ -0,0 +1,17 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_mem_alloc_head_block.c b/src/maths/UMFPACK/umf_mem_alloc_head_block.c new file mode 100644 index 000000000..6d7355c51 --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_alloc_head_block.c @@ -0,0 +1,55 @@ +/* ========================================================================== */ +/* === UMF_mem_alloc_head_block ============================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_mem_alloc_head_block.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/src/maths/UMFPACK/umf_mem_alloc_head_block.h b/src/maths/UMFPACK/umf_mem_alloc_head_block.h new file mode 100644 index 000000000..510a6fa03 --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_alloc_head_block.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_mem_alloc_head_block +( + NumericType *Numeric, + Int nunits +) ; diff --git a/src/maths/UMFPACK/umf_mem_alloc_tail_block.c b/src/maths/UMFPACK/umf_mem_alloc_tail_block.c new file mode 100644 index 000000000..a16698a43 --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_alloc_tail_block.c @@ -0,0 +1,134 @@ +/* ========================================================================== */ +/* === UMF_mem_alloc_tail_block ============================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +#include "umf_internal.h" +#include "umf_mem_alloc_tail_block.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/src/maths/UMFPACK/umf_mem_alloc_tail_block.h b/src/maths/UMFPACK/umf_mem_alloc_tail_block.h new file mode 100644 index 000000000..d072a73ac --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_alloc_tail_block.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_mem_alloc_tail_block +( + NumericType *Numeric, + Int nunits +) ; diff --git a/src/maths/UMFPACK/umf_mem_free_tail_block.c b/src/maths/UMFPACK/umf_mem_free_tail_block.c new file mode 100644 index 000000000..9bec421d9 --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_free_tail_block.c @@ -0,0 +1,143 @@ +/* ========================================================================== */ +/* === UMF_mem_free_tail_block ============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_mem_free_tail_block.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/src/maths/UMFPACK/umf_mem_free_tail_block.h b/src/maths/UMFPACK/umf_mem_free_tail_block.h new file mode 100644 index 000000000..14c7324de --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_free_tail_block.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_mem_free_tail_block +( + NumericType *Numeric, + Int i +) ; diff --git a/src/maths/UMFPACK/umf_mem_init_memoryspace.c b/src/maths/UMFPACK/umf_mem_init_memoryspace.c new file mode 100644 index 000000000..0e05f5c55 --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_init_memoryspace.c @@ -0,0 +1,65 @@ +/* ========================================================================== */ +/* === UMF_mem_init_memoryspace ============================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +#include "umf_internal.h" +#include "umf_mem_init_memoryspace.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/src/maths/UMFPACK/umf_mem_init_memoryspace.h b/src/maths/UMFPACK/umf_mem_init_memoryspace.h new file mode 100644 index 000000000..45d552fb8 --- /dev/null +++ b/src/maths/UMFPACK/umf_mem_init_memoryspace.h @@ -0,0 +1,10 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_mem_init_memoryspace +( + NumericType *Numeric +) ; diff --git a/src/maths/UMFPACK/umf_realloc.c b/src/maths/UMFPACK/umf_realloc.c new file mode 100644 index 000000000..feae25ada --- /dev/null +++ b/src/maths/UMFPACK/umf_realloc.c @@ -0,0 +1,76 @@ +/* ========================================================================== */ +/* === UMF_realloc ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_realloc.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 AMD/Source/amd_global.c for the memory allocator selection */ + p2 = amd_realloc (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/src/maths/UMFPACK/umf_realloc.h b/src/maths/UMFPACK/umf_realloc.h new file mode 100644 index 000000000..5b2f1c257 --- /dev/null +++ b/src/maths/UMFPACK/umf_realloc.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_report_perm.c b/src/maths/UMFPACK/umf_report_perm.c new file mode 100644 index 000000000..aa5260dbd --- /dev/null +++ b/src/maths/UMFPACK/umf_report_perm.c @@ -0,0 +1,86 @@ +/* ========================================================================== */ +/* === UMF_report_perm ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" +#include "umf_report_perm.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/src/maths/UMFPACK/umf_report_perm.h b/src/maths/UMFPACK/umf_report_perm.h new file mode 100644 index 000000000..6b18d85b7 --- /dev/null +++ b/src/maths/UMFPACK/umf_report_perm.h @@ -0,0 +1,14 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_report_vector.c b/src/maths/UMFPACK/umf_report_vector.c new file mode 100644 index 000000000..553368318 --- /dev/null +++ b/src/maths/UMFPACK/umf_report_vector.c @@ -0,0 +1,111 @@ +/* ========================================================================== */ +/* === UMF_report_vector ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" +#include "umf_report_vector.h" + +/* ========================================================================== */ +/* === print_value ========================================================== */ +/* ========================================================================== */ + +PRIVATE void print_value +( + Int i, + const double Xx [ ], + const double Xz [ ], /* used for complex case only */ + Int scalar /* if true, then print real part only */ +) +{ + Entry xi ; + /* if Xz is null, then X is in "merged" format (compatible with Entry, */ + /* and ANSI C99 double _Complex type). */ + PRINTF ((" "ID" :", INDEX (i))) ; + if (scalar) + { + PRINT_SCALAR (Xx [i]) ; + } + else + { + ASSIGN (xi, Xx, Xz, i, SPLIT (Xz)) ; + 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/src/maths/UMFPACK/umf_report_vector.h b/src/maths/UMFPACK/umf_report_vector.h new file mode 100644 index 000000000..6a758d735 --- /dev/null +++ b/src/maths/UMFPACK/umf_report_vector.h @@ -0,0 +1,15 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_row_search.c b/src/maths/UMFPACK/umf_row_search.c new file mode 100644 index 000000000..f20ca0ca5 --- /dev/null +++ b/src/maths/UMFPACK/umf_row_search.c @@ -0,0 +1,837 @@ +/* ========================================================================== */ +/* === UMF_row_search ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_row_search.h b/src/maths/UMFPACK/umf_row_search.h new file mode 100644 index 000000000..e0171ee89 --- /dev/null +++ b/src/maths/UMFPACK/umf_row_search.h @@ -0,0 +1,32 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_scale.c b/src/maths/UMFPACK/umf_scale.c new file mode 100644 index 000000000..04f09dff3 --- /dev/null +++ b/src/maths/UMFPACK/umf_scale.c @@ -0,0 +1,82 @@ +/* ========================================================================== */ +/* === UMF_scale ============================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Divide a vector of stride 1 by the pivot value. */ + +#include "umf_internal.h" +#include "umf_scale.h" + +GLOBAL void UMF_scale +( + Int n, + Entry pivot, + Entry X [ ] +) +{ + Entry x ; + double s ; + Int i ; + + /* ---------------------------------------------------------------------- */ + /* 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 */ + /* ------------------------------------------------------------------ */ + + /* The pivot is not tiny, and is not NaN. Don't bother to check for + * zeros in the pivot column, X. This is slightly more accurate than + * multiplying by 1/pivot (but slightly slower), particularly if the + * pivot column consists of only IEEE subnormals. */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] /= pivot ; */ + x = X [i] ; + DIV (X [i], x, pivot) ; + } + } +} diff --git a/src/maths/UMFPACK/umf_scale.h b/src/maths/UMFPACK/umf_scale.h new file mode 100644 index 000000000..99799f52a --- /dev/null +++ b/src/maths/UMFPACK/umf_scale.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_scale +( + Int n, + Entry alpha, + Entry X [ ] +) ; diff --git a/src/maths/UMFPACK/umf_scale_column.c b/src/maths/UMFPACK/umf_scale_column.c new file mode 100644 index 000000000..9fd37964e --- /dev/null +++ b/src/maths/UMFPACK/umf_scale_column.c @@ -0,0 +1,434 @@ +/* ========================================================================== */ +/* === UMF_scale_column ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_scale_column.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 */ + /* ---------------------------------------------------------------------- */ + + Entry pivot_value ; + Entry *Fcol, *Flublock, *Flblock, *Fublock, *Fcblock ; + 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 ; +#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/src/maths/UMFPACK/umf_scale_column.h b/src/maths/UMFPACK/umf_scale_column.h new file mode 100644 index 000000000..230678783 --- /dev/null +++ b/src/maths/UMFPACK/umf_scale_column.h @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL void UMF_scale_column +( + NumericType *Numeric, + WorkType *Work +) ; diff --git a/src/maths/UMFPACK/umf_set_stats.c b/src/maths/UMFPACK/umf_set_stats.c new file mode 100644 index 000000000..fb1f23211 --- /dev/null +++ b/src/maths/UMFPACK/umf_set_stats.c @@ -0,0 +1,134 @@ +/* ========================================================================== */ +/* === UMF_set_stats ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_set_stats.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/src/maths/UMFPACK/umf_set_stats.h b/src/maths/UMFPACK/umf_set_stats.h new file mode 100644 index 000000000..f4342ce58 --- /dev/null +++ b/src/maths/UMFPACK/umf_set_stats.h @@ -0,0 +1,24 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_singletons.c b/src/maths/UMFPACK/umf_singletons.c new file mode 100644 index 000000000..3c4f32a50 --- /dev/null +++ b/src/maths/UMFPACK/umf_singletons.c @@ -0,0 +1,935 @@ +/* ========================================================================== */ +/* === UMF_singletons ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 + * . x x x x x x x x + * . . x x x x x x x + * . . . x . . . . . + * . . . x x . . . . + * . . . x x s s s s + * . . . x x s s s s + * . . . x x s s s s + * . . . 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, CSparse cs_dmperm, + * and SuiteSparse/BTF). + */ + +#include "umf_internal.h" +#include "umf_singletons.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) ; + DEBUG0 (("empty k "ID"\n", k)) ; + 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 */ + Int strategy, /* strategy requested by user */ + Int do_singletons, /* if false, then do not look for singletons */ + + /* 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 (!do_singletons) + { + /* do not look for singletons at all */ + n1 = 0 ; + n1r = 0 ; + n1c = 0 ; + } + else if (Quser != (Int *) NULL) + { + /* user has provided an input column ordering */ + if (strategy == UMFPACK_STRATEGY_UNSYMMETRIC) + { + /* 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 + { + /* do not look for singletons if Quser given and strategy is + * not unsymmetric */ + n1 = 0 ; + n1r = 0 ; + n1c = 0 ; + } + } + 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 */ + /* ---------------------------------------------------------------------- */ + + if (Quser != (Int *) NULL && strategy == UMFPACK_STRATEGY_SYMMETRIC) + { + /* rows should be symmetrically permuted according to Quser */ + ASSERT (n_row == n_col) ; + nempty_row = finish_permutation (n1, n_row, Rdeg, Quser, Rperm, + &max_rdeg) ; + } + else + { + /* rows should not be symmetrically permuted according to Quser */ + 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 */ + /* ---------------------------------------------------------------------- */ + + /* The prior version of this code (with a "break" statement; UMFPACK 5.2) + * causes UMFPACK to fail when optimization is enabled with gcc version + * 4.2.4 in a 64-bit Linux environment. The bug is a compiler bug, not a + * an UMFPACK bug. It is fixed in gcc version 4.3.2. However, as a + * workaround for the compiler, the code below has been "fixed". */ + + 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 ; /* replaced the break with this test: */ is_sym && + /* the remainder of this test is unchanged from v5.2.0: */ + s < n_col - nempty_col ; s++) + { + if (Cperm [s] != Rperm [s]) + { + is_sym = FALSE ; + /* removed a break statement here, which is OK but it tickles + * the gcc 4.2.{3,4} compiler bug */ + } + } + } + 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/src/maths/UMFPACK/umf_singletons.h b/src/maths/UMFPACK/umf_singletons.h new file mode 100644 index 000000000..9addf7436 --- /dev/null +++ b/src/maths/UMFPACK/umf_singletons.h @@ -0,0 +1,32 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 strategy, + Int do_singletons, + 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/src/maths/UMFPACK/umf_solve.c b/src/maths/UMFPACK/umf_solve.c new file mode 100644 index 000000000..42bf87938 --- /dev/null +++ b/src/maths/UMFPACK/umf_solve.c @@ -0,0 +1,1396 @@ +/* ========================================================================== */ +/* === UMF_solve ============================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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). + + Added on option that allows the complex A and X to be split differently + than B, Oct 10, 2005. Contributed by David Bateman. +*/ + +#include "umf_internal.h" +#include "umf_solve.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 */ + /* ---------------------------------------------------------------------- */ + + Entry axx, wi, xj, zi, xi, aij, bi ; + double omega [3], d, z2i, yi, flops ; + Entry *W, *Z, *S, *X ; + double *Z2, *Y, *B2, *Rs ; + Int *Rperm, *Cperm, i, n, p, step, j, nz, status, p2, do_scale ; +#ifdef COMPLEX + Int AXsplit ; + Int Bsplit ; +#endif +#ifndef NRECIPROCAL + Int do_recip = Numeric->do_recip ; +#endif + + /* ---------------------------------------------------------------------- */ + /* initializations */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + UMF_dump_lu (Numeric) ; + ASSERT (Numeric && Xx && Bx && Pattern && SolveWork && Info) ; +#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 + if (irstep > 0) + { + if (!Ap || !Ai || !Ax) + { + return (UMFPACK_ERROR_argument_missing) ; + } + /* A, B, and X in split format if Az, Bz, and Xz present */ + AXsplit = SPLIT (Az) || SPLIT(Xz); + 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 + { + /* A is ignored, only look at X for split/packed cases */ + AXsplit = SPLIT(Xz); + } + Bsplit = SPLIT (Bz); + + if (AXsplit) + { + X = (Entry *) (SolveWork + 2*n) ; /* Entry X [0..n-1] */ + } + else + { + X = (Entry *) Xx ; /* Entry X [0..n-1] */ + } +#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, Az, p, AXsplit) ; + 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, Bz, i, Bsplit) ; + 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, Bz, i, Bsplit) ; + SCALE (X [i], Rs [i]) ; + } + } + else +#endif + { + /* divide by the scale factors */ + for (i = 0 ; i < n ; i++) + { + ASSIGN (X [i], Bx, Bz, i, Bsplit) ; + 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, Bz, Rperm [i], Bsplit) ; + } + } + } + else + { + for (i = 0 ; i < n ; i++) + { + /* Z [i] = B [i] ; */ + ASSIGN (Z [i], Bx, Bz, i, Bsplit) ; + } + 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, Az, p, AXsplit) ; + 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 (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, Bz, i, Bsplit) ; + 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, Az, p, AXsplit) ; + 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 (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, Az, p, AXsplit) ; + 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, Az, p, AXsplit) ; + 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, Az, p, AXsplit) ; + 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, Bz, i, Bsplit) ; + 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, Bz, Cperm [i], Bsplit) ; + } + } + else + { + /* Z = b - A' x */ + for (i = 0 ; i < n ; i++) + { + /* Z [i] = B [i] ; */ + ASSIGN (Z [i], Bx, Bz, i, Bsplit) ; + } + 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, Az, p, Bsplit) ; + 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 (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 (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] += Z [i] ; was +=W[i] in v4.3, which is wrong */ + ASSEMBLE (X [i], Z [i]) ; /* bug fix, v4.3.1 */ + } + } + + /* -------------------------------------------------------------- */ + /* 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, Bz, i, Bsplit) ; + z2i = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* axx = conjugate (Ax [p]) * X [Ai [p]] ; */ + ASSIGN (aij, Ax, Az, p, AXsplit) ; + 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, Az, p, AXsplit) ; + 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, Az, p, AXsplit) ; + 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, Az, p, AXsplit) ; + 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, Bz, i, Bsplit) ; + 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, Bz, Cperm [i], Bsplit) ; + } + } + else + { + /* Z = b - A.' x */ + for (i = 0 ; i < n ; i++) + { + /* Z [i] = B [i] ; */ + ASSIGN (Z [i], Bx, Bz, i, Bsplit) ; + } + 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, Az, p, AXsplit) ; + 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 (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 (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] += Z [i] ; was +=W[i] in v4.3, which is wrong */ + ASSEMBLE (X [i], Z [i]) ; /* bug fix, v4.3.1 */ + } + } + + /* -------------------------------------------------------------- */ + /* 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, Bz, i, Bsplit) ; + z2i = 0. ; + p2 = Ap [i+1] ; + for (p = Ap [i] ; p < p2 ; p++) + { + /* axx = Ax [p] * X [Ai [p]] ; */ + ASSIGN (aij, Ax, Az, p, AXsplit) ; + 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, Bz, Rperm [i], Bsplit) ; + } + 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, Bz, i, Bsplit) ; + } + 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, Bz, i, Bsplit) ; + } + 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, Bz, i, Bsplit) ; + } + 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, Bz, i, Bsplit) ; + } + 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, Bz, i, Bsplit) ; + } + 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, Bz, i, Bsplit) ; + } + 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, Bz, i, Bsplit) ; + } + 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, Bz, Cperm [i], Bsplit) ; + } + 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, Bz, Cperm [i], Bsplit) ; + } + 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, Bz, i, Bsplit) ; + } + 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, Bz, i, Bsplit) ; + } + 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 [ ] */ + if (AXsplit) + { + 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/src/maths/UMFPACK/umf_solve.h b/src/maths/UMFPACK/umf_solve.h new file mode 100644 index 000000000..80a796ca2 --- /dev/null +++ b/src/maths/UMFPACK/umf_solve.h @@ -0,0 +1,25 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_start_front.c b/src/maths/UMFPACK/umf_start_front.c new file mode 100644 index 000000000..47aa354f5 --- /dev/null +++ b/src/maths/UMFPACK/umf_start_front.c @@ -0,0 +1,284 @@ +/* ========================================================================== */ +/* === UMF_start_front ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_start_front.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 +) +{ + double maxbytes ; + Int fnrows_max, fncols_max, fnr2, fnc2, fsize, fcurr_size, maxfrsize, + overflow, nb, f, cdeg ; + + 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/src/maths/UMFPACK/umf_start_front.h b/src/maths/UMFPACK/umf_start_front.h new file mode 100644 index 000000000..1a1c4f224 --- /dev/null +++ b/src/maths/UMFPACK/umf_start_front.h @@ -0,0 +1,13 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_store_lu.c b/src/maths/UMFPACK/umf_store_lu.c new file mode 100644 index 000000000..97e10816f --- /dev/null +++ b/src/maths/UMFPACK/umf_store_lu.c @@ -0,0 +1,1057 @@ +/* ========================================================================== */ +/* === UMF_store_lu ========================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_store_lu.h" +#include "umf_mem_alloc_head_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 */ + /* ---------------------------------------------------------------------- */ + + Entry pivot_value ; +#ifdef DROP + double droptol ; +#endif + Entry *D, *Lval, *Uval, *Fl1, *Fl2, *Fu1, *Fu2, + *Flublock, *Flblock, *Fublock ; + 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] ; + +#ifndef NDEBUG + Int *Col_degree, *Row_degree ; +#endif + +#ifdef DROP + Int all_lnz, all_unz ; + 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 (("\nSTORE 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++) + { + Entry x ; + double s ; + 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++) + { + Entry x ; + double s ; + 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++) + { + Entry x ; + double s ; + Int row2, pos ; + 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++) + { + Entry x ; + double s ; + Int row2, pos ; + 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++) + { + Entry x ; + Int row2, pos ; + 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++) + { + Entry x ; + Int row2, pos ; + 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++) + { + Entry x ; + double s ; + Int row2, pos ; + 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++) + { + Entry x ; + double s ; + Int row2, pos ; + 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++) + { + Entry x ; + Int row2, pos ; + 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++) + { + Entry x ; + Int row2, pos ; + 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++) + { + Entry x ; + double s ; + 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++) + { + Entry x ; + double s ; + 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++) + { + Entry x ; + double s ; + Int col2, pos ; + 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++) + { + Entry x ; + double s ; + Int col2, pos ; + 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++) + { + Entry x ; + Int col2, pos ; + 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++) + { + Entry x ; + Int col2, pos ; + 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++) + { + Entry x ; + double s ; + Int col2, pos ; + 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++) + { + Entry x ; + double s ; + Int col2, pos ; + 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++) + { + Entry x ; + Int col2, pos ; + 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++) + { + Entry x ; + Int col2, pos ; + 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/src/maths/UMFPACK/umf_store_lu.h b/src/maths/UMFPACK/umf_store_lu.h new file mode 100644 index 000000000..82627ada6 --- /dev/null +++ b/src/maths/UMFPACK/umf_store_lu.h @@ -0,0 +1,17 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_symbolic_usage.c b/src/maths/UMFPACK/umf_symbolic_usage.c new file mode 100644 index 000000000..31abdf919 --- /dev/null +++ b/src/maths/UMFPACK/umf_symbolic_usage.c @@ -0,0 +1,46 @@ +/* ========================================================================== */ +/* === UMF_symbolic_usage =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Returns the final size of the Symbolic object, in Units */ + +#include "umf_internal.h" +#include "umf_symbolic_usage.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/src/maths/UMFPACK/umf_symbolic_usage.h b/src/maths/UMFPACK/umf_symbolic_usage.h new file mode 100644 index 000000000..3fad24a53 --- /dev/null +++ b/src/maths/UMFPACK/umf_symbolic_usage.h @@ -0,0 +1,15 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_transpose.c b/src/maths/UMFPACK/umf_transpose.c new file mode 100644 index 000000000..6ac743503 --- /dev/null +++ b/src/maths/UMFPACK/umf_transpose.c @@ -0,0 +1,401 @@ +/* ========================================================================== */ +/* === UMF_transpose ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_transpose.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 ; +#ifdef COMPLEX + Int split ; +#endif + + /* ---------------------------------------------------------------------- */ + /* 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) != AMD_OK) + { + 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 + split = SPLIT (Az) && SPLIT (Rz) ; + + if (do_conjugate && do_values) + { + if (Q != (Int *) NULL) + { + if (split) + { + /* 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,Q)' (merged complex values) */ + 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 [2*bp] = Ax [2*p] ; + Rx [2*bp+1] = -Ax [2*p+1] ; + } + } + } + } + else + { + if (split) + { + /* 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 + { + /* R = A (P,:)' (merged complex values) */ + for (j = 0 ; j < n_col ; j++) + { + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + bp = W [Ai [p]]++ ; + Ri [bp] = j ; + Rx [2*bp] = Ax [2*p] ; + Rx [2*bp+1] = -Ax [2*p+1] ; + } + } + } + } + } + else +#endif + { + if (Q != (Int *) NULL) + { + if (do_values) + { +#ifdef COMPLEX + if (split) +#endif + { + /* 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 + } + } + } +#ifdef COMPLEX + else + { + /* R = A (P,Q).' (merged complex values) */ + 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 [2*bp] = Ax [2*p] ; + Rx [2*bp+1] = Ax [2*p+1] ; + } + } + } +#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) + { +#ifdef COMPLEX + if (split) +#endif + { + /* 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 + } + } + } +#ifdef COMPLEX + else + { + /* R = A (P,:).' (merged complex values) */ + for (j = 0 ; j < n_col ; j++) + { + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + bp = W [Ai [p]]++ ; + Ri [bp] = j ; + Rx [2*bp] = Ax [2*p] ; + Rx [2*bp+1] = Ax [2*p+1] ; + } + } + } +#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) == AMD_OK) ; +#endif + + return (UMFPACK_OK) ; +} diff --git a/src/maths/UMFPACK/umf_transpose.h b/src/maths/UMFPACK/umf_transpose.h new file mode 100644 index 000000000..4c4dee911 --- /dev/null +++ b/src/maths/UMFPACK/umf_transpose.h @@ -0,0 +1,27 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_triplet.c b/src/maths/UMFPACK/umf_triplet.c new file mode 100644 index 000000000..aaca50c6e --- /dev/null +++ b/src/maths/UMFPACK/umf_triplet.c @@ -0,0 +1,429 @@ +/* ========================================================================== */ +/* === UMF_triplet ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 occurred. + + 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_triplet.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 +#ifdef DO_VALUES +#ifdef COMPLEX + Int split = SPLIT (Tz) && SPLIT (Az) && SPLIT (Rz) ; +#endif +#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, Tz, k, split) ; + 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 +#ifdef COMPLEX + if (split) + { + Rx [p] = Tx [k] ; + Rz [p] = Tz [k] ; + } + else + { + Rx [2*p ] = Tx [2*k ] ; + Rx [2*p+1] = Tx [2*k+1] ; + } +#else + Rx [p] = Tx [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 */ +#ifdef COMPLEX + if (split) + { + Rx [pj] += Rx [p] ; + Rz [pj] += Rz [p] ; + } + else + { + Rx[2*pj ] += Rx[2*p ] ; + Rx[2*pj+1] += Rx[2*p+1] ; + } +#else + Rx [pj] += Rx [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 +#ifdef COMPLEX + if (split) + { + Rx [pdest] = Rx [p] ; + Rz [pdest] = Rz [p] ; + } + else + { + Rx [2*pdest ] = Rx [2*p ] ; + Rx [2*pdest+1] = Rx [2*p+1] ; + } +#else + Rx [pdest] = Rx [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 +#ifdef COMPLEX + if (split) + { + Ax [cp] = Rx [p] ; + Az [cp] = Rz [p] ; + } + else + { + Ax [2*cp ] = Rx [2*p ] ; + Ax [2*cp+1] = Rx [2*p+1] ; + } +#else + Ax [cp] = Rx [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/src/maths/UMFPACK/umf_triplet.h b/src/maths/UMFPACK/umf_triplet.h new file mode 100644 index 000000000..62f972999 --- /dev/null +++ b/src/maths/UMFPACK/umf_triplet.h @@ -0,0 +1,85 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_tuple_lengths.c b/src/maths/UMFPACK/umf_tuple_lengths.c new file mode 100644 index 000000000..8dd349efb --- /dev/null +++ b/src/maths/UMFPACK/umf_tuple_lengths.c @@ -0,0 +1,136 @@ +/* ========================================================================== */ +/* === UMF_tuple_lengths ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_tuple_lengths.h" + +GLOBAL Int UMF_tuple_lengths /* return memory usage */ +( + NumericType *Numeric, + WorkType *Work, + double *p_dusage /* output argument */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + double dusage ; + 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 ; + 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/src/maths/UMFPACK/umf_tuple_lengths.h b/src/maths/UMFPACK/umf_tuple_lengths.h new file mode 100644 index 000000000..a31ccb94a --- /dev/null +++ b/src/maths/UMFPACK/umf_tuple_lengths.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_tuple_lengths +( + NumericType *Numeric, + WorkType *Work, + double *dusage +) ; diff --git a/src/maths/UMFPACK/umf_usolve.c b/src/maths/UMFPACK/umf_usolve.c new file mode 100644 index 000000000..f5a127dc5 --- /dev/null +++ b/src/maths/UMFPACK/umf_usolve.c @@ -0,0 +1,227 @@ +/* ========================================================================== */ +/* === UMF_usolve =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_usolve.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 */ + /* ---------------------------------------------------------------------- */ + + Entry xk ; + Entry *xp, *D, *Uval ; + Int k, deg, j, *ip, col, *Upos, *Uilen, pos, + *Uip, n, ulen, up, newUchain, npiv, n1, *Ui ; + + /* ---------------------------------------------------------------------- */ + /* 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/src/maths/UMFPACK/umf_usolve.h b/src/maths/UMFPACK/umf_usolve.h new file mode 100644 index 000000000..3b950d33c --- /dev/null +++ b/src/maths/UMFPACK/umf_usolve.h @@ -0,0 +1,12 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL double UMF_usolve +( + NumericType *Numeric, + Entry X [ ], + Int Pattern [ ] +) ; diff --git a/src/maths/UMFPACK/umf_utsolve.c b/src/maths/UMFPACK/umf_utsolve.c new file mode 100644 index 000000000..3d557b6c9 --- /dev/null +++ b/src/maths/UMFPACK/umf_utsolve.c @@ -0,0 +1,332 @@ +/* ========================================================================== */ +/* === UMF_utsolve ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_utsolve.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 */ + /* ---------------------------------------------------------------------- */ + + Entry xk ; + Entry *xp, *D, *Uval ; + Int k, deg, j, *ip, col, *Upos, *Uilen, kstart, kend, up, + *Uip, n, uhead, ulen, pos, npiv, n1, *Ui ; + + /* ---------------------------------------------------------------------- */ + /* 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/src/maths/UMFPACK/umf_utsolve.h b/src/maths/UMFPACK/umf_utsolve.h new file mode 100644 index 000000000..dae314318 --- /dev/null +++ b/src/maths/UMFPACK/umf_utsolve.h @@ -0,0 +1,20 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umf_valid_numeric.c b/src/maths/UMFPACK/umf_valid_numeric.c new file mode 100644 index 000000000..76855e34f --- /dev/null +++ b/src/maths/UMFPACK/umf_valid_numeric.c @@ -0,0 +1,47 @@ +/* ========================================================================== */ +/* === UMF_valid_numeric ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" +#include "umf_valid_numeric.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/src/maths/UMFPACK/umf_valid_numeric.h b/src/maths/UMFPACK/umf_valid_numeric.h new file mode 100644 index 000000000..0b258e4fe --- /dev/null +++ b/src/maths/UMFPACK/umf_valid_numeric.h @@ -0,0 +1,10 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_valid_numeric +( + NumericType *Numeric +) ; diff --git a/src/maths/UMFPACK/umf_valid_symbolic.c b/src/maths/UMFPACK/umf_valid_symbolic.c new file mode 100644 index 000000000..dd818519c --- /dev/null +++ b/src/maths/UMFPACK/umf_valid_symbolic.c @@ -0,0 +1,48 @@ +/* ========================================================================== */ +/* === UMF_valid_symbolic =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" +#include "umf_valid_symbolic.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/src/maths/UMFPACK/umf_valid_symbolic.h b/src/maths/UMFPACK/umf_valid_symbolic.h new file mode 100644 index 000000000..741a79944 --- /dev/null +++ b/src/maths/UMFPACK/umf_valid_symbolic.h @@ -0,0 +1,10 @@ +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +GLOBAL Int UMF_valid_symbolic +( + SymbolicType *Symbolic +) ; diff --git a/src/maths/UMFPACK/umf_version.h b/src/maths/UMFPACK/umf_version.h new file mode 100644 index 000000000..fd7fefa8b --- /dev/null +++ b/src/maths/UMFPACK/umf_version.h @@ -0,0 +1,878 @@ +/* ========================================================================== */ +/* === umf_version.h ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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, UF_long's as integers + ZLONG: complex double precision, UF_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 UF_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 (UMF_WINDOWS) && !defined (MATHWORKS) + +/* 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 SPLIT(s) (1) +#define REAL_COMPONENT(c) (c) +#define IMAG_COMPONENT(c) (0.) +#define ASSIGN(c,s1,s2,p,split) { (c) = (s1)[p] ; } +#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) ; } +#define SCALE(c,s) { (c) *= (s) ; } +#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 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) + +/* -------------------------------------------------------------------------- */ + +/* Return TRUE if a complex number is in split form, FALSE if in packed form */ +#define SPLIT(sz) ((sz) != (double *) NULL) + +/* -------------------------------------------------------------------------- */ + +/* c = (s1) + (s2)*i, if s2 is null, then X is in "packed" format (compatible + * with Entry and ANSI C99 double _Complex type). */ +#define ASSIGN(c,s1,s2,p,split) \ +{ \ + if (split) \ + { \ + (c).Real = (s1)[p] ; \ + (c).Imag = (s2)[p] ; \ + } \ + else \ + { \ + (c) = ((Entry *)(s1))[p] ; \ + } \ +} + +/* -------------------------------------------------------------------------- */ + +/* 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 */ +#define SCALE(c,s) \ +{ \ + (c).Real *= (s) ; \ + (c).Imag *= (s) ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* 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, using function pointer */ +#define DIV(c,a,b) \ +{ \ + (void) umfpack_divcomplex ((a).Real, (a).Imag, (b).Real, (b).Imag, \ + &((c).Real), &((c).Imag)) ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* c = a/conjugate(b), using function pointer */ +#define DIV_CONJ(c,a,b) \ +{ \ + (void) umfpack_divcomplex ((a).Real, (a).Imag, (b).Real, (-(b).Imag), \ + &((c).Real), &((c).Imag)) ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* 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 + a.imag^2) */ +#define ABS(s,a) \ +{ \ + (s) = umfpack_hypot ((a).Real, (a).Imag) ; \ +} + +/* -------------------------------------------------------------------------- */ + +/* 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_cholmod umf_i_cholmod + +#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_get_determinant umfpack_di_get_determinant +#define UMFPACK_numeric umfpack_di_numeric +#define UMFPACK_qsymbolic umfpack_di_qsymbolic +#define UMFPACK_fsymbolic umfpack_di_fsymbolic +#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 UF_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_cholmod umf_l_cholmod + +#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_get_determinant umfpack_dl_get_determinant +#define UMFPACK_numeric umfpack_dl_numeric +#define UMFPACK_qsymbolic umfpack_dl_qsymbolic +#define UMFPACK_fsymbolic umfpack_dl_fsymbolic +#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_cholmod umf_i_cholmod + +#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_get_determinant umfpack_zi_get_determinant +#define UMFPACK_numeric umfpack_zi_numeric +#define UMFPACK_qsymbolic umfpack_zi_qsymbolic +#define UMFPACK_fsymbolic umfpack_zi_fsymbolic +#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 UF_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_cholmod umf_l_cholmod + +#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_get_determinant umfpack_zl_get_determinant +#define UMFPACK_numeric umfpack_zl_numeric +#define UMFPACK_qsymbolic umfpack_zl_qsymbolic +#define UMFPACK_fsymbolic umfpack_zl_fsymbolic +#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/src/maths/UMFPACK/umfpack_col_to_triplet.c b/src/maths/UMFPACK/umfpack_col_to_triplet.c new file mode 100644 index 000000000..f49a13d79 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_col_to_triplet.c @@ -0,0 +1,72 @@ +/* ========================================================================== */ +/* === UMFPACK_col_to_triplet =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umfpack_defaults.c b/src/maths/UMFPACK/umfpack_defaults.c new file mode 100644 index 000000000..243821c8d --- /dev/null +++ b/src/maths/UMFPACK/umfpack_defaults.c @@ -0,0 +1,71 @@ +/* ========================================================================== */ +/* === UMFPACK_defaults ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 ; + + 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_AGGRESSIVE] = UMFPACK_DEFAULT_AGGRESSIVE ; + Control [UMFPACK_SINGLETONS] = UMFPACK_DEFAULT_SINGLETONS ; + Control [UMFPACK_ORDERING] = UMFPACK_DEFAULT_ORDERING ; + 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 NBLAS + /* 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 +} diff --git a/src/maths/UMFPACK/umfpack_free_numeric.c b/src/maths/UMFPACK/umfpack_free_numeric.c new file mode 100644 index 000000000..7e4e53182 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_free_numeric.c @@ -0,0 +1,57 @@ +/* ========================================================================== */ +/* === UMFPACK_free_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umfpack_free_symbolic.c b/src/maths/UMFPACK/umfpack_free_symbolic.c new file mode 100644 index 000000000..b865c4344 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_free_symbolic.c @@ -0,0 +1,56 @@ +/* ========================================================================== */ +/* === UMFPACK_free_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umfpack_get_determinant.c b/src/maths/UMFPACK/umfpack_get_determinant.c new file mode 100644 index 000000000..bfeacaada --- /dev/null +++ b/src/maths/UMFPACK/umfpack_get_determinant.c @@ -0,0 +1,308 @@ +/* ========================================================================== */ +/* === UMFPACK_get_determinant ============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* UMFPACK_get_determinant contributed by David Bateman, Motorola, Paris. */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. From the LU factors, scale factor, and permutation vectors + held in the Numeric object, calculates the determinant of the matrix A. + See umfpack_get_determinant.h for a more detailed description. + + Dynamic memory usage: calls UMF_malloc once, for a total space of + n integers, and then frees all of it via UMF_free when done. + + Contributed by David Bateman, Motorola, Nov. 2004. + Modified for V4.4, Jan. 2005. +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" +#include "umf_malloc.h" +#include "umf_free.h" + +/* ========================================================================== */ +/* === rescale_determinant ================================================== */ +/* ========================================================================== */ + +/* If the mantissa is too big or too small, rescale it and change exponent */ + +PRIVATE Int rescale_determinant +( + Entry *d_mantissa, + double *d_exponent +) +{ + double d_abs ; + + ABS (d_abs, *d_mantissa) ; + + if (SCALAR_IS_ZERO (d_abs)) + { + /* the determinant is zero */ + *d_exponent = 0 ; + return (FALSE) ; + } + + if (SCALAR_IS_NAN (d_abs)) + { + /* the determinant is NaN */ + return (FALSE) ; + } + + while (d_abs < 1.) + { + SCALE (*d_mantissa, 10.0) ; + *d_exponent = *d_exponent - 1.0 ; + ABS (d_abs, *d_mantissa) ; + } + + while (d_abs >= 10.) + { + SCALE (*d_mantissa, 0.1) ; + *d_exponent = *d_exponent + 1.0 ; + ABS (d_abs, *d_mantissa) ; + } + + return (TRUE) ; +} + +/* ========================================================================== */ +/* === UMFPACK_get_determinant ============================================== */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_get_determinant +( + double *Mx, +#ifdef COMPLEX + double *Mz, +#endif + double *Ex, + void *NumericHandle, + double User_Info [UMFPACK_INFO] +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Entry d_mantissa, d_tmp ; + double d_exponent, Info2 [UMFPACK_INFO], one [2] = {1.0, 0.0}, d_sign ; + Entry *D ; + double *Info, *Rs ; + NumericType *Numeric ; + Int i, n, itmp, npiv, *Wi, *Rperm, *Cperm, do_scale ; + +#ifndef NRECIPROCAL + Int do_recip ; +#endif + + /* ---------------------------------------------------------------------- */ + /* check input parameters */ + /* ---------------------------------------------------------------------- */ + + 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 ; + for (i = 0 ; i < UMFPACK_INFO ; i++) + { + Info [i] = EMPTY ; + } + } + + Info [UMFPACK_STATUS] = UMFPACK_OK ; + + Numeric = (NumericType *) NumericHandle ; + if (!UMF_valid_numeric (Numeric)) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_Numeric_object ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + 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) ; + } + + if (Mx == (double *) NULL) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + + n = Numeric->n_row ; + + /* ---------------------------------------------------------------------- */ + /* allocate workspace */ + /* ---------------------------------------------------------------------- */ + + Wi = (Int *) UMF_malloc (n, sizeof (Int)) ; + + if (!Wi) + { + DEBUGm4 (("out of memory: get determinant\n")) ; + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + /* ---------------------------------------------------------------------- */ + /* compute the determinant */ + /* ---------------------------------------------------------------------- */ + + Rs = Numeric->Rs ; /* row scale factors */ + do_scale = (Rs != (double *) NULL) ; + +#ifndef NRECIPROCAL + do_recip = Numeric->do_recip ; +#endif + + d_mantissa = ((Entry *) one) [0] ; + d_exponent = 0.0 ; + D = Numeric->D ; + + /* compute product of diagonal entries of U */ + for (i = 0 ; i < n ; i++) + { + MULT (d_tmp, d_mantissa, D [i]) ; + d_mantissa = d_tmp ; + + if (!rescale_determinant (&d_mantissa, &d_exponent)) + { + /* the determinant is zero or NaN */ + Info [UMFPACK_STATUS] = UMFPACK_WARNING_singular_matrix ; + /* no need to compute the determinant of R */ + do_scale = FALSE ; + break ; + } + } + + /* compute product of diagonal entries of R (or its inverse) */ + if (do_scale) + { + for (i = 0 ; i < n ; i++) + { +#ifndef NRECIPROCAL + if (do_recip) + { + /* compute determinant of R inverse */ + SCALE_DIV (d_mantissa, Rs [i]) ; + } + else +#endif + { + /* compute determinant of R */ + SCALE (d_mantissa, Rs [i]) ; + } + if (!rescale_determinant (&d_mantissa, &d_exponent)) + { + /* the determinant is zero or NaN. This is very unlikey to + * occur here, since the scale factors for a tiny or zero row + * are set to 1. */ + Info [UMFPACK_STATUS] = UMFPACK_WARNING_singular_matrix ; + break ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* determine if P and Q are odd or even permutations */ + /* ---------------------------------------------------------------------- */ + + npiv = 0 ; + Rperm = Numeric->Rperm ; + + for (i = 0 ; i < n ; i++) + { + Wi [i] = Rperm [i] ; + } + + for (i = 0 ; i < n ; i++) + { + while (Wi [i] != i) + { + itmp = Wi [Wi [i]] ; + Wi [Wi [i]] = Wi [i] ; + Wi [i] = itmp ; + npiv++ ; + } + } + + Cperm = Numeric->Cperm ; + + for (i = 0 ; i < n ; i++) + { + Wi [i] = Cperm [i] ; + } + + for (i = 0 ; i < n ; i++) + { + while (Wi [i] != i) + { + itmp = Wi [Wi [i]] ; + Wi [Wi [i]] = Wi [i] ; + Wi [i] = itmp ; + npiv++ ; + } + } + + /* if npiv is odd, the sign is -1. if it is even, the sign is +1 */ + d_sign = (npiv % 2) ? -1. : 1. ; + + /* ---------------------------------------------------------------------- */ + /* free workspace */ + /* ---------------------------------------------------------------------- */ + + (void) UMF_free ((void *) Wi) ; + + /* ---------------------------------------------------------------------- */ + /* compute the magnitude and exponent of the determinant */ + /* ---------------------------------------------------------------------- */ + + if (Ex == (double *) NULL) + { + /* Ex is not provided, so return the entire determinant in d_mantissa */ + SCALE (d_mantissa, pow (10.0, d_exponent)) ; + } + else + { + Ex [0] = d_exponent ; + } + + Mx [0] = d_sign * REAL_COMPONENT (d_mantissa) ; + +#ifdef COMPLEX + if (SPLIT (Mz)) + { + Mz [0] = d_sign * IMAG_COMPONENT (d_mantissa) ; + } + else + { + Mx [1] = d_sign * IMAG_COMPONENT (d_mantissa) ; + } +#endif + + /* determine if the determinant has (or will) overflow or underflow */ + if (d_exponent + 1.0 > log10 (DBL_MAX)) + { + Info [UMFPACK_STATUS] = UMFPACK_WARNING_determinant_overflow ; + } + else if (d_exponent - 1.0 < log10 (DBL_MIN)) + { + Info [UMFPACK_STATUS] = UMFPACK_WARNING_determinant_underflow ; + } + + return (UMFPACK_OK) ; +} diff --git a/src/maths/UMFPACK/umfpack_get_lunz.c b/src/maths/UMFPACK/umfpack_get_lunz.c new file mode 100644 index 000000000..21752d3af --- /dev/null +++ b/src/maths/UMFPACK/umfpack_get_lunz.c @@ -0,0 +1,55 @@ +/* ========================================================================== */ +/* === UMFPACK_get_lunz ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umfpack_get_numeric.c b/src/maths/UMFPACK/umfpack_get_numeric.c new file mode 100644 index 000000000..9dd22080c --- /dev/null +++ b/src/maths/UMFPACK/umfpack_get_numeric.c @@ -0,0 +1,1057 @@ +/* ========================================================================== */ +/* === UMFPACK_get_numeric ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 */ + /* ---------------------------------------------------------------------- */ + + getL = Lp && Lj && Lx ; + getU = Up && Ui && Ux ; + + 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 ; +#ifdef COMPLEX + if (SPLIT (Dz)) + { + for (k = 0 ; k < n_inner ; k++) + { + Dx [k] = REAL_COMPONENT (D [k]) ; + Dz [k] = IMAG_COMPONENT (D [k]) ; + } + } + else + { + for (k = 0 ; k < n_inner ; k++) + { + Dx [2*k ] = REAL_COMPONENT (D [k]) ; + Dx [2*k+1] = IMAG_COMPONENT (D [k]) ; + } + } +#else + { + D = Numeric->D ; + for (k = 0 ; k < n_inner ; k++) + { + Dx [k] = 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 */ + /* ---------------------------------------------------------------------- */ + + Entry value ; + Entry *xp, *Lval ; + Int deg, *ip, j, row, n_row, n_col, n_inner, *Lpos, *Lilen, *Lip, p, llen, + lnz2, lp, newLchain, k, pos, npiv, *Li, n1 ; +#ifdef COMPLEX + Int split = SPLIT (Lz) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* 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 ; +#ifdef COMPLEX + if (split) + { + + Lx [p] = REAL_COMPONENT (value) ; + Lz [p] = IMAG_COMPONENT (value) ; + } + else + { + Lx [2*p ] = REAL_COMPONENT (value) ; + Lx [2*p+1] = IMAG_COMPONENT (value) ; + } +#else + Lx [p] = 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 ; +#ifdef COMPLEX + if (split) + { + Lx [p] = REAL_COMPONENT (value) ; + Lz [p] = IMAG_COMPONENT (value) ; + } + else + { + Lx [2*p ] = REAL_COMPONENT (value) ; + Lx [2*p+1] = IMAG_COMPONENT (value) ; + } +#else + Lx [p] = 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 ; + +#ifdef COMPLEX + if (split) + { + Lx [p] = 1. ; + Lz [p] = 0. ; + } + else + { + Lx [2*p ] = 1. ; + Lx [2*p+1] = 0. ; + } +#else + Lx [p] = 1. ; +#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 */ + /* ---------------------------------------------------------------------- */ + + Entry value ; + Entry *xp, *D, *Uval ; + Int deg, j, *ip, col, *Upos, *Uilen, *Uip, n_col, ulen, *Usi, + unz2, p, k, up, newUchain, pos, npiv, n1 ; +#ifdef COMPLEX + Int split = SPLIT (Uz) ; +#endif +#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 ; +#ifdef COMPLEX + if (split) + { + + Ux [p] = REAL_COMPONENT (D [col]) ; + Uz [p] = IMAG_COMPONENT (D [col]) ; + } + else + { + Ux [2*p ] = REAL_COMPONENT (D [col]) ; + Ux [2*p+1] = IMAG_COMPONENT (D [col]) ; + } +#else + Ux [p] = 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 ; +#ifdef COMPLEX + if (split) + { + Ux [p] = REAL_COMPONENT (value) ; + Uz [p] = IMAG_COMPONENT (value) ; + } + else + { + Ux [2*p ] = REAL_COMPONENT (value) ; + Ux [2*p+1] = IMAG_COMPONENT (value) ; + } +#else + Ux [p] = 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 ; +#ifdef COMPLEX + if (split) + { + Ux [p] = REAL_COMPONENT (value) ; + Uz [p] = IMAG_COMPONENT (value) ; + } + else + { + Ux [2*p ] = REAL_COMPONENT (value) ; + Ux [2*p+1] = IMAG_COMPONENT (value) ; + } +#else + Ux [p] = 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/src/maths/UMFPACK/umfpack_get_symbolic.c b/src/maths/UMFPACK/umfpack_get_symbolic.c new file mode 100644 index 000000000..560d38c52 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_get_symbolic.c @@ -0,0 +1,191 @@ +/* ========================================================================== */ +/* === UMFPACK_get_symbolic ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umfpack_global.c b/src/maths/UMFPACK/umfpack_global.c new file mode 100644 index 000000000..b227573ba --- /dev/null +++ b/src/maths/UMFPACK/umfpack_global.c @@ -0,0 +1,130 @@ +/* ========================================================================== */ +/* === UMFPACK_global ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Global variables. UMFPACK uses these function pointers for several + user-redefinable functions. The amd_* functions are defined in + AMD/Source/amd_global.c. + + Function pointer default for mexFunction + (see MATLAB/umfpackmex.c) + ---------------- ------- --------------- + amd_malloc malloc mxMalloc + amd_free free mxFree + amd_realloc realloc mxRealloc + amd_calloc calloc mxCalloc + amd_printf printf mexPrintf + + umfpack_hypot umf_hypot umf_hypot + umfpack_divcomplex umf_divcomplex umf_divcomplex + + This routine is compiled just once for all four versions of UMFPACK + (int/UF_long, double/complex). +*/ + +#include "umf_internal.h" + +double (*umfpack_hypot) (double, double) = umf_hypot ; +int (*umfpack_divcomplex) (double, double, double, double, double *, double *) + = umf_divcomplex ; + + +/* ========================================================================== */ +/* === umf_hypot ============================================================ */ +/* ========================================================================== */ + +/* There is an equivalent routine called hypot in , which conforms + * to ANSI C99. However, UMFPACK does not assume that ANSI C99 is available. + * You can use the ANSI C99 hypot routine with: + * + * #include + * umfpack_hypot = hypot ; + * + * prior to calling any UMFPACK routine. + * + * s = hypot (x,y) computes s = sqrt (x*x + y*y) but does so more accurately. + * + * The NaN case for the double relops x >= y and x+y == x is safely ignored. + */ + +double umf_hypot (double x, double y) +{ + double s, r ; + x = SCALAR_ABS (x) ; + y = SCALAR_ABS (y) ; + if (x >= y) + { + if (x + y == x) + { + s = x ; + } + else + { + r = y / x ; + s = x * sqrt (1.0 + r*r) ; + } + } + else + { + if (y + x == y) + { + s = y ; + } + else + { + r = x / y ; + s = y * sqrt (1.0 + r*r) ; + } + } + return (s) ; +} + + +/* ========================================================================== */ +/* === umf_divcomplex ======================================================= */ +/* ========================================================================== */ + +/* c = a/b where c, a, and b are complex. The real and imaginary parts are + * passed as separate arguments to this routine. The NaN case is ignored + * for the double relop br >= bi. Returns TRUE (1) if the denominator is + * zero, FALSE (0) otherwise. + * + * This uses ACM Algo 116, by R. L. Smith, 1962, which tries to avoid + * underflow and overflow. + * + * c can be the same variable as a or b. + */ + +int umf_divcomplex +( + double ar, double ai, /* real and imaginary parts of a */ + double br, double bi, /* real and imaginary parts of b */ + double *cr, double *ci /* real and imaginary parts of c */ +) +{ + double tr, ti, r, den ; + if (SCALAR_ABS (br) >= SCALAR_ABS (bi)) + { + r = bi / br ; + den = br + r * bi ; + tr = (ar + ai * r) / den ; + ti = (ai - ar * r) / den ; + } + else + { + r = br / bi ; + den = r * br + bi ; + tr = (ar * r + ai) / den ; + ti = (ai * r - ar) / den ; + } + *cr = tr ; + *ci = ti ; + return (SCALAR_IS_ZERO (den)) ; +} diff --git a/src/maths/UMFPACK/umfpack_load_numeric.c b/src/maths/UMFPACK/umfpack_load_numeric.c new file mode 100644 index 000000000..a23490214 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_load_numeric.c @@ -0,0 +1,161 @@ +/* ========================================================================== */ +/* === UMFPACK_load_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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) != (size_t) 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/src/maths/UMFPACK/umfpack_load_symbolic.c b/src/maths/UMFPACK/umfpack_load_symbolic.c new file mode 100644 index 000000000..10db36124 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_load_symbolic.c @@ -0,0 +1,165 @@ +/* ========================================================================== */ +/* === UMFPACK_load_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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) != (size_t) 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/src/maths/UMFPACK/umfpack_numeric.c b/src/maths/UMFPACK/umfpack_numeric.c new file mode 100644 index 000000000..7cbc74238 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_numeric.c @@ -0,0 +1,793 @@ +/* ========================================================================== */ +/* === UMFPACK_numeric ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 */ + /* ---------------------------------------------------------------------- */ + + double Info2 [UMFPACK_INFO], alloc_init, relpt, relpt2, droptol, + front_alloc_init, stats [2] ; + double *Info ; + WorkType WorkSpace, *Work ; + NumericType *Numeric ; + SymbolicType *Symbolic ; + Int n_row, n_col, n_inner, newsize, i, status, *inew, npiv, ulen, scale ; + Unit *mnew ; + + /* ---------------------------------------------------------------------- */ + /* 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 or other symmetric ordering */ + if (/* Symbolic->ordering == UMFPACK_ORDERING_AMD */ alloc_init >= 0 + && Symbolic->amd_lunz > 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) + { + 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 +) +{ + double nsize, bsize ; + Int n_row, n_col, n_inner, min_usage, trying ; + NumericType *Numeric ; + + 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/src/maths/UMFPACK/umfpack_qsymbolic.c b/src/maths/UMFPACK/umfpack_qsymbolic.c new file mode 100644 index 000000000..a30044350 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_qsymbolic.c @@ -0,0 +1,2752 @@ +/* ========================================================================== */ +/* === UMFPACK_qsymbolic ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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_singletons.h" +#include "umf_cholmod.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 */ + +} 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 + +/* ========================================================================== */ +/* === inverse_permutation ================================================== */ +/* ========================================================================== */ + +/* Check a permutation, and return its inverse */ + +PRIVATE int inverse_permutation +( + Int *P, /* input, size n, P[k]=i means i is kth object in permutation */ + Int *Pinv, /* output, size n, Pinv[i]=k if P[k]=i */ + Int n /* input */ +) +{ + Int i, k ; + for (i = 0 ; i < n ; i++) + { + Pinv [i] = EMPTY ; + } + for (k = 0 ; k < n ; k++) + { + i = P [k] ; + if (i < 0 || i >= n || Pinv [i] != EMPTY) + { + /* invalid permutation */ + return (FALSE) ; + } + Pinv [i] = k ; + } + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === do_amd_1 ============================================================= */ +/* ========================================================================== */ + +/* do_amd_1: Construct A+A' for a sparse matrix A and perform the AMD ordering + * or user_ordering. Modified from AMD/Source/amd_1.c + * + * The n-by-n sparse matrix A can be unsymmetric. It is stored in MATLAB-style + * compressed-column form, with sorted row indices in each column, and no + * duplicate entries. Diagonal entries may be present, but they are ignored. + * Row indices of column j of A are stored in Ai [Ap [j] ... Ap [j+1]-1]. + * Ap [0] must be zero, and nz = Ap [n] is the number of entries in A. The + * size of the matrix, n, must be greater than or equal to zero. + * + * This routine must be preceded by a call to AMD_aat, which computes the + * number of entries in each row/column in A+A', excluding the diagonal. + * Len [j], on input, is the number of entries in row/column j of A+A'. This + * routine constructs the matrix A+A' and then calls AMD_2 or the user_ordering. + * No error checking is performed (this was done in AMD_valid). + */ + +PRIVATE int do_amd_1 +( + Int n, /* n > 0 */ + Int Ap [ ], /* input of size n+1, not modified */ + 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+1, + * ideally slen = 1.2 * sum (Len) + 8n */ + Int S [ ], /* size slen workspace */ + Int ordering_option, + Int print_level, + + /* user-provided ordering function */ + int (*user_ordering) /* TRUE if OK, FALSE otherwise */ + ( + /* inputs, not modified on output */ + Int, /* nrow */ + Int, /* ncol */ + Int, /* sym: if TRUE and nrow==ncol do A+A', else do A'A */ + Int *, /* Ap, size ncol+1 */ + Int *, /* Ai, size nz */ + /* output */ + Int *, /* size ncol, fill-reducing permutation */ + /* input/output */ + void *, /* user_params (ignored by UMFPACK) */ + double * /* user_info[0..2], optional output for symmetric case. + user_info[0]: max column count for L=chol(P(A+A')P') + user_info[1]: nnz (L) + user_info[2]: flop count for chol, if A real */ + ), + void *user_params, /* passed to user_ordering function */ + + Int *ordering_used, + + double amd_Control [ ], /* input array of size AMD_CONTROL */ + double amd_Info [ ] /* output array of size AMD_INFO */ +) +{ + Int i, j, k, p, pfree, iwlen, pj, p1, p2, pj2, anz, *Iw, *Pe, *Nv, *Head, + *Elen, *Degree, *s, *W, *Sp, *Tp ; + + /* --------------------------------------------------------------------- */ + /* construct the matrix for AMD_2 or user_ordering */ + /* --------------------------------------------------------------------- */ + + ASSERT (n > 0) ; +#ifndef NDEBUG + for (p = 0 ; p < slen ; p++) S [p] = EMPTY ; +#endif + + s = S ; + Pe = s ; s += (n+1) ; slen -= (n+1) ; + Nv = s ; s += n ; slen -= n ; + + if (user_ordering == NULL) + { + /* iwlen = slen - (3*n+1) ; */ + Head = s ; s += n ; slen -= n ; + Elen = s ; s += n ; slen -= n ; + Degree = s ; s += n ; slen -= n ; + } + else + { + /* iwlen = slen - (6*n+1) ; */ + Head = NULL ; + Elen = NULL ; + Degree = NULL ; + } + + W = s ; s += n ; slen -= n ; + + iwlen = slen ; + Iw = s ; s += iwlen ; + + ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ; + anz = Ap [n] ; + + /* 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] ; + } + Pe [n] = pfree ; + +#ifndef NDEBUG + if (user_ordering == NULL) + { + /* 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) ; + } + else + { + ASSERT (iwlen >= pfree) ; + } + 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 ; j++) ASSERT (Sp [j] == Pe [j+1]) ; +#endif + + /* Tp and Sp no longer needed ] */ + + /* --------------------------------------------------------------------- */ + /* order the matrix */ + /* --------------------------------------------------------------------- */ + + if (ordering_option == UMFPACK_ORDERING_AMD) + { + + /* use AMD as the symmetric ordering */ + AMD_2 (n, Pe, Iw, Len, iwlen, pfree, + Nv, Pinv, P, Head, Elen, Degree, W, amd_Control, amd_Info) ; + *ordering_used = UMFPACK_ORDERING_AMD ; + return (TRUE) ; + + } + else + { + + /* use the user-provided symmetric ordering, or umf_cholmod */ + double user_info [3], dmax, lnz, flops ; + int ok ; + user_info [0] = EMPTY ; + user_info [1] = EMPTY ; + user_info [2] = EMPTY ; + + if (ordering_option == UMFPACK_ORDERING_USER) + { + ok = (*user_ordering) (n, n, TRUE, Pe, Iw, P, + user_params, user_info) ; + *ordering_used = UMFPACK_ORDERING_USER ; + } + else + /* if (ordering_option == UMFPACK_ORDERING_CHOLMOD + || ordering_option == UMFPACK_ORDERING_GIVEN + || ordering_option == UMFPACK_ORDERING_NONE + || ordering_option == UMFPACK_ORDERING_METIS + || ordering_option == UMFPACK_ORDERING_BEST) */ + { + Int params [3] ; + params [0] = ordering_option ; + params [1] = print_level ; + ok = UMF_cholmod (n, n, TRUE, Pe, Iw, P, ¶ms, user_info) ; + *ordering_used = params [2] ; + } + + if (!ok) + { + /* user_ordering or UMF_cholmod failed */ + amd_Info [AMD_STATUS] = AMD_INVALID ; + return (FALSE) ; + } + + /* get the user ordering statistics, if computed */ + dmax = user_info [0] ; + lnz = user_info [1] ; + flops = user_info [2] ; + + /* construct amd_Info, as if AMD was called */ + amd_Info [AMD_STATUS] = AMD_OK ; + amd_Info [AMD_N] = n ; + amd_Info [AMD_NZ] = anz ; + /* amd_Info [AMD_SYMMETRY] not computed ; */ + /* amd_Info [AMD_NZDIAG] not computed ; */ + amd_Info [AMD_NZ_A_PLUS_AT] = pfree ; + amd_Info [AMD_NDENSE] = 0 ; + /* amd_Info [AMD_MEMORY] not computed ; */ + amd_Info [AMD_NCMPA] = 0 ; + amd_Info [AMD_LNZ] = lnz ; + amd_Info [AMD_NDIV] = lnz ; + if (flops >= 0) + { + amd_Info [AMD_NMULTSUBS_LDL] = (flops - n) / 2 ; + amd_Info [AMD_NMULTSUBS_LU] = (flops - n) ; + } + else + { + amd_Info [AMD_NMULTSUBS_LDL] = EMPTY ; + amd_Info [AMD_NMULTSUBS_LU] = EMPTY ; + } + amd_Info [AMD_DMAX] = dmax ; + + /* construct the inverse permutation */ + return (inverse_permutation (P, Pinv, n)) ; + } +} + + +/* ========================================================================== */ +/* === do_amd =============================================================== */ +/* ========================================================================== */ + +PRIVATE int do_amd +( + Int n, + Int Ap [ ], /* size n+1 */ + 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 Clen workspace */ + double amd_Control [ ], /* AMD control parameters */ + double amd_Info [ ], /* AMD info */ + SymbolicType *Symbolic, /* Symbolic object */ + double Info [ ], /* UMFPACK info */ + Int ordering_option, + Int print_level, + + /* user-provided ordering function */ + int (*user_ordering) /* TRUE if OK, FALSE otherwise */ + ( + /* inputs, not modified on output */ + Int, /* nrow */ + Int, /* ncol */ + Int, /* sym: if TRUE and nrow==ncol do A+A', else do A'A */ + Int *, /* Ap, size ncol+1 */ + Int *, /* Ai, size nz */ + /* output */ + Int *, /* size ncol, fill-reducing permutation */ + /* input/output */ + void *, /* user_params (ignored by UMFPACK) */ + double * /* user_info[0..2], optional output for symmetric case. + user_info[0]: max column count for L=chol(P(A+A')P') + user_info[1]: nnz (L) + user_info[2]: flop count for chol, if A real */ + ), + void *user_params, /* passed to user_ordering function */ + Int *ordering_used +) +{ + int ok = TRUE ; + *ordering_used = UMFPACK_ORDERING_NONE ; + + 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 + { + ok = do_amd_1 (n, Ap, Ai, Q, Qinv, Sdeg, Clen, + Ci, ordering_option, print_level, user_ordering, user_params, + ordering_used, amd_Control, amd_Info) ; + + /* return estimates computed from AMD or user ordering P(A+A')P' */ + if (ok) + { + 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] ; + } + } + return (ok) ; +} + +/* ========================================================================== */ +/* === 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 ; +#ifdef COMPLEX + Int split = SPLIT (Az) ; +#endif + + nzdiag = 0 ; + do_nzdiag = (Ax != (double *) NULL) ; + +#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 */ +#ifdef COMPLEX + if (split) + { + if (SCALAR_IS_NONZERO (Ax [p]) || + SCALAR_IS_NONZERO (Az [p])) + { + nzdiag++ ; + } + } + else + { + if (SCALAR_IS_NONZERO (Ax [2*p ]) || + SCALAR_IS_NONZERO (Ax [2*p+1])) + { + nzdiag++ ; + } + } +#else + if (SCALAR_IS_NONZERO (Ax [p])) + { + nzdiag++ ; + } +#endif + } + } + } + DEBUG5 (("\n")) ; + } + } + Sp [n_col - n1] = pp ; + + 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 + +} + +/* ========================================================================== */ +/* === symbolic_analysis ==================================================== */ +/* ========================================================================== */ + +PRIVATE Int symbolic_analysis +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + + /* user-provided ordering (may be NULL) */ + const Int Quser [ ], + + /* user-provided ordering function */ + int (*user_ordering) /* TRUE if OK, FALSE otherwise */ + ( + /* inputs, not modified on output */ + Int, /* nrow */ + Int, /* ncol */ + Int, /* sym: if TRUE and nrow==ncol do A+A', else do A'A */ + Int *, /* Ap, size ncol+1 */ + Int *, /* Ai, size nz */ + /* output */ + Int *, /* size ncol, fill-reducing permutation */ + /* input/output */ + void *, /* user_params (ignored by UMFPACK) */ + double * /* user_info[0..2], optional output for symmetric case. + user_info[0]: max column count for L=chol(P(A+A')P') + user_info[1]: nnz (L) + user_info[2]: flop count for chol, if A real */ + ), + void *user_params, /* passed to user_ordering function */ + + void **SymbolicHandle, + const double Control [UMFPACK_CONTROL], + double User_Info [UMFPACK_INFO] +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + double knobs [COLAMD_KNOBS], flops, f, r, c, 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] ; + double *Info ; + 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, oldrow, + dense_row_threshold, tlen, aggressive, *Rp, *Ri ; + Int do_singletons, ordering_option, print_level ; + int ok ; + + 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) ; + force_fixQ = GET_CONTROL (UMFPACK_FIXQ, UMFPACK_DEFAULT_FIXQ) ; + do_singletons = GET_CONTROL (UMFPACK_SINGLETONS,UMFPACK_DEFAULT_SINGLETONS); + 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 ; + print_level = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + /* get the ordering_option */ + ordering_option = GET_CONTROL (UMFPACK_ORDERING, UMFPACK_DEFAULT_ORDERING) ; + if (ordering_option < 0 || ordering_option > UMFPACK_ORDERING_USER) + { + ordering_option = UMFPACK_DEFAULT_ORDERING ; + } + if (Quser == (Int *) NULL) + { + /* Quser is NULL, so ordering cannot be "given" */ + /* user_ordering function not provided, so ordering cannot be "user" */ + if (ordering_option == UMFPACK_ORDERING_GIVEN || + (ordering_option == UMFPACK_ORDERING_USER && !user_ordering)) + { + ordering_option = UMFPACK_ORDERING_NONE ; + } + } + else + { + /* if Quser is not NULL, then always use it */ + ordering_option = UMFPACK_ORDERING_GIVEN ; + } + + 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)) ; + + 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 (UF_long)) ; + Info [UMFPACK_SIZE_OF_POINTER] = (double) (sizeof (void *)) ; + Info [UMFPACK_SIZE_OF_ENTRY] = (double) (sizeof (Entry)) ; + Info [UMFPACK_SYMBOLIC_DEFRAG] = 0 ; + Info [UMFPACK_ORDERING_USED] = EMPTY ; + + if (SymbolicHandle != NULL) + { + *SymbolicHandle = (void *) NULL ; + } + + if (!Ai || !Ap || !SymbolicHandle) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + + 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 + || strategy == UMFPACK_STRATEGY_OBSOLETE) + { + /* unrecognized strategy */ + strategy = UMFPACK_STRATEGY_AUTO ; + } + + if (Quser != (Int *) NULL) + { + /* when the user provides Q, only symmetric and unsymmetric strategies + * are available */ + if (strategy != UMFPACK_STRATEGY_SYMMETRIC) + { + strategy = UMFPACK_STRATEGY_UNSYMMETRIC ; + } + } + + 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 + 1 ; + + 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 UF_long 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 + 1 ; + 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->ordering = EMPTY ; /* not yet determined */ + Symbolic->amd_lunz = EMPTY ; + Symbolic->max_nchains = EMPTY ; + + 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 ; + Cdeg [n_col] = EMPTY ; /* unused space */ + Rdeg [n_row] = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* 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 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 ; + 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, strategy, + do_singletons, /* if false, then do not look for singletons */ + 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) == AMD_OK) ; + + 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]. + * Switch to the unsymmetric strategy, ignoring user-requested + * strategy. */ + 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), 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 + ) ; + + /* use Ci as workspace to sort S into R, if needed [ */ + if (Quser != (Int *) NULL) + { + /* need to sort the columns of S first */ + Rp = Ci ; + Ri = Ci + (n_row) + 1 ; + (void) UMF_transpose (n2, n2, Sp, Si, (double *) NULL, + (Int *) NULL, (Int *) NULL, 0, + Rp, Ri, (double *) NULL, Wq, FALSE +#ifdef COMPLEX + , (double *) NULL, (double *) NULL, FALSE +#endif + ) ; + } + else + { + /* S already has sorted columns */ + Rp = Sp ; + Ri = Si ; + } + ASSERT (AMD_valid (n2, n2, Rp, Ri) == AMD_OK) ; + + nzaat = AMD_aat (n2, Rp, Ri, 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 */ + + /* done using Ci as workspace to sort S into R ] */ + +#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.5 && nzdiag >= 0.9 * n2) + { + /* pattern is mostly symmetric (50% or more) and the diagonal is + * mostly zero-free (90% or more). Use symmetric strategy. */ + strategy = UMFPACK_STRATEGY_SYMMETRIC ; + DEBUG0 (("Strategy: select symmetric\n")) ; + } + else + { + /* otherwise use unsymmetric strategy */ + strategy = UMFPACK_STRATEGY_UNSYMMETRIC ; + DEBUG0 (("Strategy: select unsymmetric\n")) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* finalize the strategy, including fixQ and prefer_diagonal */ + /* ---------------------------------------------------------------------- */ + + DEBUG0 (("strategy is now "ID"\n", strategy)) ; + + 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) ; + 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")) ; + fixQ = FALSE ; + prefer_diagonal = FALSE ; + } + + 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", ordering_option)) ; + 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_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 ordering_used ; + Int *Qinv = Fr_npivcol ; + ASSERT (n_row == n_col && nn == n_row) ; + ASSERT (Clen >= (nzaat + nzaat/5 + nn) + 7*nn + 1) ; + ok = do_amd (n2, Sp, Si, Wq, Qinv, Sdeg, Clen, Ci, + amd_Control, amd_Info, Symbolic, Info, + ordering_option, print_level, user_ordering, user_params, + &ordering_used) ; + if (!ok) + { + DEBUGm4 (("symmetric ordering failed\n")) ; + status = UMFPACK_ERROR_ordering_failed ; + Info [UMFPACK_STATUS] = status ; + error (&Symbolic, SW) ; + return (status) ; + } + /* combine the singleton ordering and the AMD ordering */ + Symbolic->ordering = ordering_used ; + 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] ; + } + Symbolic->ordering = UMFPACK_ORDERING_GIVEN ; + } + + /* ---------------------------------------------------------------------- */ + /* use COLAMD or user_ordering to order the matrix */ + /* ---------------------------------------------------------------------- */ + + if (strategy == UMFPACK_STRATEGY_UNSYMMETRIC && Quser == (Int *) NULL) + { + Int nrow2, ncol2 ; + + /* ------------------------------------------------------------------ */ + /* 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 + ) ; + + /* size of pruned matrix */ + nrow2 = n_row - n1 - nempty_row ; + ncol2 = n_col - n1 - nempty_col ; + + if ((ordering_option == UMFPACK_ORDERING_USER + || ordering_option == UMFPACK_ORDERING_NONE + || ordering_option == UMFPACK_ORDERING_METIS + || ordering_option == UMFPACK_ORDERING_CHOLMOD + || ordering_option == UMFPACK_ORDERING_BEST) + && nrow2 > 0 && ncol2 > 0) + { + + /* -------------------------------------------------------------- */ + /* use the user-provided column ordering */ + /* -------------------------------------------------------------- */ + + double user_info [3] ; /* not needed */ + Int *Qinv = Fr_npivcol ; /* use Fr_npivcol as workspace for Qinv */ + Int *QQ = Fr_nrows ; /* use Fr_nrows as workspace for QQ */ + + /* analyze the resulting ordering for UMFPACK */ + do_UMF_analyze = TRUE ; + + if (ordering_option == UMFPACK_ORDERING_USER) + { + ok = (*user_ordering) ( + /* inputs */ + nrow2, + ncol2, + FALSE, + Cperm_init, /* column pointers, Cp [0 ... ncol] */ + Ci, /* row indices */ + /* outputs, contents not defined on input */ + QQ, /* size ncol, QQ [k] = j if col j is kth col of A*Q */ + /* parameters and info for user ordering */ + user_params, + user_info) ; + Symbolic->ordering = UMFPACK_ORDERING_USER ; + } + else + { + Int params [3] ; + params [0] = ordering_option ; + params [1] = print_level ; + ok = UMF_cholmod ( + /* inputs */ + nrow2, + ncol2, + FALSE, + Cperm_init, /* column pointers, Cp [0 ... ncol] */ + Ci, /* row indices */ + /* outputs, contents not defined on input */ + QQ, /* size ncol, QQ [k] = j if col j is kth col of A*Q */ + /* parameters and info for user ordering */ + ¶ms, + user_info) ; + Symbolic->ordering = params [2] ; + } + + /* compute Qinv from QQ */ + if (!ok || !inverse_permutation (QQ, Qinv, ncol2)) + { + /* user ordering failed */ + DEBUGm4 (("user ordering failed\n")) ; + status = UMFPACK_ERROR_ordering_failed ; + Info [UMFPACK_STATUS] = status ; + error (&Symbolic, SW) ; + return (status) ; + } + + /* Combine the singleton and colamd ordering into Cperm_init */ + /* Note that the user_unsymmetric_ordering function returns its + * inverse permutation in Qinv */ + combine_ordering (n1, nempty_col, n_col, Cperm_init, Cperm1, Qinv) ; + + } + else + { + + /* -------------------------------------------------------------- */ + /* 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) ; + Symbolic->ordering = UMFPACK_ORDERING_AMD ; + + /* # 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 ; + + } + + /* ordering has been finalized */ + Info [UMFPACK_ORDERING_USED] = Symbolic->ordering ; + DEBUG0 (("Final ordering used: "ID"\n", Symbolic->ordering)) ; + + 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, *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 */ + /* ---------------------------------------------------------------------- */ + + 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 + + (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)) ; + } + } +#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 used to construct the Diagonal_map. + */ + + 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 ; + } + + for (newcol = 0 ; newcol < nn ; newcol++) + { + oldcol = Cperm_init [newcol] ; + oldrow = oldcol ; + newrow = Ci [oldrow] ; + ASSERT (newrow >= 0 && newrow < nn) ; + 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 ; + CLEAR (aij) ; + oldrow = Ai [p] ; + newrow = Ci [oldrow] ; + if (Ax != (double *) NULL) + { + ASSIGN (aij, Ax, Az, p, SPLIT (Az)) ; + } + 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 ; + } + } + + Chain_maxrows [nchains] = 0 ; + Chain_maxcols [nchains] = 0 ; + + /* 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) /* divide by pivot */ + /* 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->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) ; +} + + +/* ========================================================================== */ +/* === 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] +) +{ + return (symbolic_analysis (n_row, n_col, Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + + /* user-provided ordering (ignored if NULL) */ + Quser, + + /* no user provided ordering function */ + (void *) NULL, + (void *) NULL, + + SymbolicHandle, Control, User_Info)) ; +} + + +/* ========================================================================== */ +/* === UMFPACK_fsymbolic ==================================================== */ +/* ========================================================================== */ + +GLOBAL Int UMFPACK_fsymbolic +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + + /* user-provided ordering function */ + int (*user_ordering) /* TRUE if OK, FALSE otherwise */ + ( + /* inputs, not modified on output */ + Int, /* nrow */ + Int, /* ncol */ + Int, /* sym: if TRUE and nrow==ncol do A+A', else do A'A */ + Int *, /* Ap, size ncol+1 */ + Int *, /* Ai, size nz */ + /* output */ + Int *, /* size ncol, fill-reducing permutation */ + /* input/output */ + void *, /* user_params (ignored by UMFPACK) */ + double * /* user_info[0..2], optional output for symmetric case. + user_info[0]: max column count for L=chol(P(A+A')P') + user_info[1]: nnz (L) + user_info[2]: flop count for chol, if A real */ + ), + void *user_params, /* passed to user_ordering function */ + + void **SymbolicHandle, + const double Control [UMFPACK_CONTROL], + double User_Info [UMFPACK_INFO] +) +{ + return (symbolic_analysis (n_row, n_col, Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + + /* user ordering not provided */ + (Int *) NULL, + /* user ordering functions used instead */ + user_ordering, + user_params, + + SymbolicHandle, Control, User_Info)) ; +} diff --git a/src/maths/UMFPACK/umfpack_report_control.c b/src/maths/UMFPACK/umfpack_report_control.c new file mode 100644 index 000000000..df317aaa4 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_report_control.c @@ -0,0 +1,379 @@ +/* ========================================================================== */ +/* === UMFPACK_report_control =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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] +) +{ + double drow, dcol, relpt, relpt2, alloc_init, front_alloc_init, amd_alpha, + force_fixQ, droptol, aggr ; + Int prl, nb, irstep, strategy, scale, s ; + Int do_singletons, ordering_option ; + + prl = GET_CONTROL (UMFPACK_PRL, UMFPACK_DEFAULT_PRL) ; + + if (prl < 2) + { + /* default is to print nothing */ + return ; + } + + PRINTF (("UMFPACK V%d.%d.%d (%s), Control:\n", UMFPACK_MAIN_VERSION, + UMFPACK_SUB_VERSION, UMFPACK_SUBSUB_VERSION, UMFPACK_DATE)) ; + + /* ---------------------------------------------------------------------- */ + /* 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: UF_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: UF_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 /* auto strategy */ + { + strategy = UMFPACK_STRATEGY_AUTO ; + PRINTF ((" (auto)\n")) ; + } + + /* ---------------------------------------------------------------------- */ + /* ordering */ + /* ---------------------------------------------------------------------- */ + + ordering_option = GET_CONTROL (UMFPACK_ORDERING, UMFPACK_DEFAULT_ORDERING) ; + if (ordering_option < 0 || ordering_option > UMFPACK_ORDERING_USER) + { + ordering_option = UMFPACK_DEFAULT_ORDERING ; + } + PRINTF ((" "ID": ordering: "ID, + (Int) INDEX (UMFPACK_ORDERING), ordering_option)) ; + if (ordering_option == UMFPACK_ORDERING_CHOLMOD) + { + PRINTF ((" CHOLMOD: AMD/COLAMD, then try METIS, and select best\n")) ; + } + else if (ordering_option == UMFPACK_ORDERING_AMD) + { + PRINTF ((" AMD/COLAMD\n")) ; + } + else if (ordering_option == UMFPACK_ORDERING_GIVEN) + { + PRINTF ((" user-provided permutation vector\n")) ; + } + else if (ordering_option == UMFPACK_ORDERING_NONE) + { + PRINTF ((" none\n")) ; + } + else if (ordering_option == UMFPACK_ORDERING_METIS) + { + PRINTF ((" METIS\n")) ; + } + else if (ordering_option == UMFPACK_ORDERING_BEST) + { + PRINTF ((" best effort. Try AMD/COLAMD and METIS, select best\n")) ; + } + else if (ordering_option == UMFPACK_ORDERING_USER) + { + PRINTF ((" user-provided ordering function\n")) ; + } + + /* ---------------------------------------------------------------------- */ + /* disable singletons (default is FALSE) */ + /* ---------------------------------------------------------------------- */ + + do_singletons = GET_CONTROL (UMFPACK_SINGLETONS,UMFPACK_DEFAULT_SINGLETONS); + PRINTF ((" "ID": singleton filter:", (Int) INDEX (UMFPACK_SINGLETONS))) ; + if (do_singletons) + { + PRINTF ((" enabled\n")) ; + } + else + { + PRINTF ((" disabled\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)) ; + + /* ---------------------------------------------------------------------- */ + /* 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))) ; + +#ifdef NBLAS + PRINTF (("none. UMFPACK will be slow.\n")) ; +#else + PRINTF (("Fortran BLAS. size of BLAS integer: "ID"\n", + (Int) (sizeof (BLAS_INT)))) ; +#endif + +#ifdef MATLAB_MEX_FILE + PRINTF ((" compiled for MATLAB\n")) ; +#else + PRINTF ((" compiled for ANSI C\n")) ; +#endif + +#ifdef NO_TIMER + PRINTF ((" no CPU timer \n")) ; +#else +#ifndef NPOSIX + PRINTF ((" CPU timer is POSIX times ( ) routine.\n")) ; +#else +#ifdef GETRUSAGE + PRINTF ((" CPU timer is getrusage.\n")) ; +#else + PRINTF ((" CPU timer is ANSI C clock (may wrap around).\n")) ; +#endif +#endif +#endif + + PRINTF ((" computer/operating system: %s\n", UMFPACK_ARCHITECTURE)) ; + PRINTF ((" size of int: %g UF_long: %g Int: %g pointer: %g" + " double: %g Entry: %g (in bytes)\n\n", (double) sizeof (int), + (double) sizeof (UF_long), (double) sizeof (Int), + (double) sizeof (void *), (double) sizeof (double), + (double) sizeof (Entry))) ; +} diff --git a/src/maths/UMFPACK/umfpack_report_info.c b/src/maths/UMFPACK/umfpack_report_info.c new file mode 100644 index 000000000..7186cc128 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_report_info.c @@ -0,0 +1,627 @@ +/* ========================================================================== */ +/* === UMFPACK_report_info ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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, strategy ; + + /* ---------------------------------------------------------------------- */ + /* 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 (("UMFPACK V%d.%d.%d (%s), Info:\n", UMFPACK_MAIN_VERSION, + UMFPACK_SUB_VERSION, UMFPACK_SUBSUB_VERSION, UMFPACK_DATE)) ; + +#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: UF_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: UF_long\n")) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* print compile-time options */ + /* ---------------------------------------------------------------------- */ + + PRINTF ((" BLAS library used: ")) ; + +#ifdef NBLAS + PRINTF (("none. UMFPACK will be slow.\n")) ; +#else + PRINTF (("Fortran BLAS. size of BLAS integer: "ID"\n", + (Int) (sizeof (BLAS_INT)))) ; +#endif + + PRINTF ((" MATLAB: ")) ; +#ifdef MATLAB_MEX_FILE + PRINTF (("yes.\n")) ; +#else +#ifdef MATHWORKS + PRINTF (("yes.\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 UF_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 */ + /* ---------------------------------------------------------------------- */ + + strategy = Info [UMFPACK_STRATEGY_USED] ; + if (strategy == UMFPACK_STRATEGY_SYMMETRIC) + { + PRINTF (("\n strategy used: symmetric\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_GIVEN) + { + PRINTF ((" ordering used: user perm.\n")) ; + } + else if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_USER) + { + PRINTF ((" ordering used: user function\n")) ; + } + else if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_NONE) + { + PRINTF ((" ordering used: none\n")) ; + } + else if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_METIS) + { + PRINTF ((" ordering used: metis on A+A'\n")) ; + } + else + { + PRINTF ((" ordering used: not computed\n")) ; + } + } + else + { + PRINTF (("\n strategy used: unsymmetric\n")) ; + if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_AMD) + { + PRINTF ((" ordering used: colamd on A\n")) ; + } + else if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_GIVEN) + { + PRINTF ((" ordering used: user perm.\n")) ; + } + else if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_USER) + { + PRINTF ((" ordering used: user function\n")) ; + } + else if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_NONE) + { + PRINTF ((" ordering used: none\n")) ; + } + else if (Info [UMFPACK_ORDERING_USED] == UMFPACK_ORDERING_METIS) + { + PRINTF ((" ordering used: metis on A'A\n")) ; + } + else + { + PRINTF ((" ordering used: not computed\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 AMD */ + /* ---------------------------------------------------------------------- */ + + if (strategy == UMFPACK_STRATEGY_SYMMETRIC && + Info [UMFPACK_ORDERING_USED] != UMFPACK_ORDERING_GIVEN) + { + 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]) ; + if (dmax > 0) + { + 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) ; + +#define TMIN 0.001 + + if (tnum > TMIN && fnum > 0) + { + PRINT_INFO ( + " numeric factorization mflops (CPU time): %.2f\n", + 1e-6 * fnum / tnum) ; + } + if (twnum > TMIN && fnum > 0) + { + PRINT_INFO ( + " numeric factorization mflops (wallclock): %.2f\n", + 1e-6 * fnum / twnum) ; + } + + ttot = EMPTY ; + ftot = fnum ; + if (tsym >= TMIN && tnum >= 0) + { + ttot = tsym + tnum ; + PRINT_INFO (" symbolic + numeric CPU time (sec): %.2f\n", + ttot) ; + if (ftot > 0 && ttot > TMIN) + { + PRINT_INFO ( + " symbolic + numeric mflops (CPU time): %.2f\n", + 1e-6 * ftot / ttot) ; + } + } + + twtot = EMPTY ; + if (twsym >= TMIN && twnum >= TMIN) + { + twtot = twsym + twnum ; + PRINT_INFO (" symbolic + numeric wall clock time (sec): %.2f\n", + twtot) ; + if (ftot > 0 && twtot > TMIN) + { + 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 > TMIN) + { + PRINT_INFO ( + " solve mflops (CPU time): %.2f\n", + 1e-6 * fsolve / tsolve) ; + } + if (fsolve > 0 && twsolve > TMIN) + { + 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 >= TMIN) + { + if (ttot >= TMIN && ftot >= 0) + { + ttot += tsolve ; + PRINT_INFO ( + " total symbolic + numeric + solve CPU time: %.2f\n", + ttot) ; + if (ftot > 0 && ttot > TMIN) + { + PRINT_INFO ( + " total symbolic + numeric + solve mflops (CPU): %.2f\n", + 1e-6 * ftot / ttot) ; + } + } + } + + if (twsolve >= TMIN) + { + if (twtot >= TMIN && ftot >= 0) + { + twtot += tsolve ; + PRINT_INFO ( + " total symbolic+numeric+solve wall clock time: %.2f\n", + twtot) ; + if (ftot > 0 && twtot > TMIN) + { + PRINT_INFO ( + " total symbolic+numeric+solve mflops(wallclock) %.2f\n", + 1e-6 * ftot / twtot) ; + } + } + } + PRINTF (("\n")) ; +} diff --git a/src/maths/UMFPACK/umfpack_report_matrix.c b/src/maths/UMFPACK/umfpack_report_matrix.c new file mode 100644 index 000000000..910b68237 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_report_matrix.c @@ -0,0 +1,203 @@ +/* ========================================================================== */ +/* === UMFPACK_report_matrix ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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" + +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] +) +{ + Entry a ; + Int prl, i, k, length, ilast, p, nz, prl1, p1, p2, n, n_i, do_values ; + char *vector_kind, *index_kind ; +#ifdef COMPLEX + Int split = SPLIT (Az) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* 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_kind = "column" ; /* column vectors */ + index_kind = "row" ; /* with row indices */ + n = n_col ; + n_i = n_row ; + } + else + { + vector_kind = "row" ; /* row vectors */ + index_kind = "column" ; /* with column indices */ + n = n_row ; + n_i = n_col ; + } + + PRINTF (("%s-form matrix, n_row "ID" n_col "ID", ", + vector_kind, 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) ; + } + + do_values = Ax != (double *) NULL ; + + 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_kind, 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_kind, INDEX (k), p1, p2-1, length)) ; + ilast = EMPTY ; + for (p = p1 ; p < p2 ; p++) + { + i = Ai [p] ; + PRINTF4 (("\t%s "ID" ", index_kind, INDEX (i))) ; + if (do_values && prl >= 4) + { + PRINTF ((":")) ; + ASSIGN (a, Ax, Az, p, split) ; + PRINT_ENTRY (a) ; + } + if (i < 0 || i >= n_i) + { + PRINTF ((" ERROR: %s index "ID" out of range in %s "ID"\n\n", + index_kind, INDEX (i), vector_kind, 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_kind, INDEX (i), + vector_kind, 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_kind)) ; + PRINTF (("OK\n\n")) ; + + return (UMFPACK_OK) ; +} diff --git a/src/maths/UMFPACK/umfpack_report_numeric.c b/src/maths/UMFPACK/umfpack_report_numeric.c new file mode 100644 index 000000000..f70555fde --- /dev/null +++ b/src/maths/UMFPACK/umfpack_report_numeric.c @@ -0,0 +1,663 @@ +/* ========================================================================== */ +/* === UMFPACK_report_numeric =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umfpack_report_perm.c b/src/maths/UMFPACK/umfpack_report_perm.c new file mode 100644 index 000000000..5f3baab2e --- /dev/null +++ b/src/maths/UMFPACK/umfpack_report_perm.c @@ -0,0 +1,44 @@ +/* ========================================================================== */ +/* === UMFPACK_report_perm ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umfpack_report_status.c b/src/maths/UMFPACK/umfpack_report_status.c new file mode 100644 index 000000000..e831f39fa --- /dev/null +++ b/src/maths/UMFPACK/umfpack_report_status.c @@ -0,0 +1,123 @@ +/* ========================================================================== */ +/* === UMFPACK_report_status ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 occurred. */ + /* 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 (("UMFPACK V%d.%d.%d (%s): ", UMFPACK_MAIN_VERSION, + UMFPACK_SUB_VERSION, UMFPACK_SUBSUB_VERSION, UMFPACK_DATE)) ; + + 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_ordering_failed: + PRINTF (("ERROR: ordering failed\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/src/maths/UMFPACK/umfpack_report_symbolic.c b/src/maths/UMFPACK/umfpack_report_symbolic.c new file mode 100644 index 000000000..9d444178e --- /dev/null +++ b/src/maths/UMFPACK/umfpack_report_symbolic.c @@ -0,0 +1,243 @@ +/* ========================================================================== */ +/* === UMFPACK_report_symbolic ============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Prints the Symbolic object. See umfpack_report_symbolic.h + for details. Not all of the object is printed. + + 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\n")) ; + PRINTF ((" ordering used: ")) ; + if (Symbolic->ordering == UMFPACK_ORDERING_AMD) + { + PRINTF (("amd on A\n")) ; + } + else if (Symbolic->ordering == UMFPACK_ORDERING_GIVEN) + { + PRINTF (("user permutation")) ; + } + else if (Symbolic->ordering == UMFPACK_ORDERING_USER) + { + PRINTF (("user function")) ; + } + else if (Symbolic->ordering == UMFPACK_ORDERING_METIS) + { + PRINTF (("metis on A")) ; + } + } + else /* if (Symbolic->strategy == UMFPACK_STRATEGY_UNSYMMETRIC) */ + { + PRINTF (("unsymmetric\n")) ; + PRINTF ((" ordering used: ")) ; + if (Symbolic->ordering == UMFPACK_ORDERING_AMD) + { + PRINTF (("colamd on A\n")) ; + } + else if (Symbolic->ordering == UMFPACK_ORDERING_GIVEN) + { + PRINTF (("user permutation")) ; + } + else if (Symbolic->ordering == UMFPACK_ORDERING_USER) + { + PRINTF (("user function")) ; + } + else if (Symbolic->ordering == UMFPACK_ORDERING_METIS) + { + PRINTF (("metis on A'A")) ; + } + } + 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/src/maths/UMFPACK/umfpack_report_triplet.c b/src/maths/UMFPACK/umfpack_report_triplet.c new file mode 100644 index 000000000..ba11a69d6 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_report_triplet.c @@ -0,0 +1,99 @@ +/* ========================================================================== */ +/* === UMFPACK_report_triplet =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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] +) +{ + Entry t ; + Int prl, prl1, k, i, j, do_values ; +#ifdef COMPLEX + Int split = SPLIT (Tz) ; +#endif + + 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")) ; + + do_values = Tx != (double *) NULL ; + + 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, Tz, k, split) ; + 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/src/maths/UMFPACK/umfpack_report_vector.c b/src/maths/UMFPACK/umfpack_report_vector.c new file mode 100644 index 000000000..a88804c64 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_report_vector.c @@ -0,0 +1,43 @@ +/* ========================================================================== */ +/* === UMFPACK_report_vector ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umfpack_save_numeric.c b/src/maths/UMFPACK/umfpack_save_numeric.c new file mode 100644 index 000000000..befec30ac --- /dev/null +++ b/src/maths/UMFPACK/umfpack_save_numeric.c @@ -0,0 +1,96 @@ +/* ========================================================================== */ +/* === UMFPACK_save_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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) != (size_t) n) \ + { \ + fclose (f) ; \ + return (UMFPACK_ERROR_file_IO) ; \ + } \ + fflush (f) ; \ +} + +/* ========================================================================== */ +/* === 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) ; + } + /* It is possible that some parts of Numeric->Memory are + unitialized and unused; this is OK, but it can generate + a valgrind warning. */ + WRITE (Numeric->Memory, Unit, Numeric->size) ; + + /* close the file */ + fclose (f) ; + + return (UMFPACK_OK) ; +} diff --git a/src/maths/UMFPACK/umfpack_save_symbolic.c b/src/maths/UMFPACK/umfpack_save_symbolic.c new file mode 100644 index 000000000..871c645df --- /dev/null +++ b/src/maths/UMFPACK/umfpack_save_symbolic.c @@ -0,0 +1,96 @@ +/* ========================================================================== */ +/* === UMFPACK_save_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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) != (size_t) n) \ + { \ + fclose (f) ; \ + return (UMFPACK_ERROR_file_IO) ; \ + } \ + fflush (f) ; \ +} + +/* ========================================================================== */ +/* === 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/src/maths/UMFPACK/umfpack_scale.c b/src/maths/UMFPACK/umfpack_scale.c new file mode 100644 index 000000000..49467a5a7 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_scale.c @@ -0,0 +1,158 @@ +/* ========================================================================== */ +/* === UMFPACK_scale ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 ; +#ifdef COMPLEX + Int split = SPLIT (Xz) && SPLIT (Bz) ; +#endif + + Numeric = (NumericType *) NumericHandle ; + if (!UMF_valid_numeric (Numeric)) + { + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + n = Numeric->n_row ; + Rs = Numeric->Rs ; + + if (!Xx || !Bx) + { + 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 */ +#ifdef COMPLEX + if (split) + { + for (i = 0 ; i < n ; i++) + { + Xx [i] = Bx [i] * Rs [i] ; + Xz [i] = Bz [i] * Rs [i] ; + } + } + else + { + for (i = 0 ; i < n ; i++) + { + Xx [2*i ] = Bx [2*i ] * Rs [i] ; + Xx [2*i+1] = Bx [2*i+1] * Rs [i] ; + } + } +#else + for (i = 0 ; i < n ; i++) + { + Xx [i] = Bx [i] * Rs [i] ; + } +#endif + } + else +#endif + { + /* divide by the scale factors */ +#ifdef COMPLEX + if (split) + { + for (i = 0 ; i < n ; i++) + { + Xx [i] = Bx [i] / Rs [i] ; + Xz [i] = Bz [i] / Rs [i] ; + } + } + else + { + for (i = 0 ; i < n ; i++) + { + Xx [2*i ] = Bx [2*i ] / Rs [i] ; + Xx [2*i+1] = Bx [2*i+1] / Rs [i] ; + } + } +#else + for (i = 0 ; i < n ; i++) + { + Xx [i] = Bx [i] / Rs [i] ; + } +#endif + } + } + else + { + /* no scale factors, just copy B into X */ +#ifdef COMPLEX + if (split) + { + for (i = 0 ; i < n ; i++) + { + Xx [i] = Bx [i] ; + Xz [i] = Bz [i] ; + } + } + else + { + for (i = 0 ; i < n ; i++) + { + Xx [2*i ] = Bx [2*i ] ; + Xx [2*i+1] = Bx [2*i+1] ; + } + } +#else + for (i = 0 ; i < n ; i++) + { + Xx [i] = Bx [i] ; + } +#endif + } + + return (UMFPACK_OK) ; +} diff --git a/src/maths/UMFPACK/umfpack_solve.c b/src/maths/UMFPACK/umfpack_solve.c new file mode 100644 index 000000000..c4e826386 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_solve.c @@ -0,0 +1,245 @@ +/* ========================================================================== */ +/* === UMFPACK_solve ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 */ + /* ---------------------------------------------------------------------- */ + + double Info2 [UMFPACK_INFO], stats [2] ; + double *Info ; + NumericType *Numeric ; + Int n, i, irstep, status ; +#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) + { + 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/src/maths/UMFPACK/umfpack_symbolic.c b/src/maths/UMFPACK/umfpack_symbolic.c new file mode 100644 index 000000000..37d25e05b --- /dev/null +++ b/src/maths/UMFPACK/umfpack_symbolic.c @@ -0,0 +1,38 @@ +/* ========================================================================== */ +/* === UMFPACK_symbolic ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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] +) +{ + return (UMFPACK_qsymbolic (n_row, n_col, Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + (Int *) NULL, SymbolicHandle, Control, Info)) ; +} diff --git a/src/maths/UMFPACK/umfpack_tictoc.c b/src/maths/UMFPACK/umfpack_tictoc.c new file mode 100644 index 000000000..62e0e07e7 --- /dev/null +++ b/src/maths/UMFPACK/umfpack_tictoc.c @@ -0,0 +1,121 @@ +/* ========================================================================== */ +/* === umfpack_tictoc ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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() */ +/* -------------------------------------------------------------------------- */ + +#ifdef LIBRT + +/* Linux/Unix, must compile with -lrt */ +#include + +void umfpack_tic (double stats [2]) +{ + /* get the current real time and return as a double */ + stats [0] = umfpack_timer ( ) ; + stats [1] = stats [0] ; +} + +#else + +#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 */ + + double ticks ; + struct tms t ; + + 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 +#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/src/maths/UMFPACK/umfpack_timer.c b/src/maths/UMFPACK/umfpack_timer.c new file mode 100644 index 000000000..26d03b58d --- /dev/null +++ b/src/maths/UMFPACK/umfpack_timer.c @@ -0,0 +1,100 @@ +/* ========================================================================== */ +/* === umfpack_timer ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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. + See umfpack_tictoc.h, which is the timer used internally by UMFPACK. +*/ + +#include "ngspice/umfpack_timer.h" + +#ifdef NO_TIMER + +/* -------------------------------------------------------------------------- */ +/* no timer used if -DNO_TIMER is defined at compile time */ +/* -------------------------------------------------------------------------- */ + +double umfpack_timer ( void ) +{ + return (0) ; +} + +#else + +#ifdef LIBRT + +#include +double umfpack_timer ( void ) /* returns time in seconds */ +{ + /* get the current real time and return as a double */ + struct timespec now ; + clock_gettime (CLOCK_REALTIME, &now) ; + return ((double) (now.tv_sec ) + (double) (now.tv_nsec) * 1e-9) ; +} + + +#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 +#endif diff --git a/src/maths/UMFPACK/umfpack_transpose.c b/src/maths/UMFPACK/umfpack_transpose.c new file mode 100644 index 000000000..25ea2110f --- /dev/null +++ b/src/maths/UMFPACK/umfpack_transpose.c @@ -0,0 +1,108 @@ +/* ========================================================================== */ +/* === UMFPACK_transpose ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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/src/maths/UMFPACK/umfpack_triplet_to_col.c b/src/maths/UMFPACK/umfpack_triplet_to_col.c new file mode 100644 index 000000000..97750d1ef --- /dev/null +++ b/src/maths/UMFPACK/umfpack_triplet_to_col.c @@ -0,0 +1,225 @@ +/* ========================================================================== */ +/* === UMFPACK_triplet_to_col =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Copyright (c) Timothy A. Davis, CISE, */ +/* Univ. of Florida. All Rights Reserved. See ../Doc/License for License. */ +/* 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 occurred. 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 ; +#ifdef COMPLEX + double *Rz ; + Int split ; +#endif + +#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 ; + + do_values = Ax && Tx ; + + if (do_values) + { +#ifdef COMPLEX + Rx = (double *) UMF_malloc (2*nz+2, sizeof (double)) ; + split = SPLIT (Tz) && SPLIT (Az) ; + if (split) + { + Rz = Rx + nz ; + } + else + { + Rz = (double *) NULL ; + } +#else + Rx = (double *) UMF_malloc (nz+1, sizeof (double)) ; +#endif + if (!Rx) + { + DEBUGm4 (("out of memory: triplet work \n")) ; + ASSERT (UMF_malloc_count == init_count) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + } + + 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) ; + 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 *) 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) + 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 *) 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) ; +} diff --git a/src/maths/UMFPACK/umfpacksmp.c b/src/maths/UMFPACK/umfpacksmp.c new file mode 100644 index 000000000..1171e3c80 --- /dev/null +++ b/src/maths/UMFPACK/umfpacksmp.c @@ -0,0 +1,701 @@ +/* + * Spice3 COMPATIBILITY MODULE + * + * Author: Advising professor: + * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli + * UC Berkeley + * + * This module contains routines that make Sparse1.3 a direct + * replacement for the SMP sparse matrix package in Spice3c1 or Spice3d1. + * Sparse1.3 is in general a faster and more robust package than SMP. + * These advantages become significant on large circuits. + * + * >>> User accessible functions contained in this file: + * SMPaddElt + * SMPmakeElt + * SMPcClear + * SMPclear + * SMPcLUfac + * SMPluFac + * SMPcReorder + * SMPreorder + * SMPcaSolve + * SMPcSolve + * SMPsolve + * SMPmatSize + * SMPnewMatrix + * SMPdestroy + * SMPpreOrder + * SMPprint + * SMPgetError + * SMPcProdDiag + * LoadGmin + * SMPfindElt + * SMPcombine + * SMPcCombine + */ + +/* + * To replace SMP with Sparse, rename the file spSpice3.h to + * spMatrix.h and place Sparse in a subdirectory of SPICE called + * `sparse'. Then on UNIX compile Sparse by executing `make spice'. + * If not on UNIX, after compiling Sparse and creating the sparse.a + * archive, compile this file (spSMP.c) and spSMP.o to the archive, + * then copy sparse.a into the SPICE main directory and rename it + * SMP.a. Finally link SPICE. + * + * To be compatible with SPICE, the following Sparse compiler options + * (in spConfig.h) should be set as shown below: + * + * EXPANDABLE YES + * TRANSLATE NO + * INITIALIZE NO or YES, YES for use with test prog. + * DIAGONAL_PIVOTING YES + * MODIFIED_MARKOWITZ NO + * DELETE NO + * STRIP NO + * MODIFIED_NODAL YES + * QUAD_ELEMENT NO + * TRANSPOSE YES + * SCALING NO + * DOCUMENTATION YES + * MULTIPLICATION NO + * DETERMINANT YES + * STABILITY NO + * CONDITION NO + * PSEUDOCONDITION NO + * DEBUG YES + * + * spREAL double + */ + +/* + * Revision and copyright information. + * + * Copyright (c) 1985,86,87,88,89,90 + * by Kenneth S. Kundert and the University of California. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and supporting + * documentation and that the authors and the University of California + * are properly credited. The authors and the University of California + * make no representations as to the suitability of this software for + * any purpose. It is provided `as is', without express or implied warranty. + */ + +/* + * IMPORTS + * + * >>> Import descriptions: + * spMatrix.h + * Sparse macros and declarations. + * SMPdefs.h + * Spice3's matrix macro definitions. + */ + +#include "ngspice/config.h" +#include +#include +#include +#include "ngspice/spmatrix.h" +#include "../sparse/spdefs.h" +#include "ngspice/smpdefs.h" + +#if defined (_MSC_VER) +extern double scalbn(double, int); +#define logb _logb +extern double logb(double); +#endif + +static void LoadGmin_CSC (double **diag, int n, double Gmin) ; +static void LoadGmin(SMPmatrix *eMatrix, double Gmin); + +void +SMPmatrix_CSC (SMPmatrix *Matrix) +{ + spMatrix_CSC (Matrix->SPmatrix, Matrix->CKTumfpackAp, Matrix->CKTumfpackAi, Matrix->CKTumfpackAx, + Matrix->CKTumfpackN, Matrix->CKTbind_Sparse, Matrix->CKTbind_CSC, Matrix->CKTdiag_CSC) ; + return ; +} + +void +SMPnnz (SMPmatrix *Matrix) +{ + Matrix->CKTumfpackN = spGetSize (Matrix->SPmatrix, 1) ; + Matrix->CKTumfpacknz = Matrix->SPmatrix->Elements ; + return ; +} + +/* + * SMPaddElt() + */ +int +SMPaddElt (SMPmatrix *Matrix, int Row, int Col, double Value) +{ + *spGetElement (Matrix->SPmatrix, Row, Col) = Value ; + return spError (Matrix->SPmatrix) ; +} + +/* + * SMPmakeElt() + */ +double * +SMPmakeElt (SMPmatrix *Matrix, int Row, int Col) +{ + return spGetElement (Matrix->SPmatrix, Row, Col) ; +} + +/* + * SMPcClear() + */ + +void +SMPcClear (SMPmatrix *Matrix) +{ + spClear (Matrix->SPmatrix) ; +} + +/* + * SMPclear() + */ + +void +SMPclear (SMPmatrix *Matrix) +{ + int i ; + if (Matrix->CKTumfpackMODE) + { + spClear (Matrix->SPmatrix) ; + if (Matrix->CKTumfpackAx != NULL) + { + for (i = 0 ; i < Matrix->CKTumfpacknz ; i++) + Matrix->CKTumfpackAx [i] = 0 ; + } + } else { + spClear (Matrix->SPmatrix) ; + } +} + +#define NG_IGNORE(x) (void)x + +/* + * SMPcLUfac() + */ +/*ARGSUSED*/ + +int +SMPcLUfac (SMPmatrix *Matrix, double PivTol) +{ + NG_IGNORE(PivTol); + + spSetComplex (Matrix->SPmatrix) ; + return spFactor (Matrix->SPmatrix) ; +} + +/* + * SMPluFac() + */ +/*ARGSUSED*/ + +int +SMPluFac (SMPmatrix *Matrix, double PivTol, double Gmin) +{//printf("ReFactor\n"); + int status ; + + NG_IGNORE (PivTol) ; + + if (Matrix->CKTumfpackMODE) + { + spSetReal (Matrix->SPmatrix) ; + LoadGmin_CSC (Matrix->CKTdiag_CSC, Matrix->CKTumfpackN, Gmin) ; + + status = umfpack_di_numeric (Matrix->CKTumfpackAp, Matrix->CKTumfpackAi, Matrix->CKTumfpackAx, + Matrix->CKTumfpackSymbolic, &(Matrix->CKTumfpackNumeric), Matrix->CKTumfpackControl, Matrix->CKTumfpackInfo) ; + + if (status < 0) { + umfpack_di_report_info (Matrix->CKTumfpackControl, Matrix->CKTumfpackInfo) ; + umfpack_di_report_status (Matrix->CKTumfpackControl, status) ; + return 1 ; + } + + return 0 ; + + } else { + spSetReal (Matrix->SPmatrix) ; + LoadGmin (Matrix, Gmin) ; + return spFactor (Matrix->SPmatrix) ; + } +} + +/* + * SMPcReorder() + */ + +int +SMPcReorder (SMPmatrix *Matrix, double PivTol, double PivRel, int *NumSwaps) +{ + *NumSwaps = 1 ; + spSetComplex (Matrix->SPmatrix) ; + return spOrderAndFactor (Matrix->SPmatrix, NULL, + (spREAL)PivRel, (spREAL)PivTol, YES) ; +} + +/* + * SMPreorder() + */ + +int +SMPreorder (SMPmatrix *Matrix, double PivTol, double PivRel, double Gmin) +{//printf("Factor\n"); + int status ; + if (Matrix->CKTumfpackMODE) + { + spSetReal (Matrix->SPmatrix) ; + LoadGmin_CSC (Matrix->CKTdiag_CSC, Matrix->CKTumfpackN, Gmin) ; + + status = umfpack_di_numeric (Matrix->CKTumfpackAp, Matrix->CKTumfpackAi, Matrix->CKTumfpackAx, + Matrix->CKTumfpackSymbolic, &(Matrix->CKTumfpackNumeric), Matrix->CKTumfpackControl, Matrix->CKTumfpackInfo) ; + + if (status < 0) { + umfpack_di_report_info (Matrix->CKTumfpackControl, Matrix->CKTumfpackInfo) ; + umfpack_di_report_status (Matrix->CKTumfpackControl, status) ; + return 1 ; + } + + return 0 ; + } + else { + spSetReal (Matrix->SPmatrix) ; + LoadGmin (Matrix, Gmin) ; + return spOrderAndFactor (Matrix->SPmatrix, NULL, + (spREAL)PivRel, (spREAL)PivTol, YES) ; + } +} + +/* + * SMPcaSolve() + */ + +void +SMPcaSolve (SMPmatrix *Matrix, double RHS[], double iRHS[], double Spare[], double iSpare[]) +{ + printf ("SMPcaSolve\n") ; + NG_IGNORE (iSpare) ; + NG_IGNORE (Spare) ; + + spSolveTransposed (Matrix->SPmatrix, RHS, RHS, iRHS, iRHS) ; +} + +/* + * SMPcSolve() + */ + +void +SMPcSolve (SMPmatrix *Matrix, double RHS[], double iRHS[], double Spare[], double iSpare[]) +{ + NG_IGNORE (iSpare) ; + NG_IGNORE (Spare) ; + + spSolve (Matrix->SPmatrix, RHS, RHS, iRHS, iRHS) ; +} + +/* + * SMPsolve() + */ + +void +SMPsolve (SMPmatrix *Matrix, double RHS[], double Spare[]) +{//printf("Solve\n"); + int i, *pExtOrder, status ; + if (Matrix->CKTumfpackMODE) + { + NG_IGNORE (Spare) ; + + pExtOrder = &Matrix->SPmatrix->IntToExtRowMap[Matrix->CKTumfpackN] ; + for (i = Matrix->CKTumfpackN - 1 ; i >= 0 ; i--) + Matrix->CKTumfpackIntermediate [i] = RHS [*(pExtOrder--)] ; + + status = umfpack_di_solve (UMFPACK_A, Matrix->CKTumfpackAp, Matrix->CKTumfpackAi, Matrix->CKTumfpackAx, + Matrix->CKTumfpackX, Matrix->CKTumfpackIntermediate, Matrix->CKTumfpackNumeric, + Matrix->CKTumfpackControl, Matrix->CKTumfpackInfo) ; + + umfpack_di_report_info (Matrix->CKTumfpackControl, Matrix->CKTumfpackInfo) ; + umfpack_di_report_status (Matrix->CKTumfpackControl, status) ; + if (status < 0) printf ("SMPsolve using UMFPACK error\n") ; + + pExtOrder = &Matrix->SPmatrix->IntToExtColMap[Matrix->CKTumfpackN] ; + for (i = Matrix->CKTumfpackN - 1 ; i >= 0 ; i--) + RHS [*(pExtOrder--)] = Matrix->CKTumfpackX [i] ; + } else { + spSolve (Matrix->SPmatrix, RHS, RHS, NULL, NULL) ; + } +} + +/* + * SMPmatSize() + */ +int +SMPmatSize (SMPmatrix *Matrix) +{ + return spGetSize (Matrix->SPmatrix, 1) ; +} + +/* + * SMPnewMatrix() + */ +int +SMPnewMatrix (SMPmatrix *Matrix) +{ + int Error; + Matrix->SPmatrix = spCreate (0, 1, &Error) ; + return Error ; +} + +/* + * SMPdestroy() + */ + +void +SMPdestroy (SMPmatrix *Matrix) +{ + spDestroy (Matrix->SPmatrix) ; +} + +/* + * SMPpreOrder() + */ + +int +SMPpreOrder (SMPmatrix *Matrix) +{//printf("PreOrder\n"); + int status ; + if (Matrix->CKTumfpackMODE) + { + umfpack_di_defaults (Matrix->CKTumfpackControl) ; + Matrix->CKTumfpackControl [UMFPACK_SCALE] = UMFPACK_SCALE_MAX ; + Matrix->CKTumfpackControl [UMFPACK_STRATEGY] = UMFPACK_STRATEGY_SYMMETRIC ; + Matrix->CKTumfpackControl [UMFPACK_ORDERING] = UMFPACK_ORDERING_AMD ; + Matrix->CKTumfpackControl [UMFPACK_SYM_PIVOT_TOLERANCE] = 0.001 ; + + status = umfpack_di_symbolic (Matrix->CKTumfpackN, Matrix->CKTumfpackN, Matrix->CKTumfpackAp, Matrix->CKTumfpackAi, Matrix->CKTumfpackAx, + &(Matrix->CKTumfpackSymbolic), Matrix->CKTumfpackControl, Matrix->CKTumfpackInfo) ; + + if (status < 0) { + umfpack_di_report_info (Matrix->CKTumfpackControl, Matrix->CKTumfpackInfo) ; + umfpack_di_report_status (Matrix->CKTumfpackControl, status) ; + return 1 ; + } + + return 0 ; + + } else { + spMNA_Preorder (Matrix->SPmatrix) ; + return spError (Matrix->SPmatrix) ; + } +} + +/* + * SMPprint() + */ +/*ARGSUSED*/ +void +SMPprint (SMPmatrix *Matrix, FILE *File) +{ + NG_IGNORE (File) ; + + spPrint (Matrix->SPmatrix, 0, 1, 1) ; +} + +/* + * SMPgetError() + */ +void +SMPgetError (SMPmatrix *Matrix, int *Col, int *Row) +{ + spWhereSingular (Matrix->SPmatrix, Row, Col) ; +} + +/* + * SMPcProdDiag() + * note: obsolete for Spice3d2 and later + */ +int +SMPcProdDiag (SMPmatrix *Matrix, SPcomplex *pMantissa, int *pExponent) +{ + spDeterminant (Matrix->SPmatrix, pExponent, &(pMantissa->real), &(pMantissa->imag)) ; + return spError (Matrix->SPmatrix) ; +} + +/* + * SMPcDProd() + */ +int +SMPcDProd (SMPmatrix *Matrix, SPcomplex *pMantissa, int *pExponent) +{ + double re, im, x, y, z; + int p; + + spDeterminant (Matrix->SPmatrix, &p, &re, &im) ; + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 +#endif + +#ifdef debug_print + printf("Determinant 10: (%20g,%20g)^%d\n", re, im, p); +#endif + + /* Convert base 10 numbers to base 2 numbers, for comparison */ + y = p * M_LN10 / M_LN2; + x = (int) y; + y -= x; + + /* ASSERT + * x = integral part of exponent, y = fraction part of exponent + */ + + /* Fold in the fractional part */ +#ifdef debug_print + printf(" ** base10 -> base2 int = %g, frac = %20g\n", x, y); +#endif + z = pow(2.0, y); + re *= z; + im *= z; +#ifdef debug_print + printf(" ** multiplier = %20g\n", z); +#endif + + /* Re-normalize (re or im may be > 2.0 or both < 1.0 */ + if (re != 0.0) { + y = logb(re); + if (im != 0.0) + z = logb(im); + else + z = 0; + } else if (im != 0.0) { + z = logb(im); + y = 0; + } else { + /* Singular */ + /*printf("10 -> singular\n");*/ + y = 0; + z = 0; + } + +#ifdef debug_print + printf(" ** renormalize changes = %g,%g\n", y, z); +#endif + if (y < z) + y = z; + + *pExponent = (int)(x + y); + x = scalbn(re, (int) -y); + z = scalbn(im, (int) -y); +#ifdef debug_print + printf(" ** values are: re %g, im %g, y %g, re' %g, im' %g\n", + re, im, y, x, z); +#endif + pMantissa->real = scalbn(re, (int) -y); + pMantissa->imag = scalbn(im, (int) -y); + +#ifdef debug_print + printf("Determinant 10->2: (%20g,%20g)^%d\n", pMantissa->real, + pMantissa->imag, *pExponent); +#endif + return spError (Matrix->SPmatrix) ; +} + + + +/* + * The following routines need internal knowledge of the Sparse data + * structures. + */ + +/* + * LOAD GMIN + * + * This routine adds Gmin to each diagonal element. Because Gmin is + * added to the current diagonal, which may bear little relation to + * what the outside world thinks is a diagonal, and because the + * elements that are diagonals may change after calling spOrderAndFactor, + * use of this routine is not recommended. It is included here simply + * for compatibility with Spice3. + */ + + +static void +LoadGmin_CSC (double **diag, int n, double Gmin) +{ + + int i ; + if (Gmin != 0.0) { + for (i = 0 ; i < n ; i++) { + if (diag [i] != NULL) *(diag [i]) += Gmin ; + } + } + + return ; +} + +static void +LoadGmin(SMPmatrix *eMatrix, double Gmin) +{ + MatrixPtr Matrix = eMatrix->SPmatrix ; + int I; + ArrayOfElementPtrs Diag; + ElementPtr diag; + + /* Begin `LoadGmin'. */ + assert (IS_SPARSE (Matrix)) ; + + if (Gmin != 0.0) { + Diag = Matrix->Diag; + for (I = Matrix->Size; I > 0; I--) { + if ((diag = Diag[I]) != NULL) + diag->Real += Gmin; + } + } + return; +} + + +/* + * FIND ELEMENT + * + * This routine finds an element in the matrix by row and column number. + * If the element exists, a pointer to it is returned. If not, then NULL + * is returned unless the CreateIfMissing flag is TRUE, in which case a + * pointer to the new element is returned. + */ + +SMPelement * +SMPfindElt(SMPmatrix *eMatrix, int Row, int Col, int CreateIfMissing) +{ + MatrixPtr Matrix = eMatrix->SPmatrix ; + ElementPtr Element; + + /* Begin `SMPfindElt'. */ + assert( IS_SPARSE( Matrix ) ); + Row = Matrix->ExtToIntRowMap[Row]; + Col = Matrix->ExtToIntColMap[Col]; + Element = Matrix->FirstInCol[Col]; + Element = spcFindElementInCol(Matrix, &Element, Row, Col, CreateIfMissing); + return (SMPelement *)Element; +} + +/* XXX The following should probably be implemented in spUtils */ + +/* + * SMPcZeroCol() + */ +int +SMPcZeroCol(SMPmatrix *eMatrix, int Col) +{ + MatrixPtr Matrix = eMatrix->SPmatrix ; + ElementPtr Element; + + Col = Matrix->ExtToIntColMap[Col]; + + for (Element = Matrix->FirstInCol[Col]; + Element != NULL; + Element = Element->NextInCol) + { + Element->Real = 0.0; + Element->Imag = 0.0; + } + + return spError( Matrix ); +} + +/* + * SMPcAddCol() + */ +int +SMPcAddCol(SMPmatrix *eMatrix, int Accum_Col, int Addend_Col) +{ + MatrixPtr Matrix = eMatrix->SPmatrix ; + ElementPtr Accum, Addend, *Prev; + + Accum_Col = Matrix->ExtToIntColMap[Accum_Col]; + Addend_Col = Matrix->ExtToIntColMap[Addend_Col]; + + Addend = Matrix->FirstInCol[Addend_Col]; + Prev = &Matrix->FirstInCol[Accum_Col]; + Accum = *Prev;; + + while (Addend != NULL) { + while (Accum && Accum->Row < Addend->Row) { + Prev = &Accum->NextInCol; + Accum = *Prev; + } + if (!Accum || Accum->Row > Addend->Row) { + Accum = spcCreateElement(Matrix, Addend->Row, Accum_Col, Prev, 0); + } + Accum->Real += Addend->Real; + Accum->Imag += Addend->Imag; + Addend = Addend->NextInCol; + } + + return spError( Matrix ); +} + +/* + * SMPzeroRow() + */ +int +SMPzeroRow(SMPmatrix *eMatrix, int Row) +{ + MatrixPtr Matrix = eMatrix->SPmatrix ; + ElementPtr Element; + + Row = Matrix->ExtToIntColMap[Row]; + + if (Matrix->RowsLinked == NO) + spcLinkRows(Matrix); + + if (Matrix->PreviousMatrixWasComplex || Matrix->Complex) { + for (Element = Matrix->FirstInRow[Row]; + Element != NULL; + Element = Element->NextInRow) + { + Element->Real = 0.0; + Element->Imag = 0.0; + } + } else { + for (Element = Matrix->FirstInRow[Row]; + Element != NULL; + Element = Element->NextInRow) + { + Element->Real = 0.0; + } + } + + return spError( Matrix ); +} + +#ifdef PARALLEL_ARCH +/* + * SMPcombine() + */ +void +SMPcombine(SMPmatrix *Matrix, double RHS[], double Spare[]) +{ + spSetReal (Matrix->SPmatrix) ; + spCombine (Matrix->SPmatrix, RHS, Spare, NULL, NULL) ; +} + +/* + * SMPcCombine() + */ +void +SMPcCombine (SMPmatrix *Matrix, double RHS[], double Spare[], double iRHS[], double iSpare[]) +{ + spSetComplex (Matrix->SPmatrix) ; + spCombine (Matrix->SPmatrix, RHS, Spare, iRHS, iSpare) ; +} +#endif /* PARALLEL_ARCH */