From 413242a1f7a4d5614478c7ec24578fee320f5ae5 Mon Sep 17 00:00:00 2001 From: Fritz Obermeyer Date: Wed, 10 Sep 2014 20:49:58 -0700 Subject: [PATCH] Update to metis 5.1.0 --- README | 2 +- setup.py | 2 +- src/metis/BUILD-Windows.txt | 45 + src/metis/BUILD.txt | 60 + src/metis/CMakeLists.txt | 27 + src/metis/Changelog | 286 + src/metis/GKlib/BUILD.txt | 25 + src/metis/GKlib/CMakeLists.txt | 21 + src/metis/GKlib/GKlib.h | 84 + src/metis/GKlib/GKlibSystem.cmake | 129 + src/metis/GKlib/b64.c | 95 + src/metis/GKlib/blas.c | 36 + src/metis/GKlib/conf/check_thread_storage.c | 5 + src/metis/GKlib/csr.c | 2010 + src/metis/GKlib/error.c | 214 + src/metis/GKlib/evaluate.c | 132 + src/metis/GKlib/fkvkselect.c | 142 + src/metis/GKlib/fs.c | 225 + src/metis/GKlib/getopt.c | 854 + src/metis/GKlib/gk_arch.h | 71 + src/metis/GKlib/gk_defs.h | 69 + src/metis/GKlib/gk_externs.h | 25 + src/metis/GKlib/gk_getopt.h | 64 + src/metis/GKlib/gk_macros.h | 153 + src/metis/GKlib/gk_mkblas.h | 201 + src/metis/GKlib/gk_mkmemory.h | 142 + src/metis/GKlib/gk_mkpqueue.h | 437 + src/metis/GKlib/gk_mkpqueue2.h | 215 + src/metis/GKlib/gk_mkrandom.h | 123 + src/metis/GKlib/gk_mksort.h | 273 + src/metis/GKlib/gk_mkutils.h | 40 + src/metis/GKlib/gk_proto.h | 381 + src/metis/GKlib/gk_struct.h | 268 + src/metis/GKlib/gk_types.h | 38 + src/metis/GKlib/gkregex.c | 10704 + src/metis/GKlib/gkregex.h | 556 + src/metis/GKlib/graph.c | 1574 + src/metis/GKlib/htable.c | 247 + src/metis/GKlib/io.c | 384 + src/metis/GKlib/itemsets.c | 210 + src/metis/GKlib/mcore.c | 393 + src/metis/GKlib/memory.c | 252 + src/metis/GKlib/ms_inttypes.h | 301 + src/metis/GKlib/ms_stat.h | 22 + src/metis/GKlib/ms_stdint.h | 222 + src/metis/GKlib/omp.c | 27 + src/metis/GKlib/pdb.c | 460 + src/metis/GKlib/pqueue.c | 25 + src/metis/GKlib/random.c | 134 + src/metis/GKlib/rw.c | 103 + src/metis/GKlib/seq.c | 174 + src/metis/GKlib/sort.c | 327 + src/metis/GKlib/string.c | 529 + src/metis/GKlib/test/CMakeLists.txt | 13 + src/metis/GKlib/test/Makefile.in.old | 258 + src/metis/GKlib/test/Makefile.old | 39 + src/metis/GKlib/test/fis.c | 286 + src/metis/GKlib/test/gkgraph.c | 351 + src/metis/GKlib/test/gksort.c | 346 + src/metis/GKlib/test/rw.c | 307 + src/metis/GKlib/test/strings.c | 82 + src/metis/GKlib/timers.c | 52 + src/metis/GKlib/tokenizer.c | 77 + src/metis/GKlib/util.c | 108 + src/metis/Install.txt | 25 + src/metis/LICENSE.txt | 18 + src/metis/balance.c | 276 - src/metis/ccgraph.c | 520 - src/metis/checkgraph.c | 125 - src/metis/cmetis.c | 1161 - src/metis/coarsen.c | 86 - src/metis/compress.c | 255 - src/metis/debug.c | 272 - src/metis/defs.h | 127 - src/metis/estmem.c | 155 - src/metis/fm.c | 193 - src/metis/fortran.c | 150 - src/metis/frename.c | 310 - src/metis/graph.c | 492 - src/metis/graphs/4elt.graph | 15607 + src/metis/graphs/README | 19 + src/metis/graphs/copter2.graph | 55477 ++++ src/metis/graphs/mdual.graph | 258570 +++++++++++++++++ src/metis/graphs/metis.mesh | 7435 + src/metis/graphs/test.mgraph | 770 + src/metis/include/CMakeLists.txt | 3 + src/metis/include/metis.h | 350 + src/metis/initpart.c | 424 - src/metis/kmetis.c | 127 - src/metis/kvmetis.c | 130 - src/metis/kwayfm.c | 672 - src/metis/kwayrefine.c | 480 - src/metis/kwayvolfm.c | 1778 - src/metis/kwayvolrefine.c | 456 - src/metis/libmetis/CMakeLists.txt | 16 + src/metis/libmetis/auxapi.c | 43 + src/metis/libmetis/balance.c | 498 + src/metis/{ => libmetis}/bucketsort.c | 17 +- src/metis/libmetis/checkgraph.c | 263 + src/metis/libmetis/coarsen.c | 1132 + src/metis/libmetis/compress.c | 229 + src/metis/libmetis/contig.c | 699 + src/metis/libmetis/debug.c | 461 + src/metis/libmetis/defs.h | 60 + src/metis/libmetis/fm.c | 543 + src/metis/libmetis/fortran.c | 142 + src/metis/libmetis/frename.c | 136 + src/metis/libmetis/gklib.c | 120 + src/metis/libmetis/gklib_defs.h | 53 + src/metis/libmetis/gklib_rename.h | 122 + src/metis/libmetis/graph.c | 274 + src/metis/libmetis/initpart.c | 630 + src/metis/libmetis/kmetis.c | 243 + src/metis/libmetis/kwayfm.c | 1852 + src/metis/libmetis/kwayrefine.c | 672 + src/metis/libmetis/macros.h | 258 + src/metis/libmetis/mcutil.c | 330 + src/metis/libmetis/mesh.c | 412 + src/metis/libmetis/meshpart.c | 262 + src/metis/libmetis/metislib.h | 41 + src/metis/libmetis/minconn.c | 729 + src/metis/{ => libmetis}/mincover.c | 58 +- src/metis/{ => libmetis}/mmd.c | 36 +- src/metis/libmetis/ometis.c | 701 + src/metis/libmetis/options.c | 532 + src/metis/libmetis/parmetis.c | 723 + src/metis/libmetis/pmetis.c | 387 + src/metis/libmetis/proto.h | 348 + src/metis/libmetis/refine.c | 211 + src/metis/libmetis/rename.h | 266 + src/metis/libmetis/separator.c | 176 + src/metis/libmetis/sfm.c | 612 + src/metis/libmetis/srefine.c | 163 + src/metis/libmetis/stat.c | 179 + src/metis/{ => libmetis}/stdheaders.h | 5 +- src/metis/libmetis/struct.h | 206 + src/metis/libmetis/timing.c | 63 + src/metis/libmetis/util.c | 138 + src/metis/libmetis/wspace.c | 214 + src/metis/macros.h | 55 - src/metis/manual/manual.pdf | Bin 0 -> 355362 bytes src/metis/match.c | 267 - src/metis/mbalance.c | 260 - src/metis/mbalance2.c | 328 - src/metis/mcoarsen.c | 96 - src/metis/memory.c | 276 - src/metis/mesh.c | 1028 - src/metis/meshpart.c | 433 - src/metis/metis.h | 297 - src/metis/metislib.h | 50 - src/metis/mfm.c | 344 - src/metis/mfm2.c | 349 - src/metis/minitpart.c | 356 - src/metis/minitpart2.c | 366 - src/metis/mkmetis.c | 123 - src/metis/mkwayfmh.c | 677 - src/metis/mkwayrefine.c | 295 - src/metis/mmatch.c | 506 - src/metis/mpmetis.c | 402 - src/metis/mrefine.c | 217 - src/metis/mrefine2.c | 55 - src/metis/mrkmetis.c | 102 - src/metis/mutil.c | 101 - src/metis/myqsort.c | 58 - src/metis/ometis.c | 762 - src/metis/parmetis.c | 368 - src/metis/pmetis.c | 330 - src/metis/pqueue.c | 578 - src/metis/programs/CMakeLists.txt | 31 + src/metis/programs/cmdline_gpmetis.c | 391 + src/metis/programs/cmdline_m2gmetis.c | 148 + src/metis/programs/cmdline_mpmetis.c | 366 + src/metis/programs/cmdline_ndmetis.c | 272 + src/metis/programs/cmpfillin.c | 73 + src/metis/programs/defs.h | 59 + src/metis/programs/gpmetis.c | 222 + src/metis/programs/graphchk.c | 74 + src/metis/programs/io.c | 568 + src/metis/programs/m2gmetis.c | 140 + src/metis/programs/metisbin.h | 52 + src/metis/programs/mpmetis.c | 190 + src/metis/programs/ndmetis.c | 169 + src/metis/programs/proto.h | 60 + src/metis/programs/smbfactor.c | 332 + src/metis/programs/stat.c | 148 + src/metis/programs/struct.h | 69 + src/metis/proto.h | 509 - src/metis/refine.c | 204 - src/metis/rename.h | 398 - src/metis/rkmetis.c | 122 - src/metis/separator.c | 286 - src/metis/sfm.c | 1069 - src/metis/srefine.c | 166 - src/metis/stat.c | 317 - src/metis/streamio.c | 163 - src/metis/struct.h | 279 - src/metis/subdomains.c | 1295 - src/metis/timing.c | 66 - src/metis/util.c | 136 - src/metis/vsgen.bat | 5 + 200 files changed, 382298 insertions(+), 22335 deletions(-) create mode 100644 src/metis/BUILD-Windows.txt create mode 100644 src/metis/BUILD.txt create mode 100644 src/metis/CMakeLists.txt create mode 100644 src/metis/Changelog create mode 100644 src/metis/GKlib/BUILD.txt create mode 100644 src/metis/GKlib/CMakeLists.txt create mode 100644 src/metis/GKlib/GKlib.h create mode 100644 src/metis/GKlib/GKlibSystem.cmake create mode 100644 src/metis/GKlib/b64.c create mode 100644 src/metis/GKlib/blas.c create mode 100644 src/metis/GKlib/conf/check_thread_storage.c create mode 100644 src/metis/GKlib/csr.c create mode 100644 src/metis/GKlib/error.c create mode 100644 src/metis/GKlib/evaluate.c create mode 100644 src/metis/GKlib/fkvkselect.c create mode 100644 src/metis/GKlib/fs.c create mode 100644 src/metis/GKlib/getopt.c create mode 100644 src/metis/GKlib/gk_arch.h create mode 100644 src/metis/GKlib/gk_defs.h create mode 100644 src/metis/GKlib/gk_externs.h create mode 100644 src/metis/GKlib/gk_getopt.h create mode 100644 src/metis/GKlib/gk_macros.h create mode 100644 src/metis/GKlib/gk_mkblas.h create mode 100644 src/metis/GKlib/gk_mkmemory.h create mode 100644 src/metis/GKlib/gk_mkpqueue.h create mode 100644 src/metis/GKlib/gk_mkpqueue2.h create mode 100644 src/metis/GKlib/gk_mkrandom.h create mode 100644 src/metis/GKlib/gk_mksort.h create mode 100644 src/metis/GKlib/gk_mkutils.h create mode 100644 src/metis/GKlib/gk_proto.h create mode 100644 src/metis/GKlib/gk_struct.h create mode 100644 src/metis/GKlib/gk_types.h create mode 100644 src/metis/GKlib/gkregex.c create mode 100644 src/metis/GKlib/gkregex.h create mode 100644 src/metis/GKlib/graph.c create mode 100644 src/metis/GKlib/htable.c create mode 100644 src/metis/GKlib/io.c create mode 100644 src/metis/GKlib/itemsets.c create mode 100644 src/metis/GKlib/mcore.c create mode 100644 src/metis/GKlib/memory.c create mode 100644 src/metis/GKlib/ms_inttypes.h create mode 100644 src/metis/GKlib/ms_stat.h create mode 100644 src/metis/GKlib/ms_stdint.h create mode 100644 src/metis/GKlib/omp.c create mode 100644 src/metis/GKlib/pdb.c create mode 100644 src/metis/GKlib/pqueue.c create mode 100644 src/metis/GKlib/random.c create mode 100644 src/metis/GKlib/rw.c create mode 100644 src/metis/GKlib/seq.c create mode 100644 src/metis/GKlib/sort.c create mode 100644 src/metis/GKlib/string.c create mode 100644 src/metis/GKlib/test/CMakeLists.txt create mode 100644 src/metis/GKlib/test/Makefile.in.old create mode 100644 src/metis/GKlib/test/Makefile.old create mode 100644 src/metis/GKlib/test/fis.c create mode 100644 src/metis/GKlib/test/gkgraph.c create mode 100644 src/metis/GKlib/test/gksort.c create mode 100644 src/metis/GKlib/test/rw.c create mode 100644 src/metis/GKlib/test/strings.c create mode 100644 src/metis/GKlib/timers.c create mode 100644 src/metis/GKlib/tokenizer.c create mode 100644 src/metis/GKlib/util.c create mode 100644 src/metis/Install.txt create mode 100644 src/metis/LICENSE.txt delete mode 100644 src/metis/balance.c delete mode 100644 src/metis/ccgraph.c delete mode 100644 src/metis/checkgraph.c delete mode 100644 src/metis/cmetis.c delete mode 100644 src/metis/coarsen.c delete mode 100644 src/metis/compress.c delete mode 100644 src/metis/debug.c delete mode 100644 src/metis/defs.h delete mode 100644 src/metis/estmem.c delete mode 100644 src/metis/fm.c delete mode 100644 src/metis/fortran.c delete mode 100644 src/metis/frename.c delete mode 100644 src/metis/graph.c create mode 100644 src/metis/graphs/4elt.graph create mode 100644 src/metis/graphs/README create mode 100644 src/metis/graphs/copter2.graph create mode 100644 src/metis/graphs/mdual.graph create mode 100644 src/metis/graphs/metis.mesh create mode 100644 src/metis/graphs/test.mgraph create mode 100644 src/metis/include/CMakeLists.txt create mode 100644 src/metis/include/metis.h delete mode 100644 src/metis/initpart.c delete mode 100644 src/metis/kmetis.c delete mode 100644 src/metis/kvmetis.c delete mode 100644 src/metis/kwayfm.c delete mode 100644 src/metis/kwayrefine.c delete mode 100644 src/metis/kwayvolfm.c delete mode 100644 src/metis/kwayvolrefine.c create mode 100644 src/metis/libmetis/CMakeLists.txt create mode 100644 src/metis/libmetis/auxapi.c create mode 100644 src/metis/libmetis/balance.c rename src/metis/{ => libmetis}/bucketsort.c (67%) create mode 100644 src/metis/libmetis/checkgraph.c create mode 100644 src/metis/libmetis/coarsen.c create mode 100644 src/metis/libmetis/compress.c create mode 100644 src/metis/libmetis/contig.c create mode 100644 src/metis/libmetis/debug.c create mode 100644 src/metis/libmetis/defs.h create mode 100644 src/metis/libmetis/fm.c create mode 100644 src/metis/libmetis/fortran.c create mode 100644 src/metis/libmetis/frename.c create mode 100644 src/metis/libmetis/gklib.c create mode 100644 src/metis/libmetis/gklib_defs.h create mode 100644 src/metis/libmetis/gklib_rename.h create mode 100644 src/metis/libmetis/graph.c create mode 100644 src/metis/libmetis/initpart.c create mode 100644 src/metis/libmetis/kmetis.c create mode 100644 src/metis/libmetis/kwayfm.c create mode 100644 src/metis/libmetis/kwayrefine.c create mode 100644 src/metis/libmetis/macros.h create mode 100644 src/metis/libmetis/mcutil.c create mode 100644 src/metis/libmetis/mesh.c create mode 100644 src/metis/libmetis/meshpart.c create mode 100644 src/metis/libmetis/metislib.h create mode 100644 src/metis/libmetis/minconn.c rename src/metis/{ => libmetis}/mincover.c (79%) rename src/metis/{ => libmetis}/mmd.c (94%) create mode 100644 src/metis/libmetis/ometis.c create mode 100644 src/metis/libmetis/options.c create mode 100644 src/metis/libmetis/parmetis.c create mode 100644 src/metis/libmetis/pmetis.c create mode 100644 src/metis/libmetis/proto.h create mode 100644 src/metis/libmetis/refine.c create mode 100644 src/metis/libmetis/rename.h create mode 100644 src/metis/libmetis/separator.c create mode 100644 src/metis/libmetis/sfm.c create mode 100644 src/metis/libmetis/srefine.c create mode 100644 src/metis/libmetis/stat.c rename src/metis/{ => libmetis}/stdheaders.h (73%) create mode 100644 src/metis/libmetis/struct.h create mode 100644 src/metis/libmetis/timing.c create mode 100644 src/metis/libmetis/util.c create mode 100644 src/metis/libmetis/wspace.c delete mode 100644 src/metis/macros.h create mode 100644 src/metis/manual/manual.pdf delete mode 100644 src/metis/match.c delete mode 100644 src/metis/mbalance.c delete mode 100644 src/metis/mbalance2.c delete mode 100644 src/metis/mcoarsen.c delete mode 100644 src/metis/memory.c delete mode 100644 src/metis/mesh.c delete mode 100644 src/metis/meshpart.c delete mode 100644 src/metis/metis.h delete mode 100644 src/metis/metislib.h delete mode 100644 src/metis/mfm.c delete mode 100644 src/metis/mfm2.c delete mode 100644 src/metis/minitpart.c delete mode 100644 src/metis/minitpart2.c delete mode 100644 src/metis/mkmetis.c delete mode 100644 src/metis/mkwayfmh.c delete mode 100644 src/metis/mkwayrefine.c delete mode 100644 src/metis/mmatch.c delete mode 100644 src/metis/mpmetis.c delete mode 100644 src/metis/mrefine.c delete mode 100644 src/metis/mrefine2.c delete mode 100644 src/metis/mrkmetis.c delete mode 100644 src/metis/mutil.c delete mode 100644 src/metis/myqsort.c delete mode 100644 src/metis/ometis.c delete mode 100644 src/metis/parmetis.c delete mode 100644 src/metis/pmetis.c delete mode 100644 src/metis/pqueue.c create mode 100644 src/metis/programs/CMakeLists.txt create mode 100644 src/metis/programs/cmdline_gpmetis.c create mode 100644 src/metis/programs/cmdline_m2gmetis.c create mode 100644 src/metis/programs/cmdline_mpmetis.c create mode 100644 src/metis/programs/cmdline_ndmetis.c create mode 100644 src/metis/programs/cmpfillin.c create mode 100644 src/metis/programs/defs.h create mode 100644 src/metis/programs/gpmetis.c create mode 100644 src/metis/programs/graphchk.c create mode 100644 src/metis/programs/io.c create mode 100644 src/metis/programs/m2gmetis.c create mode 100644 src/metis/programs/metisbin.h create mode 100644 src/metis/programs/mpmetis.c create mode 100644 src/metis/programs/ndmetis.c create mode 100644 src/metis/programs/proto.h create mode 100644 src/metis/programs/smbfactor.c create mode 100644 src/metis/programs/stat.c create mode 100644 src/metis/programs/struct.h delete mode 100644 src/metis/proto.h delete mode 100644 src/metis/refine.c delete mode 100644 src/metis/rename.h delete mode 100644 src/metis/rkmetis.c delete mode 100644 src/metis/separator.c delete mode 100644 src/metis/sfm.c delete mode 100644 src/metis/srefine.c delete mode 100644 src/metis/stat.c delete mode 100644 src/metis/streamio.c delete mode 100644 src/metis/struct.h delete mode 100644 src/metis/subdomains.c delete mode 100644 src/metis/timing.c delete mode 100644 src/metis/util.c create mode 100644 src/metis/vsgen.bat diff --git a/README b/README index f22d7a9..93bae9d 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ Welcome to PyMetis, a Python wrapper for Metis. ----------------------------------------------- -This code wraps (and includes a copy of) Metis 5.0pre2. +This code wraps (and includes a copy of) Metis 5.1.0. Metis is the work of the George Karypis, Vipin Kumar and others at UMN. It may be downloaded from diff --git a/setup.py b/setup.py index 4a03af6..f971d3a 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ def main(): PyMetis is a Python wrapper for the `Metis `_ graph partititioning software by George Karypis, Vipin Kumar and others. It - includes version 5.0pre2 of Metis and wraps it using the + includes version 5.1.0 of Metis and wraps it using the `Boost Python `_ wrapper generator library. So far, it only wraps the most basic graph partitioning functionality (which is enough for my current use), but extending it diff --git a/src/metis/BUILD-Windows.txt b/src/metis/BUILD-Windows.txt new file mode 100644 index 0000000..7e1a4fa --- /dev/null +++ b/src/metis/BUILD-Windows.txt @@ -0,0 +1,45 @@ +Building METIS requires CMake 2.8, found at +http://www.cmake.org/. CMake generates Visual Studio project files, +which then can be built using Visual Studio. There are two ways to +generate visual studio files: using the command line and using the +CMake GUI. + +Using the command line +---------------------- + +Open the command prompt and cd to the METIS source directory. Run + + > cmake --help + +and look at the list of generators for the Visual Studio studio you +want to build for. For example, the generator for Visual Studio 2010 +is called "Visual Studio 10". + +After you have found the appropriate generator, run + + > .\vsgen -G "" + +to generate the project files. The project files will be placed +build\windows. + + +Using the CMake GUI +------------------- + +It is also possible to use the CMake GUI, distributed with CMake. To +do this, open the CMake GUI, and browse to the location of METIS' +source with the "Browse Source" button. You can also change the binary +directory. This is where the Visual Studio project files will be +placed. Click "Generate" to select the correct visual studio version +and build the project files. + +Using the VS project files +-------------------------- + +The Visual Studio project will be called METIS.sln. Open it in Visual +Studio. If the configuration is not already "Release", set it to +"Release". Type F7 to build. The METIS library will be in +\libmetis\Release and the executable programs will be in +\programs\Release. ( will be build\windows if +you used the command line or whatever you choose if using the CMake +GUI.) diff --git a/src/metis/BUILD.txt b/src/metis/BUILD.txt new file mode 100644 index 0000000..a82de60 --- /dev/null +++ b/src/metis/BUILD.txt @@ -0,0 +1,60 @@ +------------------------------------------------------------------------------ +Building METIS requires CMake 2.8, found at http://www.cmake.org/, as +well as GNU make. Assumming CMake and GNU make are installed, two +commands should suffice to build metis: + + $ make config + $ make + + +Configuration +------------- +METIS is primarily configured by passing options to make config. For +example: + + $ make config shared=1 cc=gcc-4.2 + +would configure metis to be built as a shared library using GCC 4.2. + +Common configuration options are: + cc=[compiler] - The C compiler to use [default is determined by CMake] + shared=1 - Build a shared library instead of a static one + [off by default] + prefix=[PATH] - Set the installation prefix [/usr/local/ by default] + +Advanced debugging related options: + gdb=1 - Build with support for GDB [off by default] + debug=1 - Enable debugging support [off by default] + assert=1 - Enable asserts [off by default] + assert2=1 - Enable very expensive asserts [off by default] + +METIS' index and real data type size can be configured by editing +include/metis.h. + + +Installation +------------ +To install METIS, run + + $ make install + +The default installation prefix is /usr/local. To pick an installation +prefix for METIS pass prefix=[path] to make config. For example, + + $ make config prefix=~/myroot/ + +will cause METIS to be installed in ~/myroot/ when make install is run. + + +Other make commands +------------------- + $ make uninstall + Removes all files installed by 'make install'. + + $ make clean + Removes all object files but retains the configuration options. + + $ make distclean + Performs clean and completely removes the build directory. + +------------------------------------------------------------------------------ diff --git a/src/metis/CMakeLists.txt b/src/metis/CMakeLists.txt new file mode 100644 index 0000000..186a320 --- /dev/null +++ b/src/metis/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.8) +project(METIS) + +set(GKLIB_PATH "GKlib" CACHE PATH "path to GKlib") +set(SHARED FALSE CACHE BOOL "build a shared library") + +if(MSVC) + set(METIS_INSTALL FALSE) +else() + set(METIS_INSTALL TRUE) +endif() + +# Configure libmetis library. +if(SHARED) + set(METIS_LIBRARY_TYPE SHARED) +else() + set(METIS_LIBRARY_TYPE STATIC) +endif(SHARED) + +include(${GKLIB_PATH}/GKlibSystem.cmake) +# Add include directories. +include_directories(${GKLIB_PATH}) +include_directories(include) +# Recursively look for CMakeLists.txt in subdirs. +add_subdirectory("include") +add_subdirectory("libmetis") +add_subdirectory("programs") diff --git a/src/metis/Changelog b/src/metis/Changelog new file mode 100644 index 0000000..2130880 --- /dev/null +++ b/src/metis/Changelog @@ -0,0 +1,286 @@ + +metis-5.1.0 +------------------------------------------------------------------------ +r13937 | karypis | 2013-03-29 23:08:21 -0500 (Fri, 29 Mar 2013) + +- Further extended the 2-hop coarsening scheme introduced in 5.0.2 for + for graphs with highly variable degree distribution (e.g., power-law). + This coarsening scheme is automatically used when the standard + 1-hop-based scheme leaves a large fraction of the vertices of the + graph unmatched. It leads to better quality partitionings, lower + memory utilization, and faster execution time. In principle, this + scheme will never be triggered for graphs/matrices appearing in + scientific computations derived from FE meshes. However, if you + notice that the quality of the solutions is significantly worse, + this 2-hop matching can be turned off by using the '-no2hop' command + line option and the associated options[] parameter (as described + in the manual). +- Fixed 0/1 numbering issue with mesh partitioning routines (flyspray + issue #109) + + +metis-5.0.3 +------------------------------------------------------------------------ +r13822 | karypis | 2013-03-11 14:40:11 -0500 (Mon, 11 Mar 2013) + +- Fixed the bug that was introduced in 5.x for creating nodal graphs + from meshes (flyspray issue #107). +- Changed the license to Apache Version 2. + + +metis-5.0.2 +------------------------------------------------------------------------ +r10974 | karypis | 2011-10-29 18:24:32 -0500 (Sat, 29 Oct 2011) + +- Fixed issue with high-degree vertices and mask-based compression. +- Fixed issue with wrong COARSENING_FRACTION. +- Modified coarsening schemes to better support non FE graphs. + + +metis-5.0.1 +------------------------------------------------------------------------ +r10709 | karypis | 2011-08-31 16:07:57 -0500 (Wed, 31 Aug 2011) + +- Fixed critical bug in the mesh partitioning routines. + + +metis-5.0 +------------------------------------------------------------------------ +r10667 | karypis | 2011-08-04 00:35:30 -0500 (Thu, 04 Aug 2011) + +- Updated/corrected error messages. +- Addressed some build issues. + + +metis-5.0rc3 +------------------------------------------------------------------------ +r10560 | karypis | 2011-07-13 08:19:10 -0500 (Wed, 13 Jul 2011) + +- Fixed various bugs that were identified by testers. +- Some minor performance and quality improvements. +- Addressed some build issues. + + +metis-5.0rc2 +------------------------------------------------------------------------ +r10496 | karypis | 2011-07-06 11:04:45 -0500 (Wed, 06 Jul 2011) + +- Various run-time and quality optimizations. +- Option error-checking. +- Signal-based heap cleanup on error. Metis API routines will not + return nicely and cleanup all memory that may have allocated. +- Reduced memory requirements. +- Fixed various bugs identified in rc1. +- Added back Fortran support in the form of alternate API names + (see libmetis/frename.h). +- Minor code changes to accommodate ParMetis 4.0. + + +metis-5.0rc1 +------------------------------------------------------------------------ +r10227 | karypis | 2011-06-13 23:35:05 -0500 (Mon, 13 Jun 2011) + +- A nearly complete re-write of Metis' code-based that changed expanded + the functionality of the command-line programs and API routines. +- Multi-constraint partitioning can be used in conjunction with + minimization of the total communication volume. +- All graph and mesh partitioning routines take as input the target + sizes of the partitions, which among others, allow them to compute + partitioning solutions that are well-suited for parallel architectures + with heterogeneous computing capabilities. +- When multi-constraint partitioning is used, the target sizes of the + partitions are specified on a per partition-constraint pair. +- The multilevel k-way partitioning algorithms can compute a + partitioning solution in which each partition is contiguous. +- All partitioning and ordering routines can compute multiple different + solutions and select the best as the final solution. +- The mesh partitioning and mesh-to-graph conversion routines can + operate on mixed element meshes. +- The command-line programs provide full access to the entire set of + capabilities provided by Metis' API. +- Re-written the memory management subsystem to reduce overall memory + requirements. + + + +metis-5.0pre2 +------------------------------------------------------------------------ +r1437 | karypis | 2007-04-07 23:16:16 -0500 (Sat, 07 Apr 2007) + +- Added installation instructions and change-logs. +- Tested 32bit & 64bit on 64bit architectures and passed tests. +- Tested 32bit on 32bit architectures and passed tests. +- strtoidx() addition for portable input file parsing +- Restructured the internal memory allocation schemes for graph and + refinement data. This should enhance portability and make the code + easier to maintain. +- Fixed some bad memory allocation calls (i.e., sizeof(x)/sizeof(idxtype). + However, there are tons of those and need to be corrected once and for + all by eliminating workspace and the associated mallocs. +- Added mprint/mscanf family of functions for portable formated I/O + of the idxtype datatype. The specifier for this datatype is %D. + All library routines use this function for printing. + The implementation of these routines is not very efficient, but + that should do for now (in principle these routines should not be + used unless debugging). +- Incorporated GKlib into METIS, which replaced many of its internal + functions. GKlib's malloc interface will enable graceful and clean + aborts (i.e., free all internally allocated memory) on fatal errors. + This will probably be available in the next pre-release. +- Fixed the problems associated with metis.h that were identified by + David (flyspray Issue #9). + + +METIS 4.0.2, 3/10/04 +------------------------------------------------------------------------------ +- Fixed a problem with weighted graphs and ometis.c + + +METIS 4.0.1, 11/29/98 +------------------------------------------------------------------------------ +This is mostly a bug-fix release + + - Fixed some bugs in the multi-constraint partitioning routines + - Fixed some bugs in the volume-minimization routines + + + +METIS 4.0.0, 9/20/98 +------------------------------------------------------------------------------ +METIS 4.0 contains a number of changes over the previous major release (ver +3.0.x). Most of these changes are concentrated on the graph and mesh +partitioning routines and they do not affect the sparse matrix re-ordering +routines. Here is a list of the major changes: + + Multi-Constraint Partitioning + ----------------------------- + METIS now includes partitioning routines that can be used to a partition + a graph in the presence of multiple balancing constraints. + + Minimizing the Total Communication Volume + ----------------------------------------- + METIS now includes partitioning routines whose objective is to minimize + the total communication volume (as opposed to minimizing the edge-cut). + + Minimizing the Maximum Connectivity of the Subdomains + ----------------------------------------------------- + The k-way partitioning routines in METIS can now directly minimize the number + of adjacent subdomains. For most graphs corresponding to finite element + meshes, METIS is able to significantly reduce the maximum (and total) number of + adjacent subdomains. + + + + +METIS 3.0.6, 1/28/98 +------------------------------------------------------------------------------- + - Fixed some problems when too many partitions were asked, and each partition + end up having 0 vertices + - Fixed some bugs in the I/O routines + - Added support for the g77 compiler under Linux + + +METIS 3.0.5, 12/22/97 +------------------------------------------------------------------------------- + - Fixed problems on 64-bit architectures (eg., -64 option on SGIs). + - Added some options in Makefile.in + + +METIS 3.0.4, 12/1/97 +------------------------------------------------------------------------------- + Fixed a memory leak in the ordering code. + + +METIS 3.0.3, 11/5/97 +------------------------------------------------------------------------------- + This is mostly a bug-fix release with just a few additions + + Added functionality + - Added support for quadrilateral elements. + - Added a routine METIS_EstimateMemory that estimates the amount of + memory that will be allocated by METIS. This is useful in determining + if a problem can run on your system. + - Added hooks to allow PARMETIS to use the orderings produced by METIS. + This is hidden from the user but it will be used in the next release + of PARMETIS. + + Bug-fixes + - Fixed a bug related to memory allocation. This should somewhat reduce the + overall memory used by METIS. + - Fixed some bugs in the 'graphchk' program in the case of weighted graphs. + - Removed some code corresponding to unused options. + - Fixed some minor bugs in the node-refinement code + + + +------------------------------------------------------------------------------- +METIS 3.0 contains a number of changes over METIS 2.0. +The major changes are the following: + + General Changes + --------------- + 1. Added code to directly partition finite element meshes. + + 2. Added code to convert finite element meshes into graphs so they + can be used by METIS. + + 1. The names, calling sequences, and options of the routines in + METISlib have been changed. + + 2. Better support has been added for Fortran programs. + + 3. Eliminated the 'metis' program. The only way to tune METIS's + behavior is to use METISlib. + + 4. Improved memory management. METIS should now only abort if truly + there is no more memory left in the system. + + + Graph Partitioning + ------------------ + 1. Added partitioning routines that can be used to compute a partition + with prescribed partition weights. For example, they can be used to + compute a 3-way partition such that partition 1 has 50% of the weight, + partition 2 has 20% of the way, and partition 3 has 30% of the weight. + + 2. Improved the speed of the k-way partitioning algorithm (kmetis). The + new code has better cache locality which dramatically improves the + speed for large graphs. A factor of 4 speedup can be obtained for + certain graphs. METIS can now partition a 4 million node graph + in well under a minute on a MIPS R10000. + + 3. Eliminated some of the options that were seldom used. + + + Fill-Reducing Orderings + ---------------------- + 1. Added a node based ordering code `onmetis' that greatly improves + ordering quality. + + 2. Improved the quality of the orderings produced by the original + edge-based ordering code (it is now called 'oemetis'). + + 3. METIS can now analyze the graph and try to compress together + nodes with identical sparsity pattern. For some problems, this + significantly reduces ordering time + + 4. METIS can now prune dense columns prior to ordering. This can be + helpful for LP matrices. + + + Mesh Partitioning + ----------------- + 1. METIS can now directly partition the element node array of finite + element meshes. It produces two partitioning vectors. One for the + elements and one for the nodes. METIS supports the following + elements: triangles, tetrahedra, hexahedra + + + Mesh-To-Graph Conversion Routines + --------------------------------- + 1. METIS now includes a number of mesh conversion functions that can + be used to create the dual and nodal graphs directly from the + element connectivity arrays. These are highly optimized routines. + + + diff --git a/src/metis/GKlib/BUILD.txt b/src/metis/GKlib/BUILD.txt new file mode 100644 index 0000000..cdb9987 --- /dev/null +++ b/src/metis/GKlib/BUILD.txt @@ -0,0 +1,25 @@ +Building GKlib requires CMake 2.8. Once you've installed CMake run + + $ make + +This will build the GKlib library in build//. Options can be tweaked by +running make config. For example, + + $ make config openmp=ON + $ make + +will build GKlib will OpenMP support if it is available. + +GKlib can be installed with + + $ make install + +and uninstalled with + + $ make uninstall + +You can choose the installation prefix with make config: + + $ make config prefix=~/local + +will cause GKlib to be install in the ~/local tree. diff --git a/src/metis/GKlib/CMakeLists.txt b/src/metis/GKlib/CMakeLists.txt new file mode 100644 index 0000000..67b600a --- /dev/null +++ b/src/metis/GKlib/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 2.8) +project(GKlib) + +get_filename_component(abs "." ABSOLUTE) +set(GKLIB_PATH ${abs}) +unset(abs) +include(GKlibSystem.cmake) + +include_directories(".") +add_library(GKlib STATIC ${GKlib_sources}) +if(UNIX) + target_link_libraries(GKlib m) +endif(UNIX) + +include_directories("test") +add_subdirectory("test") + +install(TARGETS GKlib + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib) +install(FILES ${GKlib_includes} DESTINATION include) diff --git a/src/metis/GKlib/GKlib.h b/src/metis/GKlib/GKlib.h new file mode 100644 index 0000000..492c90f --- /dev/null +++ b/src/metis/GKlib/GKlib.h @@ -0,0 +1,84 @@ +/* + * GKlib.h + * + * George's library of most frequently used routines + * + * $Id: GKlib.h 13005 2012-10-23 22:34:36Z karypis $ + * + */ + +#ifndef _GKLIB_H_ +#define _GKLIB_H_ 1 + +#define GKMSPACE + +#if defined(_MSC_VER) +#define __MSC__ +#endif +#if defined(__ICC) +#define __ICC__ +#endif + + +#include "gk_arch.h" /*!< This should be here, prior to the includes */ + + +/************************************************************************* +* Header file inclusion section +**************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__WITHPCRE__) + #include +#else + #if defined(USE_GKREGEX) + #include "gkregex.h" + #else + #include + #endif /* defined(USE_GKREGEX) */ +#endif /* defined(__WITHPCRE__) */ + + + +#if defined(__OPENMP__) +#include +#endif + + + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +#endif /* GKlib.h */ + + diff --git a/src/metis/GKlib/GKlibSystem.cmake b/src/metis/GKlib/GKlibSystem.cmake new file mode 100644 index 0000000..3fcc291 --- /dev/null +++ b/src/metis/GKlib/GKlibSystem.cmake @@ -0,0 +1,129 @@ +# Helper modules. +include(CheckFunctionExists) +include(CheckIncludeFile) + +# Setup options. +option(GDB "enable use of GDB" OFF) +option(ASSERT "turn asserts on" OFF) +option(ASSERT2 "additional assertions" OFF) +option(DEBUG "add debugging support" OFF) +option(GPROF "add gprof support" OFF) +option(OPENMP "enable OpenMP support" OFF) +option(PCRE "enable PCRE support" OFF) +option(GKREGEX "enable GKREGEX support" OFF) +option(GKRAND "enable GKRAND support" OFF) + +# Add compiler flags. +if(MSVC) + set(GKlib_COPTS "/Ox") + set(GKlib_COPTIONS "-DWIN32 -DMSC -D_CRT_SECURE_NO_DEPRECATE -DUSE_GKREGEX") +elseif(MINGW) + set(GKlib_COPTS "-DUSE_GKREGEX") +else() + set(GKlib_COPTS "-O3") + set(GKlib_COPTIONS "-DLINUX -D_FILE_OFFSET_BITS=64") +endif(MSVC) +if(CYGWIN) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -DCYGWIN") +endif(CYGWIN) +if(CMAKE_COMPILER_IS_GNUCC) +# GCC opts. + set(GKlib_COPTIONS "${GKlib_COPTIONS} -std=c99 -fno-strict-aliasing") + if(NOT MINGW) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -fPIC") + endif(NOT MINGW) +# GCC warnings. + set(GKlib_COPTIONS "${GKlib_COPTIONS} -Wall -pedantic -Wno-unused-but-set-variable -Wno-unused-variable -Wno-unknown-pragmas") +elseif(${CMAKE_C_COMPILER_ID} MATCHES "Sun") +# Sun insists on -xc99. + set(GKlib_COPTIONS "${GKlib_COPTIONS} -xc99") +endif(CMAKE_COMPILER_IS_GNUCC) + +# Find OpenMP if it is requested. +if(OPENMP) + include(FindOpenMP) + if(OPENMP_FOUND) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -D__OPENMP__ ${OpenMP_C_FLAGS}") + else() + message(WARNING "OpenMP was requested but support was not found") + endif(OPENMP_FOUND) +endif(OPENMP) + + +# Add various definitions. +if(GDB) + set(GKlib_COPTS "${GKlib_COPTS} -g") + set(GKlib_COPTIONS "${GKlib_COPTIONS} -Werror") +endif(GDB) + + +if(DEBUG) + set(GKlib_COPTS "-g") + set(GKlib_COPTIONS "${GKlib_COPTIONS} -DDEBUG") +endif(DEBUG) + +if(GPROF) + set(GKlib_COPTS "-pg") +endif(GPROF) + +if(NOT ASSERT) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -DNDEBUG") +endif(NOT ASSERT) + +if(NOT ASSERT2) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -DNDEBUG2") +endif(NOT ASSERT2) + + +# Add various options +if(PCRE) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -D__WITHPCRE__") +endif(PCRE) + +if(GKREGEX) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -DUSE_GKREGEX") +endif(GKREGEX) + +if(GKRAND) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -DUSE_GKRAND") +endif(GKRAND) + + +# Check for features. +check_include_file(execinfo.h HAVE_EXECINFO_H) +if(HAVE_EXECINFO_H) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -DHAVE_EXECINFO_H") +endif(HAVE_EXECINFO_H) + +check_function_exists(getline HAVE_GETLINE) +if(HAVE_GETLINE) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -DHAVE_GETLINE") +endif(HAVE_GETLINE) + + +# Custom check for TLS. +if(MSVC) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -D__thread=__declspec(thread)") +else() + # This if checks if that value is cached or not. + if("${HAVE_THREADLOCALSTORAGE}" MATCHES "^${HAVE_THREADLOCALSTORAGE}$") + try_compile(HAVE_THREADLOCALSTORAGE + ${CMAKE_BINARY_DIR} + ${GKLIB_PATH}/conf/check_thread_storage.c) + if(HAVE_THREADLOCALSTORAGE) + message(STATUS "checking for thread-local storage - found") + else() + message(STATUS "checking for thread-local storage - not found") + endif() + endif() + if(NOT HAVE_THREADLOCALSTORAGE) + set(GKlib_COPTIONS "${GKlib_COPTIONS} -D__thread=") + endif() +endif() + +# Finally set the official C flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GKlib_COPTIONS} ${GKlib_COPTS}") + +# Find GKlib sources. +file(GLOB GKlib_sources ${GKLIB_PATH}/*.c) +file(GLOB GKlib_includes ${GKLIB_PATH}/*.h) diff --git a/src/metis/GKlib/b64.c b/src/metis/GKlib/b64.c new file mode 100644 index 0000000..afacd68 --- /dev/null +++ b/src/metis/GKlib/b64.c @@ -0,0 +1,95 @@ +/*! +\file b64.c +\brief This file contains some simple 8bit-to-6bit encoding/deconding routines + +Most of these routines are outdated and should be converted using glibc's equivalent +routines. + +\date Started 2/22/05 +\author George +\version\verbatim $Id: b64.c 10711 2011-08-31 22:23:04Z karypis $ \endverbatim + +\verbatim +$Copyright$ +$License$ +\endverbatim + +*/ + + +#include "GKlib.h" + +#define B64OFFSET 48 /* This is the '0' number */ + + +/****************************************************************************** +* Encode 3 '8-bit' binary bytes as 4 '6-bit' characters +*******************************************************************************/ +void encodeblock(unsigned char *in, unsigned char *out) +{ + out[0] = (in[0] >> 2); + out[1] = (((in[0] & 0x03) << 4) | (in[1] >> 4)); + out[2] = (((in[1] & 0x0f) << 2) | (in[2] >> 6)); + out[3] = (in[2] & 0x3f); + + out[0] += B64OFFSET; + out[1] += B64OFFSET; + out[2] += B64OFFSET; + out[3] += B64OFFSET; + +// printf("%c %c %c %c %2x %2x %2x %2x %2x %2x %2x\n", out[0], out[1], out[2], out[3], out[0], out[1], out[2], out[3], in[0], in[1], in[2]); +} + +/****************************************************************************** +* Decode 4 '6-bit' characters into 3 '8-bit' binary bytes +*******************************************************************************/ +void decodeblock(unsigned char *in, unsigned char *out) +{ + in[0] -= B64OFFSET; + in[1] -= B64OFFSET; + in[2] -= B64OFFSET; + in[3] -= B64OFFSET; + + out[0] = (in[0] << 2 | in[1] >> 4); + out[1] = (in[1] << 4 | in[2] >> 2); + out[2] = (in[2] << 6 | in[3]); +} + + +/****************************************************************************** +* This function encodes an input array of bytes into a base64 encoding. Memory +* for the output array is assumed to have been allocated by the calling program +* and be sufficiently large. The output string is NULL terminated. +*******************************************************************************/ +void GKEncodeBase64(int nbytes, unsigned char *inbuffer, unsigned char *outbuffer) +{ + int i, j; + + if (nbytes%3 != 0) + gk_errexit(SIGERR, "GKEncodeBase64: Input buffer size should be a multiple of 3! (%d)\n", nbytes); + + for (j=0, i=0; i + + + +/*************************************************************************/ +/*! Use the templates to generate BLAS routines for the scalar data types */ +/*************************************************************************/ +GK_MKBLAS(gk_c, char, int) +GK_MKBLAS(gk_i, int, int) +GK_MKBLAS(gk_i32, int32_t, int32_t) +GK_MKBLAS(gk_i64, int64_t, int64_t) +GK_MKBLAS(gk_z, ssize_t, ssize_t) +GK_MKBLAS(gk_f, float, float) +GK_MKBLAS(gk_d, double, double) +GK_MKBLAS(gk_idx, gk_idx_t, gk_idx_t) + + + + diff --git a/src/metis/GKlib/conf/check_thread_storage.c b/src/metis/GKlib/conf/check_thread_storage.c new file mode 100644 index 0000000..e6e1e98 --- /dev/null +++ b/src/metis/GKlib/conf/check_thread_storage.c @@ -0,0 +1,5 @@ +extern __thread int x; + +int main(int argc, char **argv) { + return 0; +} diff --git a/src/metis/GKlib/csr.c b/src/metis/GKlib/csr.c new file mode 100644 index 0000000..a19d793 --- /dev/null +++ b/src/metis/GKlib/csr.c @@ -0,0 +1,2010 @@ +/*! + * \file + * + * \brief Various routines with dealing with CSR matrices + * + * \author George Karypis + * \version\verbatim $Id: csr.c 13437 2013-01-11 21:54:10Z karypis $ \endverbatim + */ + +#include + +#define OMPMINOPS 50000 + +/*************************************************************************/ +/*! Allocate memory for a CSR matrix and initializes it + \returns the allocated matrix. The various fields are set to NULL. +*/ +/**************************************************************************/ +gk_csr_t *gk_csr_Create() +{ + gk_csr_t *mat; + + mat = (gk_csr_t *)gk_malloc(sizeof(gk_csr_t), "gk_csr_Create: mat"); + + gk_csr_Init(mat); + + return mat; +} + + +/*************************************************************************/ +/*! Initializes the matrix + \param mat is the matrix to be initialized. +*/ +/*************************************************************************/ +void gk_csr_Init(gk_csr_t *mat) +{ + memset(mat, 0, sizeof(gk_csr_t)); + mat->nrows = mat->ncols = -1; +} + + +/*************************************************************************/ +/*! Frees all the memory allocated for matrix. + \param mat is the matrix to be freed. +*/ +/*************************************************************************/ +void gk_csr_Free(gk_csr_t **mat) +{ + if (*mat == NULL) + return; + gk_csr_FreeContents(*mat); + gk_free((void **)mat, LTERM); +} + + +/*************************************************************************/ +/*! Frees only the memory allocated for the matrix's different fields and + sets them to NULL. + \param mat is the matrix whose contents will be freed. +*/ +/*************************************************************************/ +void gk_csr_FreeContents(gk_csr_t *mat) +{ + gk_free((void *)&mat->rowptr, &mat->rowind, &mat->rowval, &mat->rowids, + &mat->colptr, &mat->colind, &mat->colval, &mat->colids, + &mat->rnorms, &mat->cnorms, &mat->rsums, &mat->csums, + &mat->rsizes, &mat->csizes, &mat->rvols, &mat->cvols, + &mat->rwgts, &mat->cwgts, + LTERM); +} + + +/*************************************************************************/ +/*! Returns a copy of a matrix. + \param mat is the matrix to be duplicated. + \returns the newly created copy of the matrix. +*/ +/**************************************************************************/ +gk_csr_t *gk_csr_Dup(gk_csr_t *mat) +{ + gk_csr_t *nmat; + + nmat = gk_csr_Create(); + + nmat->nrows = mat->nrows; + nmat->ncols = mat->ncols; + + /* copy the row structure */ + if (mat->rowptr) + nmat->rowptr = gk_zcopy(mat->nrows+1, mat->rowptr, + gk_zmalloc(mat->nrows+1, "gk_csr_Dup: rowptr")); + if (mat->rowids) + nmat->rowids = gk_icopy(mat->nrows, mat->rowids, + gk_imalloc(mat->nrows, "gk_csr_Dup: rowids")); + if (mat->rnorms) + nmat->rnorms = gk_fcopy(mat->nrows, mat->rnorms, + gk_fmalloc(mat->nrows, "gk_csr_Dup: rnorms")); + if (mat->rowind) + nmat->rowind = gk_icopy(mat->rowptr[mat->nrows], mat->rowind, + gk_imalloc(mat->rowptr[mat->nrows], "gk_csr_Dup: rowind")); + if (mat->rowval) + nmat->rowval = gk_fcopy(mat->rowptr[mat->nrows], mat->rowval, + gk_fmalloc(mat->rowptr[mat->nrows], "gk_csr_Dup: rowval")); + + /* copy the col structure */ + if (mat->colptr) + nmat->colptr = gk_zcopy(mat->ncols+1, mat->colptr, + gk_zmalloc(mat->ncols+1, "gk_csr_Dup: colptr")); + if (mat->colids) + nmat->colids = gk_icopy(mat->ncols, mat->colids, + gk_imalloc(mat->ncols, "gk_csr_Dup: colids")); + if (mat->cnorms) + nmat->cnorms = gk_fcopy(mat->ncols, mat->cnorms, + gk_fmalloc(mat->ncols, "gk_csr_Dup: cnorms")); + if (mat->colind) + nmat->colind = gk_icopy(mat->colptr[mat->ncols], mat->colind, + gk_imalloc(mat->colptr[mat->ncols], "gk_csr_Dup: colind")); + if (mat->colval) + nmat->colval = gk_fcopy(mat->colptr[mat->ncols], mat->colval, + gk_fmalloc(mat->colptr[mat->ncols], "gk_csr_Dup: colval")); + + return nmat; +} + + +/*************************************************************************/ +/*! Returns a submatrix containint a set of consecutive rows. + \param mat is the original matrix. + \param rstart is the starting row. + \param nrows is the number of rows from rstart to extract. + \returns the row structure of the newly created submatrix. +*/ +/**************************************************************************/ +gk_csr_t *gk_csr_ExtractSubmatrix(gk_csr_t *mat, int rstart, int nrows) +{ + ssize_t i; + gk_csr_t *nmat; + + if (rstart+nrows > mat->nrows) + return NULL; + + nmat = gk_csr_Create(); + + nmat->nrows = nrows; + nmat->ncols = mat->ncols; + + /* copy the row structure */ + if (mat->rowptr) + nmat->rowptr = gk_zcopy(nrows+1, mat->rowptr+rstart, + gk_zmalloc(nrows+1, "gk_csr_ExtractSubmatrix: rowptr")); + for (i=nrows; i>=0; i--) + nmat->rowptr[i] -= nmat->rowptr[0]; + ASSERT(nmat->rowptr[0] == 0); + + if (mat->rowids) + nmat->rowids = gk_icopy(nrows, mat->rowids+rstart, + gk_imalloc(nrows, "gk_csr_ExtractSubmatrix: rowids")); + if (mat->rnorms) + nmat->rnorms = gk_fcopy(nrows, mat->rnorms+rstart, + gk_fmalloc(nrows, "gk_csr_ExtractSubmatrix: rnorms")); + + if (mat->rsums) + nmat->rsums = gk_fcopy(nrows, mat->rsums+rstart, + gk_fmalloc(nrows, "gk_csr_ExtractSubmatrix: rsums")); + + ASSERT(nmat->rowptr[nrows] == mat->rowptr[rstart+nrows]-mat->rowptr[rstart]); + if (mat->rowind) + nmat->rowind = gk_icopy(mat->rowptr[rstart+nrows]-mat->rowptr[rstart], + mat->rowind+mat->rowptr[rstart], + gk_imalloc(mat->rowptr[rstart+nrows]-mat->rowptr[rstart], + "gk_csr_ExtractSubmatrix: rowind")); + if (mat->rowval) + nmat->rowval = gk_fcopy(mat->rowptr[rstart+nrows]-mat->rowptr[rstart], + mat->rowval+mat->rowptr[rstart], + gk_fmalloc(mat->rowptr[rstart+nrows]-mat->rowptr[rstart], + "gk_csr_ExtractSubmatrix: rowval")); + + return nmat; +} + + +/*************************************************************************/ +/*! Returns a submatrix containing a certain set of rows. + \param mat is the original matrix. + \param nrows is the number of rows to extract. + \param rind is the set of row numbers to extract. + \returns the row structure of the newly created submatrix. +*/ +/**************************************************************************/ +gk_csr_t *gk_csr_ExtractRows(gk_csr_t *mat, int nrows, int *rind) +{ + ssize_t i, ii, j, nnz; + gk_csr_t *nmat; + + nmat = gk_csr_Create(); + + nmat->nrows = nrows; + nmat->ncols = mat->ncols; + + for (nnz=0, i=0; irowptr[rind[i]+1]-mat->rowptr[rind[i]]; + + nmat->rowptr = gk_zmalloc(nmat->nrows+1, "gk_csr_ExtractPartition: rowptr"); + nmat->rowind = gk_imalloc(nnz, "gk_csr_ExtractPartition: rowind"); + nmat->rowval = gk_fmalloc(nnz, "gk_csr_ExtractPartition: rowval"); + + nmat->rowptr[0] = 0; + for (nnz=0, j=0, ii=0; iirowptr[i+1]-mat->rowptr[i], mat->rowind+mat->rowptr[i], nmat->rowind+nnz); + gk_fcopy(mat->rowptr[i+1]-mat->rowptr[i], mat->rowval+mat->rowptr[i], nmat->rowval+nnz); + nnz += mat->rowptr[i+1]-mat->rowptr[i]; + nmat->rowptr[++j] = nnz; + } + ASSERT(j == nmat->nrows); + + return nmat; +} + + +/*************************************************************************/ +/*! Returns a submatrix corresponding to a specified partitioning of rows. + \param mat is the original matrix. + \param part is the partitioning vector of the rows. + \param pid is the partition ID that will be extracted. + \returns the row structure of the newly created submatrix. +*/ +/**************************************************************************/ +gk_csr_t *gk_csr_ExtractPartition(gk_csr_t *mat, int *part, int pid) +{ + ssize_t i, j, nnz; + gk_csr_t *nmat; + + nmat = gk_csr_Create(); + + nmat->nrows = 0; + nmat->ncols = mat->ncols; + + for (nnz=0, i=0; inrows; i++) { + if (part[i] == pid) { + nmat->nrows++; + nnz += mat->rowptr[i+1]-mat->rowptr[i]; + } + } + + nmat->rowptr = gk_zmalloc(nmat->nrows+1, "gk_csr_ExtractPartition: rowptr"); + nmat->rowind = gk_imalloc(nnz, "gk_csr_ExtractPartition: rowind"); + nmat->rowval = gk_fmalloc(nnz, "gk_csr_ExtractPartition: rowval"); + + nmat->rowptr[0] = 0; + for (nnz=0, j=0, i=0; inrows; i++) { + if (part[i] == pid) { + gk_icopy(mat->rowptr[i+1]-mat->rowptr[i], mat->rowind+mat->rowptr[i], nmat->rowind+nnz); + gk_fcopy(mat->rowptr[i+1]-mat->rowptr[i], mat->rowval+mat->rowptr[i], nmat->rowval+nnz); + nnz += mat->rowptr[i+1]-mat->rowptr[i]; + nmat->rowptr[++j] = nnz; + } + } + ASSERT(j == nmat->nrows); + + return nmat; +} + + +/*************************************************************************/ +/*! Splits the matrix into multiple sub-matrices based on the provided + color array. + \param mat is the original matrix. + \param color is an array of size equal to the number of non-zeros + in the matrix (row-wise structure). The matrix is split into + as many parts as the number of colors. For meaningfull results, + the colors should be numbered consecutively starting from 0. + \returns an array of matrices for each supplied color number. +*/ +/**************************************************************************/ +gk_csr_t **gk_csr_Split(gk_csr_t *mat, int *color) +{ + ssize_t i, j; + int nrows, ncolors; + ssize_t *rowptr; + int *rowind; + float *rowval; + gk_csr_t **smats; + + nrows = mat->nrows; + rowptr = mat->rowptr; + rowind = mat->rowind; + rowval = mat->rowval; + + ncolors = gk_imax(rowptr[nrows], color)+1; + + smats = (gk_csr_t **)gk_malloc(sizeof(gk_csr_t *)*ncolors, "gk_csr_Split: smats"); + for (i=0; inrows = mat->nrows; + smats[i]->ncols = mat->ncols; + smats[i]->rowptr = gk_zsmalloc(nrows+1, 0, "gk_csr_Split: smats[i]->rowptr"); + } + + for (i=0; irowptr[i]++; + } + for (i=0; irowptr); + + for (i=0; irowind = gk_imalloc(smats[i]->rowptr[nrows], "gk_csr_Split: smats[i]->rowind"); + smats[i]->rowval = gk_fmalloc(smats[i]->rowptr[nrows], "gk_csr_Split: smats[i]->rowval"); + } + + for (i=0; irowind[smats[color[j]]->rowptr[i]] = rowind[j]; + smats[color[j]]->rowval[smats[color[j]]->rowptr[i]] = rowval[j]; + smats[color[j]]->rowptr[i]++; + } + } + + for (i=0; irowptr); + + return smats; +} + + +/**************************************************************************/ +/*! Reads a CSR matrix from the supplied file and stores it the matrix's + forward structure. + \param filename is the file that stores the data. + \param format is either GK_CSR_FMT_METIS, GK_CSR_FMT_CLUTO, + GK_CSR_FMT_CSR, GK_CSR_FMT_BINROW, GK_CSR_FMT_BINCOL + specifying the type of the input format. + The GK_CSR_FMT_CSR does not contain a header + line, whereas the GK_CSR_FMT_BINROW is a binary format written + by gk_csr_Write() using the same format specifier. + \param readvals is either 1 or 0, indicating if the CSR file contains + values or it does not. It only applies when GK_CSR_FMT_CSR is + used. + \param numbering is either 1 or 0, indicating if the numbering of the + indices start from 1 or 0, respectively. If they start from 1, + they are automatically decreamented during input so that they + will start from 0. It only applies when GK_CSR_FMT_CSR is + used. + \returns the matrix that was read. +*/ +/**************************************************************************/ +gk_csr_t *gk_csr_Read(char *filename, int format, int readvals, int numbering) +{ + ssize_t i, k, l; + size_t nfields, nrows, ncols, nnz, fmt, ncon; + size_t lnlen; + ssize_t *rowptr; + int *rowind, ival; + float *rowval=NULL, fval; + int readsizes, readwgts; + char *line=NULL, *head, *tail, fmtstr[256]; + FILE *fpin; + gk_csr_t *mat=NULL; + + + if (!gk_fexists(filename)) + gk_errexit(SIGERR, "File %s does not exist!\n", filename); + + if (format == GK_CSR_FMT_BINROW) { + mat = gk_csr_Create(); + + fpin = gk_fopen(filename, "rb", "gk_csr_Read: fpin"); + if (fread(&(mat->nrows), sizeof(int32_t), 1, fpin) != 1) + gk_errexit(SIGERR, "Failed to read the nrows from file %s!\n", filename); + if (fread(&(mat->ncols), sizeof(int32_t), 1, fpin) != 1) + gk_errexit(SIGERR, "Failed to read the ncols from file %s!\n", filename); + mat->rowptr = gk_zmalloc(mat->nrows+1, "gk_csr_Read: rowptr"); + if (fread(mat->rowptr, sizeof(ssize_t), mat->nrows+1, fpin) != mat->nrows+1) + gk_errexit(SIGERR, "Failed to read the rowptr from file %s!\n", filename); + mat->rowind = gk_imalloc(mat->rowptr[mat->nrows], "gk_csr_Read: rowind"); + if (fread(mat->rowind, sizeof(int32_t), mat->rowptr[mat->nrows], fpin) != mat->rowptr[mat->nrows]) + gk_errexit(SIGERR, "Failed to read the rowind from file %s!\n", filename); + if (readvals == 1) { + mat->rowval = gk_fmalloc(mat->rowptr[mat->nrows], "gk_csr_Read: rowval"); + if (fread(mat->rowval, sizeof(float), mat->rowptr[mat->nrows], fpin) != mat->rowptr[mat->nrows]) + gk_errexit(SIGERR, "Failed to read the rowval from file %s!\n", filename); + } + + gk_fclose(fpin); + return mat; + } + + if (format == GK_CSR_FMT_BINCOL) { + mat = gk_csr_Create(); + + fpin = gk_fopen(filename, "rb", "gk_csr_Read: fpin"); + if (fread(&(mat->nrows), sizeof(int32_t), 1, fpin) != 1) + gk_errexit(SIGERR, "Failed to read the nrows from file %s!\n", filename); + if (fread(&(mat->ncols), sizeof(int32_t), 1, fpin) != 1) + gk_errexit(SIGERR, "Failed to read the ncols from file %s!\n", filename); + mat->colptr = gk_zmalloc(mat->ncols+1, "gk_csr_Read: colptr"); + if (fread(mat->colptr, sizeof(ssize_t), mat->ncols+1, fpin) != mat->ncols+1) + gk_errexit(SIGERR, "Failed to read the colptr from file %s!\n", filename); + mat->colind = gk_imalloc(mat->colptr[mat->ncols], "gk_csr_Read: colind"); + if (fread(mat->colind, sizeof(int32_t), mat->colptr[mat->ncols], fpin) != mat->colptr[mat->ncols]) + gk_errexit(SIGERR, "Failed to read the colind from file %s!\n", filename); + if (readvals) { + mat->colval = gk_fmalloc(mat->colptr[mat->ncols], "gk_csr_Read: colval"); + if (fread(mat->colval, sizeof(float), mat->colptr[mat->ncols], fpin) != mat->colptr[mat->ncols]) + gk_errexit(SIGERR, "Failed to read the colval from file %s!\n", filename); + } + + gk_fclose(fpin); + return mat; + } + + + if (format == GK_CSR_FMT_CLUTO) { + fpin = gk_fopen(filename, "r", "gk_csr_Read: fpin"); + do { + if (gk_getline(&line, &lnlen, fpin) <= 0) + gk_errexit(SIGERR, "Premature end of input file: file:%s\n", filename); + } while (line[0] == '%'); + + if (sscanf(line, "%zu %zu %zu", &nrows, &ncols, &nnz) != 3) + gk_errexit(SIGERR, "Header line must contain 3 integers.\n"); + + readsizes = 0; + readwgts = 0; + readvals = 1; + numbering = 1; + } + else if (format == GK_CSR_FMT_METIS) { + fpin = gk_fopen(filename, "r", "gk_csr_Read: fpin"); + do { + if (gk_getline(&line, &lnlen, fpin) <= 0) + gk_errexit(SIGERR, "Premature end of input file: file:%s\n", filename); + } while (line[0] == '%'); + + fmt = ncon = 0; + nfields = sscanf(line, "%zu %zu %zu %zu", &nrows, &nnz, &fmt, &ncon); + if (nfields < 2) + gk_errexit(SIGERR, "Header line must contain at least 2 integers (#vtxs and #edges).\n"); + + ncols = nrows; + nnz *= 2; + + if (fmt > 111) + gk_errexit(SIGERR, "Cannot read this type of file format [fmt=%zu]!\n", fmt); + + sprintf(fmtstr, "%03zu", fmt%1000); + readsizes = (fmtstr[0] == '1'); + readwgts = (fmtstr[1] == '1'); + readvals = (fmtstr[2] == '1'); + numbering = 1; + ncon = (ncon == 0 ? 1 : ncon); + } + else { + readsizes = 0; + readwgts = 0; + + gk_getfilestats(filename, &nrows, &nnz, NULL, NULL); + + if (readvals == 1 && nnz%2 == 1) + gk_errexit(SIGERR, "Error: The number of numbers (%zd %d) in the input file is not even.\n", nnz, readvals); + if (readvals == 1) + nnz = nnz/2; + fpin = gk_fopen(filename, "r", "gk_csr_Read: fpin"); + } + + mat = gk_csr_Create(); + + mat->nrows = nrows; + + rowptr = mat->rowptr = gk_zmalloc(nrows+1, "gk_csr_Read: rowptr"); + rowind = mat->rowind = gk_imalloc(nnz, "gk_csr_Read: rowind"); + if (readvals != 2) + rowval = mat->rowval = gk_fsmalloc(nnz, 1.0, "gk_csr_Read: rowval"); + + if (readsizes) + mat->rsizes = gk_fsmalloc(nrows, 0.0, "gk_csr_Read: rsizes"); + + if (readwgts) + mat->rwgts = gk_fsmalloc(nrows*ncon, 0.0, "gk_csr_Read: rwgts"); + + /*---------------------------------------------------------------------- + * Read the sparse matrix file + *---------------------------------------------------------------------*/ + numbering = (numbering ? - 1 : 0); + for (ncols=0, rowptr[0]=0, k=0, i=0; irsizes[i] = (float)strtod(head, &tail); +#else + mat->rsizes[i] = strtof(head, &tail); +#endif + if (tail == head) + gk_errexit(SIGERR, "The line for vertex %zd does not have size information\n", i+1); + if (mat->rsizes[i] < 0) + errexit("The size for vertex %zd must be >= 0\n", i+1); + head = tail; + } + + /* Read vertex weights */ + if (readwgts) { + for (l=0; lrwgts[i*ncon+l] = (float)strtod(head, &tail); +#else + mat->rwgts[i*ncon+l] = strtof(head, &tail); +#endif + if (tail == head) + errexit("The line for vertex %zd does not have enough weights " + "for the %d constraints.\n", i+1, ncon); + if (mat->rwgts[i*ncon+l] < 0) + errexit("The weight vertex %zd and constraint %zd must be >= 0\n", i+1, l); + head = tail; + } + } + + + /* Read the rest of the row */ + while (1) { + ival = (int)strtol(head, &tail, 0); + if (tail == head) + break; + head = tail; + + if ((rowind[k] = ival + numbering) < 0) + gk_errexit(SIGERR, "Error: Invalid column number %d at row %zd.\n", ival, i); + + ncols = gk_max(rowind[k], ncols); + + if (readvals == 1) { +#ifdef __MSC__ + fval = (float)strtod(head, &tail); +#else + fval = strtof(head, &tail); +#endif + if (tail == head) + gk_errexit(SIGERR, "Value could not be found for column! Row:%zd, NNZ:%zd\n", i, k); + head = tail; + + rowval[k] = fval; + } + k++; + } + rowptr[i+1] = k; + } + + if (format == GK_CSR_FMT_METIS) { + ASSERT(ncols+1 == mat->nrows); + mat->ncols = mat->nrows; + } + else { + mat->ncols = ncols+1; + } + + if (k != nnz) + gk_errexit(SIGERR, "gk_csr_Read: Something wrong with the number of nonzeros in " + "the input file. NNZ=%zd, ActualNNZ=%zd.\n", nnz, k); + + gk_fclose(fpin); + + gk_free((void **)&line, LTERM); + + return mat; +} + + +/**************************************************************************/ +/*! Writes the row-based structure of a matrix into a file. + \param mat is the matrix to be written, + \param filename is the name of the output file. + \param format is one of: GK_CSR_FMT_CLUTO, GK_CSR_FMT_CSR, + GK_CSR_FMT_BINROW, GK_CSR_FMT_BINCOL. + \param writevals is either 1 or 0 indicating if the values will be + written or not. This is only applicable when GK_CSR_FMT_CSR + is used. + \param numbering is either 1 or 0 indicating if the internal 0-based + numbering will be shifted by one or not during output. This + is only applicable when GK_CSR_FMT_CSR is used. +*/ +/**************************************************************************/ +void gk_csr_Write(gk_csr_t *mat, char *filename, int format, int writevals, int numbering) +{ + ssize_t i, j; + FILE *fpout; + + if (format == GK_CSR_FMT_BINROW) { + if (filename == NULL) + gk_errexit(SIGERR, "The filename parameter cannot be NULL.\n"); + fpout = gk_fopen(filename, "wb", "gk_csr_Write: fpout"); + + fwrite(&(mat->nrows), sizeof(int32_t), 1, fpout); + fwrite(&(mat->ncols), sizeof(int32_t), 1, fpout); + fwrite(mat->rowptr, sizeof(ssize_t), mat->nrows+1, fpout); + fwrite(mat->rowind, sizeof(int32_t), mat->rowptr[mat->nrows], fpout); + if (writevals) + fwrite(mat->rowval, sizeof(float), mat->rowptr[mat->nrows], fpout); + + gk_fclose(fpout); + return; + } + + if (format == GK_CSR_FMT_BINCOL) { + if (filename == NULL) + gk_errexit(SIGERR, "The filename parameter cannot be NULL.\n"); + fpout = gk_fopen(filename, "wb", "gk_csr_Write: fpout"); + + fwrite(&(mat->nrows), sizeof(int32_t), 1, fpout); + fwrite(&(mat->ncols), sizeof(int32_t), 1, fpout); + fwrite(mat->colptr, sizeof(ssize_t), mat->ncols+1, fpout); + fwrite(mat->colind, sizeof(int32_t), mat->colptr[mat->ncols], fpout); + if (writevals) + fwrite(mat->colval, sizeof(float), mat->colptr[mat->ncols], fpout); + + gk_fclose(fpout); + return; + } + + if (filename) + fpout = gk_fopen(filename, "w", "gk_csr_Write: fpout"); + else + fpout = stdout; + + if (format == GK_CSR_FMT_CLUTO) { + fprintf(fpout, "%d %d %zd\n", mat->nrows, mat->ncols, mat->rowptr[mat->nrows]); + writevals = 1; + numbering = 1; + } + + for (i=0; inrows; i++) { + for (j=mat->rowptr[i]; jrowptr[i+1]; j++) { + fprintf(fpout, " %d", mat->rowind[j]+(numbering ? 1 : 0)); + if (writevals) + fprintf(fpout, " %f", mat->rowval[j]); + } + fprintf(fpout, "\n"); + } + if (filename) + gk_fclose(fpout); +} + + +/*************************************************************************/ +/*! Prunes certain rows/columns of the matrix. The prunning takes place + by analyzing the row structure of the matrix. The prunning takes place + by removing rows/columns but it does not affect the numbering of the + remaining rows/columns. + + \param mat the matrix to be prunned, + \param what indicates if the rows (GK_CSR_ROW) or the columns (GK_CSR_COL) + of the matrix will be prunned, + \param minf is the minimum number of rows (columns) that a column (row) must + be present in order to be kept, + \param maxf is the maximum number of rows (columns) that a column (row) must + be present at in order to be kept. + \returns the prunned matrix consisting only of its row-based structure. + The input matrix is not modified. +*/ +/**************************************************************************/ +gk_csr_t *gk_csr_Prune(gk_csr_t *mat, int what, int minf, int maxf) +{ + ssize_t i, j, nnz; + int nrows, ncols; + ssize_t *rowptr, *nrowptr; + int *rowind, *nrowind, *collen; + float *rowval, *nrowval; + gk_csr_t *nmat; + + nmat = gk_csr_Create(); + + nrows = nmat->nrows = mat->nrows; + ncols = nmat->ncols = mat->ncols; + + rowptr = mat->rowptr; + rowind = mat->rowind; + rowval = mat->rowval; + + nrowptr = nmat->rowptr = gk_zmalloc(nrows+1, "gk_csr_Prune: nrowptr"); + nrowind = nmat->rowind = gk_imalloc(rowptr[nrows], "gk_csr_Prune: nrowind"); + nrowval = nmat->rowval = gk_fmalloc(rowptr[nrows], "gk_csr_Prune: nrowval"); + + + switch (what) { + case GK_CSR_COL: + collen = gk_ismalloc(ncols, 0, "gk_csr_Prune: collen"); + + for (i=0; i= minf && collen[i] <= maxf ? 1 : 0); + + nrowptr[0] = 0; + for (nnz=0, i=0; i= minf && rowptr[i+1]-rowptr[i] <= maxf) { + for (j=rowptr[i]; jnrows = mat->nrows; + ncols = nmat->ncols = mat->ncols; + + rowptr = mat->rowptr; + rowind = mat->rowind; + rowval = mat->rowval; + colptr = mat->colptr; + colind = mat->colind; + colval = mat->colval; + + nrowptr = nmat->rowptr = gk_zmalloc(nrows+1, "gk_csr_LowFilter: nrowptr"); + nrowind = nmat->rowind = gk_imalloc(rowptr[nrows], "gk_csr_LowFilter: nrowind"); + nrowval = nmat->rowval = gk_fmalloc(rowptr[nrows], "gk_csr_LowFilter: nrowval"); + + + switch (what) { + case GK_CSR_COL: + if (mat->colptr == NULL) + gk_errexit(SIGERR, "Cannot filter columns when column-based structure has not been created.\n"); + + gk_zcopy(nrows+1, rowptr, nrowptr); + + for (i=0; irowptr == NULL) + gk_errexit(SIGERR, "Cannot filter rows when row-based structure has not been created.\n"); + + for (i=0; inrows = mat->nrows; + ncols = nmat->ncols = mat->ncols; + + rowptr = mat->rowptr; + rowind = mat->rowind; + rowval = mat->rowval; + colptr = mat->colptr; + colind = mat->colind; + colval = mat->colval; + + nrowptr = nmat->rowptr = gk_zmalloc(nrows+1, "gk_csr_LowFilter: nrowptr"); + nrowind = nmat->rowind = gk_imalloc(rowptr[nrows], "gk_csr_LowFilter: nrowind"); + nrowval = nmat->rowval = gk_fmalloc(rowptr[nrows], "gk_csr_LowFilter: nrowval"); + + + switch (what) { + case GK_CSR_COL: + if (mat->colptr == NULL) + gk_errexit(SIGERR, "Cannot filter columns when column-based structure has not been created.\n"); + + cand = gk_fkvmalloc(nrows, "gk_csr_LowFilter: cand"); + + gk_zcopy(nrows+1, rowptr, nrowptr); + for (i=0; irowptr == NULL) + gk_errexit(SIGERR, "Cannot filter rows when row-based structure has not been created.\n"); + + cand = gk_fkvmalloc(ncols, "gk_csr_LowFilter: cand"); + + nrowptr[0] = 0; + for (nnz=0, i=0; inrows = mat->nrows; + nmat->ncols = mat->ncols; + + nrows = mat->nrows; + rowptr = mat->rowptr; + rowind = mat->rowind; + rowval = mat->rowval; + + nrowptr = nmat->rowptr = gk_zmalloc(nrows+1, "gk_csr_ZScoreFilter: nrowptr"); + nrowind = nmat->rowind = gk_imalloc(rowptr[nrows], "gk_csr_ZScoreFilter: nrowind"); + nrowval = nmat->rowval = gk_fmalloc(rowptr[nrows], "gk_csr_ZScoreFilter: nrowval"); + + + switch (what) { + case GK_CSR_COL: + gk_errexit(SIGERR, "This has not been implemented yet.\n"); + break; + + case GK_CSR_ROW: + if (mat->rowptr == NULL) + gk_errexit(SIGERR, "Cannot filter rows when row-based structure has not been created.\n"); + + nrowptr[0] = 0; + for (nnz=0, i=0; i avgwgt) { + nrowind[nnz] = rowind[j]; + nrowval[nnz] = rowval[j]; + nnz++; + } + } + nrowptr[i+1] = nnz; + } + break; + + default: + gk_csr_Free(&nmat); + gk_errexit(SIGERR, "Unknown prunning type of %d\n", what); + return NULL; + } + + return nmat; +} + + +/*************************************************************************/ +/*! Compacts the column-space of the matrix by removing empty columns. + As a result of the compaction, the column numbers are renumbered. + The compaction operation is done in place and only affects the row-based + representation of the matrix. + The new columns are ordered in decreasing frequency. + + \param mat the matrix whose empty columns will be removed. +*/ +/**************************************************************************/ +void gk_csr_CompactColumns(gk_csr_t *mat) +{ + ssize_t i; + int nrows, ncols, nncols; + ssize_t *rowptr; + int *rowind, *colmap; + gk_ikv_t *clens; + + nrows = mat->nrows; + ncols = mat->ncols; + rowptr = mat->rowptr; + rowind = mat->rowind; + + colmap = gk_imalloc(ncols, "gk_csr_CompactColumns: colmap"); + + clens = gk_ikvmalloc(ncols, "gk_csr_CompactColumns: clens"); + for (i=0; i 0) + colmap[clens[i].val] = nncols++; + else + break; + } + + for (i=0; incols = nncols; + + gk_free((void **)&colmap, &clens, LTERM); +} + + +/*************************************************************************/ +/*! Sorts the indices in increasing order + \param mat the matrix itself, + \param what is either GK_CSR_ROW or GK_CSR_COL indicating which set of + indices to sort. +*/ +/**************************************************************************/ +void gk_csr_SortIndices(gk_csr_t *mat, int what) +{ + int n, nn=0; + ssize_t *ptr; + int *ind; + float *val; + + switch (what) { + case GK_CSR_ROW: + if (!mat->rowptr) + gk_errexit(SIGERR, "Row-based view of the matrix does not exists.\n"); + + n = mat->nrows; + ptr = mat->rowptr; + ind = mat->rowind; + val = mat->rowval; + break; + + case GK_CSR_COL: + if (!mat->colptr) + gk_errexit(SIGERR, "Column-based view of the matrix does not exists.\n"); + + n = mat->ncols; + ptr = mat->colptr; + ind = mat->colind; + val = mat->colval; + break; + + default: + gk_errexit(SIGERR, "Invalid index type of %d.\n", what); + return; + } + + #pragma omp parallel if (n > 100) + { + ssize_t i, j, k; + gk_ikv_t *cand; + float *tval; + + #pragma omp single + for (i=0; i ptr[i] && ind[j] < ind[j-1]) + k = 1; /* an inversion */ + cand[j-ptr[i]].val = j-ptr[i]; + cand[j-ptr[i]].key = ind[j]; + tval[j-ptr[i]] = val[j]; + } + if (k) { + gk_ikvsorti(ptr[i+1]-ptr[i], cand); + for (j=ptr[i]; jnrows; + fptr = mat->rowptr; + find = mat->rowind; + fval = mat->rowval; + + if (mat->colptr) gk_free((void **)&mat->colptr, LTERM); + if (mat->colind) gk_free((void **)&mat->colind, LTERM); + if (mat->colval) gk_free((void **)&mat->colval, LTERM); + + nr = mat->ncols; + rptr = mat->colptr = gk_zsmalloc(nr+1, 0, "gk_csr_CreateIndex: rptr"); + rind = mat->colind = gk_imalloc(fptr[nf], "gk_csr_CreateIndex: rind"); + rval = mat->colval = (fval ? gk_fmalloc(fptr[nf], "gk_csr_CreateIndex: rval") : NULL); + break; + case GK_CSR_ROW: + nf = mat->ncols; + fptr = mat->colptr; + find = mat->colind; + fval = mat->colval; + + if (mat->rowptr) gk_free((void **)&mat->rowptr, LTERM); + if (mat->rowind) gk_free((void **)&mat->rowind, LTERM); + if (mat->rowval) gk_free((void **)&mat->rowval, LTERM); + + nr = mat->nrows; + rptr = mat->rowptr = gk_zsmalloc(nr+1, 0, "gk_csr_CreateIndex: rptr"); + rind = mat->rowind = gk_imalloc(fptr[nf], "gk_csr_CreateIndex: rind"); + rval = mat->rowval = (fval ? gk_fmalloc(fptr[nf], "gk_csr_CreateIndex: rval") : NULL); + break; + default: + gk_errexit(SIGERR, "Invalid index type of %d.\n", what); + return; + } + + + for (i=0; i 6*nr) { + for (i=0; irowval) { + n = mat->nrows; + ptr = mat->rowptr; + val = mat->rowval; + + #pragma omp parallel if (ptr[n] > OMPMINOPS) + { + #pragma omp for private(j,sum) schedule(static) + for (i=0; i 0 */ + } + if (sum > 0) { + if (norm == 2) + sum=1.0/sqrt(sum); + else if (norm == 1) + sum=1.0/sum; + for (j=ptr[i]; jcolval) { + n = mat->ncols; + ptr = mat->colptr; + val = mat->colval; + + #pragma omp parallel if (ptr[n] > OMPMINOPS) + { + #pragma omp for private(j,sum) schedule(static) + for (i=0; i 0) { + if (norm == 2) + sum=1.0/sqrt(sum); + else if (norm == 1) + sum=1.0/sum; + for (j=ptr[i]; jnrows; + rowptr = mat->rowptr; + rowind = mat->rowind; + rowval = mat->rowval; + + switch (type) { + case GK_CSR_MAXTF: /* TF' = .5 + .5*TF/MAX(TF) */ + #pragma omp parallel if (rowptr[nrows] > OMPMINOPS) + { + #pragma omp for private(j, maxtf) schedule(static) + for (i=0; i OMPMINOPS) + { + #pragma omp for private(j, maxtf) schedule(static) + for (i=0; i OMPMINOPS) + { + #pragma omp for private(j) schedule(static) + for (i=0; i OMPMINOPS) + { + #pragma omp for private(j) schedule(static) + for (i=0; i OMPMINOPS) + { + #pragma omp for private(j) schedule(static) + for (i=0; i OMPMINOPS) + { + #pragma omp for private(j) schedule(static) + for (i=0; i OMPMINOPS) + { + #pragma omp for private(j) schedule(static) + for (i=0; i OMPMINOPS) + { + double logscale = 1.0/log(2.0); + #pragma omp for schedule(static,32) + for (i=0; i0.0 ? log(rowval[i]) : -log(-rowval[i]))*logscale; + } +#ifdef XXX + #pragma omp for private(j) schedule(static) + for (i=0; i0.0 ? log(rowval[j]) : -log(-rowval[j]))*logscale; + //rowval[j] = 1+sign(rowval[j], log(fabs(rowval[j]))*logscale); + } + } +#endif + } + break; + + case GK_CSR_IDF: /* TF' = TF*IDF */ + ncols = mat->ncols; + cscale = gk_fmalloc(ncols, "gk_csr_Scale: cscale"); + collen = gk_ismalloc(ncols, 0, "gk_csr_Scale: collen"); + + for (i=0; i OMPMINOPS) + { + #pragma omp for schedule(static) + for (i=0; i 0 ? log(1.0*nrows/collen[i]) : 0.0); + } + + #pragma omp parallel if (rowptr[nrows] > OMPMINOPS) + { + #pragma omp for private(j) schedule(static) + for (i=0; incols; + cscale = gk_fmalloc(ncols, "gk_csr_Scale: cscale"); + collen = gk_ismalloc(ncols, 0, "gk_csr_Scale: collen"); + + for (i=0; i OMPMINOPS) + { + #pragma omp for schedule(static) reduction(+:nnzcols) + for (i=0; i 0 ? 1 : 0); + + bgfreq = gk_max(10, (ssize_t)(.5*rowptr[nrows]/nnzcols)); + printf("nnz: %zd, nnzcols: %d, bgfreq: %d\n", rowptr[nrows], nnzcols, bgfreq); + + #pragma omp for schedule(static) + for (i=0; i 0 ? log(1.0*(nrows+2*bgfreq)/(bgfreq+collen[i])) : 0.0); + } + + #pragma omp parallel if (rowptr[nrows] > OMPMINOPS) + { + #pragma omp for private(j) schedule(static) + for (i=0; inrows; + ptr = mat->rowptr; + val = mat->rowval; + + if (mat->rsums) + gk_free((void **)&mat->rsums, LTERM); + + sums = mat->rsums = gk_fsmalloc(n, 0, "gk_csr_ComputeSums: sums"); + break; + case GK_CSR_COL: + n = mat->ncols; + ptr = mat->colptr; + val = mat->colval; + + if (mat->csums) + gk_free((void **)&mat->csums, LTERM); + + sums = mat->csums = gk_fsmalloc(n, 0, "gk_csr_ComputeSums: sums"); + break; + default: + gk_errexit(SIGERR, "Invalid sum type of %d.\n", what); + return; + } + + #pragma omp parallel for if (ptr[n] > OMPMINOPS) schedule(static) + for (i=0; inrows; + ptr = mat->rowptr; + val = mat->rowval; + + if (mat->rnorms) gk_free((void **)&mat->rnorms, LTERM); + + norms = mat->rnorms = gk_fsmalloc(n, 0, "gk_csr_ComputeSums: norms"); + break; + case GK_CSR_COL: + n = mat->ncols; + ptr = mat->colptr; + val = mat->colval; + + if (mat->cnorms) gk_free((void **)&mat->cnorms, LTERM); + + norms = mat->cnorms = gk_fsmalloc(n, 0, "gk_csr_ComputeSums: norms"); + break; + default: + gk_errexit(SIGERR, "Invalid norm type of %d.\n", what); + return; + } + + #pragma omp parallel for if (ptr[n] > OMPMINOPS) schedule(static) + for (i=0; irowptr) + gk_errexit(SIGERR, "Row-based view of the matrix does not exists.\n"); + nind1 = mat->rowptr[i1+1]-mat->rowptr[i1]; + nind2 = mat->rowptr[i2+1]-mat->rowptr[i2]; + ind1 = mat->rowind + mat->rowptr[i1]; + ind2 = mat->rowind + mat->rowptr[i2]; + val1 = mat->rowval + mat->rowptr[i1]; + val2 = mat->rowval + mat->rowptr[i2]; + break; + + case GK_CSR_COL: + if (!mat->colptr) + gk_errexit(SIGERR, "Column-based view of the matrix does not exists.\n"); + nind1 = mat->colptr[i1+1]-mat->colptr[i1]; + nind2 = mat->colptr[i2+1]-mat->colptr[i2]; + ind1 = mat->colind + mat->colptr[i1]; + ind2 = mat->colind + mat->colptr[i2]; + val1 = mat->colval + mat->colptr[i1]; + val2 = mat->colval + mat->colptr[i2]; + break; + + default: + gk_errexit(SIGERR, "Invalid index type of %d.\n", what); + return 0.0; + } + + + switch (simtype) { + case GK_CSR_COS: + case GK_CSR_JAC: + sim = stat1 = stat2 = 0.0; + i1 = i2 = 0; + while (i1 ind2[i2]) { + stat2 += val2[i2]*val2[i2]; + i2++; + } + else { + sim += val1[i1]*val2[i2]; + stat1 += val1[i1]*val1[i1]; + stat2 += val2[i2]*val2[i2]; + i1++; + i2++; + } + } + if (simtype == GK_CSR_COS) + sim = (stat1*stat2 > 0.0 ? sim/sqrt(stat1*stat2) : 0.0); + else + sim = (stat1+stat2-sim > 0.0 ? sim/(stat1+stat2-sim) : 0.0); + break; + + case GK_CSR_MIN: + sim = stat1 = stat2 = 0.0; + i1 = i2 = 0; + while (i1 ind2[i2]) { + stat2 += val2[i2]; + i2++; + } + else { + sim += gk_min(val1[i1],val2[i2]); + stat1 += val1[i1]; + stat2 += val2[i2]; + i1++; + i2++; + } + } + sim = (stat1+stat2-sim > 0.0 ? sim/(stat1+stat2-sim) : 0.0); + + break; + + case GK_CSR_AMIN: + sim = stat1 = stat2 = 0.0; + i1 = i2 = 0; + while (i1 ind2[i2]) { + stat2 += val2[i2]; + i2++; + } + else { + sim += gk_min(val1[i1],val2[i2]); + stat1 += val1[i1]; + stat2 += val2[i2]; + i1++; + i2++; + } + } + sim = (stat1 > 0.0 ? sim/stat1 : 0.0); + + break; + + default: + gk_errexit(SIGERR, "Unknown similarity measure %d\n", simtype); + return -1; + } + + return sim; + +} + + +/*************************************************************************/ +/*! Finds the n most similar rows (neighbors) to the query using cosine + similarity. + + \param mat the matrix itself + \param nqterms is the number of columns in the query + \param qind is the list of query columns + \param qval is the list of correspodning query weights + \param simtype is the type of similarity and is one of GK_CSR_COS, + GK_CSR_JAC, GK_CSR_MIN, GK_CSR_AMIN + \param nsim is the maximum number of requested most similar rows. + If -1 is provided, then everything is returned unsorted. + \param minsim is the minimum similarity of the requested most + similar rows + \param hits is the result set. This array should be at least + of length nsim. + \param i_marker is an array of size equal to the number of rows + whose values are initialized to -1. If NULL is provided + then this array is allocated and freed internally. + \param i_cand is an array of size equal to the number of rows. + If NULL is provided then this array is allocated and freed + internally. + \returns the number of identified most similar rows, which can be + smaller than the requested number of nnbrs in those cases + in which there are no sufficiently many neighbors. +*/ +/**************************************************************************/ +int gk_csr_GetSimilarRows(gk_csr_t *mat, int nqterms, int *qind, + float *qval, int simtype, int nsim, float minsim, gk_fkv_t *hits, + int *i_marker, gk_fkv_t *i_cand) +{ + ssize_t i, ii, j, k; + int nrows, ncols, ncand; + ssize_t *colptr; + int *colind, *marker; + float *colval, *rnorms, mynorm, *rsums, mysum; + gk_fkv_t *cand; + + if (nqterms == 0) + return 0; + + nrows = mat->nrows; + ncols = mat->ncols; + colptr = mat->colptr; + colind = mat->colind; + colval = mat->colval; + + marker = (i_marker ? i_marker : gk_ismalloc(nrows, -1, "gk_csr_SimilarRows: marker")); + cand = (i_cand ? i_cand : gk_fkvmalloc(nrows, "gk_csr_SimilarRows: cand")); + + switch (simtype) { + case GK_CSR_COS: + for (ncand=0, ii=0; iirnorms; + mynorm = gk_fdot(nqterms, qval, 1, qval, 1); + + for (i=0; irsums; + mysum = gk_fsum(nqterms, qval, 1); + + for (i=0; i= minsim) + cand[j++] = cand[i]; + } + ncand = j; + + if (nsim == -1 || nsim >= ncand) { + nsim = ncand; + } + else { + nsim = gk_min(nsim, ncand); + gk_dfkvkselect(ncand, nsim, cand); + gk_fkvsortd(nsim, cand); + } + + gk_fkvcopy(nsim, cand, hits); + + if (i_marker == NULL) + gk_free((void **)&marker, LTERM); + if (i_cand == NULL) + gk_free((void **)&cand, LTERM); + + return nsim; +} + diff --git a/src/metis/GKlib/error.c b/src/metis/GKlib/error.c new file mode 100644 index 0000000..e2a18cf --- /dev/null +++ b/src/metis/GKlib/error.c @@ -0,0 +1,214 @@ +/*! +\file error.c +\brief Various error-handling functions + +This file contains functions dealing with error reporting and termination + +\author George +\date 1/1/2007 +\version\verbatim $Id: error.c 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + + +#define _GK_ERROR_C_ /* this is needed to properly declare the gk_jub* variables + as an extern function in GKlib.h */ + +#include + + +/* These are the jmp_buf for the graceful exit in case of severe errors. + Multiple buffers are defined to allow for recursive invokation. */ +#define MAX_JBUFS 128 +__thread int gk_cur_jbufs=-1; +__thread jmp_buf gk_jbufs[MAX_JBUFS]; +__thread jmp_buf gk_jbuf; + +typedef void (*gksighandler_t)(int); + +/* These are the holders of the old singal handlers for the trapped signals */ +static __thread gksighandler_t old_SIGMEM_handler; /* Custom signal */ +static __thread gksighandler_t old_SIGERR_handler; /* Custom signal */ +static __thread gksighandler_t old_SIGMEM_handlers[MAX_JBUFS]; /* Custom signal */ +static __thread gksighandler_t old_SIGERR_handlers[MAX_JBUFS]; /* Custom signal */ + +/* The following is used to control if the gk_errexit() will actually abort or not. + There is always a single copy of this variable */ +static int gk_exit_on_error = 1; + + +/*************************************************************************/ +/*! This function sets the gk_exit_on_error variable + */ +/*************************************************************************/ +void gk_set_exit_on_error(int value) +{ + gk_exit_on_error = value; +} + + + +/*************************************************************************/ +/*! This function prints an error message and exits + */ +/*************************************************************************/ +void errexit(char *f_str,...) +{ + va_list argp; + + va_start(argp, f_str); + vfprintf(stderr, f_str, argp); + va_end(argp); + + if (strlen(f_str) == 0 || f_str[strlen(f_str)-1] != '\n') + fprintf(stderr,"\n"); + fflush(stderr); + + if (gk_exit_on_error) + exit(-2); + + /* abort(); */ +} + + +/*************************************************************************/ +/*! This function prints an error message and raises a signum signal + */ +/*************************************************************************/ +void gk_errexit(int signum, char *f_str,...) +{ + va_list argp; + + va_start(argp, f_str); + vfprintf(stderr, f_str, argp); + va_end(argp); + + fprintf(stderr,"\n"); + fflush(stderr); + + if (gk_exit_on_error) + raise(signum); +} + + +/***************************************************************************/ +/*! This function sets a number of signal handlers and sets the return point + of a longjmp +*/ +/***************************************************************************/ +int gk_sigtrap() +{ + if (gk_cur_jbufs+1 >= MAX_JBUFS) + return 0; + + gk_cur_jbufs++; + + old_SIGMEM_handlers[gk_cur_jbufs] = signal(SIGMEM, gk_sigthrow); + old_SIGERR_handlers[gk_cur_jbufs] = signal(SIGERR, gk_sigthrow); + + return 1; +} + + +/***************************************************************************/ +/*! This function sets the handlers for the signals to their default handlers + */ +/***************************************************************************/ +int gk_siguntrap() +{ + if (gk_cur_jbufs == -1) + return 0; + + signal(SIGMEM, old_SIGMEM_handlers[gk_cur_jbufs]); + signal(SIGERR, old_SIGERR_handlers[gk_cur_jbufs]); + + gk_cur_jbufs--; + + return 1; +} + + +/*************************************************************************/ +/*! This function is the custome signal handler, which all it does is to + perform a longjump to the most recent saved environment + */ +/*************************************************************************/ +void gk_sigthrow(int signum) +{ + longjmp(gk_jbufs[gk_cur_jbufs], signum); +} + + +/*************************************************************************** +* This function sets a number of signal handlers and sets the return point +* of a longjmp +****************************************************************************/ +void gk_SetSignalHandlers() +{ + old_SIGMEM_handler = signal(SIGMEM, gk_NonLocalExit_Handler); + old_SIGERR_handler = signal(SIGERR, gk_NonLocalExit_Handler); +} + + +/*************************************************************************** +* This function sets the handlers for the signals to their default handlers +****************************************************************************/ +void gk_UnsetSignalHandlers() +{ + signal(SIGMEM, old_SIGMEM_handler); + signal(SIGERR, old_SIGERR_handler); +} + + +/************************************************************************* +* This function is the handler for SIGUSR1 that implements the cleaning up +* process prior to a non-local exit. +**************************************************************************/ +void gk_NonLocalExit_Handler(int signum) +{ + longjmp(gk_jbuf, signum); +} + + +/*************************************************************************/ +/*! \brief Thread-safe implementation of strerror() */ +/**************************************************************************/ +char *gk_strerror(int errnum) +{ +#if defined(WIN32) || defined(__MINGW32__) + return strerror(errnum); +#else +#ifndef SUNOS + static __thread char buf[1024]; + + strerror_r(errnum, buf, 1024); + + buf[1023] = '\0'; + return buf; +#else + return strerror(errnum); +#endif +#endif +} + + + +/************************************************************************* +* This function prints a backtrace of calling functions +**************************************************************************/ +void PrintBackTrace() +{ +#ifdef HAVE_EXECINFO_H + void *array[10]; + int i, size; + char **strings; + + size = backtrace(array, 10); + strings = backtrace_symbols(array, size); + + printf("Obtained %d stack frames.\n", size); + for (i=0; i + +/********************************************************************** + * This function computes the max accuracy score of a ranked list, + * given +1/-1 class list + **********************************************************************/ +float ComputeAccuracy(int n, gk_fkv_t *list) +{ + int i, P, N, TP, FN = 0; + float bAccuracy = 0.0; + float acc; + + for (P=0, i=0;i bAccuracy) + bAccuracy = acc; + } + + return bAccuracy; +} + + +/***************************************************************************** + * This function computes the ROC score of a ranked list, given a +1/-1 class + * list. + ******************************************************************************/ +float ComputeROCn(int n, int maxN, gk_fkv_t *list) +{ + int i, P, TP, FP, TPprev, FPprev, AUC; + float prev; + + FP = TP = FPprev = TPprev = AUC = 0; + prev = list[0].key -1; + + for (P=0, i=0; i 0 ? (float)(1.0*AUC/(P*FP)) : 0.0); +} + + +/***************************************************************************** +* This function computes the median rate of false positive for each positive +* instance. +******************************************************************************/ +float ComputeMedianRFP(int n, gk_fkv_t *list) +{ + int i, P, N, TP, FP; + + P = N = 0; + for (i=0; i + +/* Byte-wise swap two items of size SIZE. */ +#define QSSWAP(a, b, stmp) do { stmp = (a); (a) = (b); (b) = stmp; } while (0) + + +/******************************************************************************/ +/*! This function puts the 'topk' largest values in the beginning of the array */ +/*******************************************************************************/ +int gk_dfkvkselect(size_t n, int topk, gk_fkv_t *cand) +{ + int i, j, lo, hi, mid; + gk_fkv_t stmp; + float pivot; + + if (n <= topk) + return n; /* return if the array has fewer elements than we want */ + + for (lo=0, hi=n-1; lo < hi;) { + mid = lo + ((hi-lo) >> 1); + + /* select the median */ + if (cand[lo].key < cand[mid].key) + mid = lo; + if (cand[hi].key > cand[mid].key) + mid = hi; + else + goto jump_over; + if (cand[lo].key < cand[mid].key) + mid = lo; + +jump_over: + QSSWAP(cand[mid], cand[hi], stmp); + pivot = cand[hi].key; + + /* the partitioning algorithm */ + for (i=lo-1, j=lo; j= pivot) { + i++; + QSSWAP(cand[i], cand[j], stmp); + } + } + i++; + QSSWAP(cand[i], cand[hi], stmp); + + + if (i > topk) + hi = i-1; + else if (i < topk) + lo = i+1; + else + break; + } + +/* + if (cand[lo].key < cand[hi].key) + printf("Hmm Error: %d %d %d %f %f\n", i, lo, hi, cand[lo].key, cand[hi].key); + + + for (i=topk; i cand[j].key) + printf("Hmm Error: %d %d %f %f %d %d\n", i, j, cand[i].key, cand[j].key, lo, hi); + } +*/ + + return topk; +} + + +/******************************************************************************/ +/*! This function puts the 'topk' smallest values in the beginning of the array */ +/*******************************************************************************/ +int gk_ifkvkselect(size_t n, int topk, gk_fkv_t *cand) +{ + int i, j, lo, hi, mid; + gk_fkv_t stmp; + float pivot; + + if (n <= topk) + return n; /* return if the array has fewer elements than we want */ + + for (lo=0, hi=n-1; lo < hi;) { + mid = lo + ((hi-lo) >> 1); + + /* select the median */ + if (cand[lo].key > cand[mid].key) + mid = lo; + if (cand[hi].key < cand[mid].key) + mid = hi; + else + goto jump_over; + if (cand[lo].key > cand[mid].key) + mid = lo; + +jump_over: + QSSWAP(cand[mid], cand[hi], stmp); + pivot = cand[hi].key; + + /* the partitioning algorithm */ + for (i=lo-1, j=lo; j topk) + hi = i-1; + else if (i < topk) + lo = i+1; + else + break; + } + +/* + if (cand[lo].key > cand[hi].key) + printf("Hmm Error: %d %d %d %f %f\n", i, lo, hi, cand[lo].key, cand[hi].key); + + + for (i=topk; i + + + +/************************************************************************* +* This function checks if a file exists +**************************************************************************/ +int gk_fexists(char *fname) +{ + struct stat status; + + if (stat(fname, &status) == -1) + return 0; + + return S_ISREG(status.st_mode); +} + + +/************************************************************************* +* This function checks if a directory exists +**************************************************************************/ +int gk_dexists(char *dirname) +{ + struct stat status; + + if (stat(dirname, &status) == -1) + return 0; + + return S_ISDIR(status.st_mode); +} + + +/*************************************************************************/ +/*! \brief Returns the size of the file in bytes + +This function returns the size of a file as a 64 bit integer. If there +were any errors in stat'ing the file, -1 is returned. +\note That due to the -1 return code, the maximum file size is limited to + 63 bits (which I guess is okay for now). +*/ +/**************************************************************************/ +intmax_t gk_getfsize(char *filename) +{ + struct stat status; + + if (stat(filename, &status) == -1) + return -1; + + return (intmax_t)(status.st_size); +} + + +/*************************************************************************/ +/*! This function gets some basic statistics about the file. + \param fname is the name of the file + \param r_nlines is the number of lines in the file. If it is NULL, + this information is not returned. + \param r_ntokens is the number of tokens in the file. If it is NULL, + this information is not returned. + \param r_max_nlntokens is the maximum number of tokens in any line + in the file. If it is NULL this information is not returned. + \param r_nbytes is the number of bytes in the file. If it is NULL, + this information is not returned. +*/ +/*************************************************************************/ +void gk_getfilestats(char *fname, size_t *r_nlines, size_t *r_ntokens, + size_t *r_max_nlntokens, size_t *r_nbytes) +{ + size_t nlines=0, ntokens=0, max_nlntokens=0, nbytes=0, oldntokens=0, nread; + int intoken=0; + char buffer[2049], *cptr; + FILE *fpin; + + fpin = gk_fopen(fname, "r", "gk_GetFileStats"); + + while (!feof(fpin)) { + nread = fread(buffer, sizeof(char), 2048, fpin); + nbytes += nread; + + buffer[nread] = '\0'; /* There is space for this one */ + for (cptr=buffer; *cptr!='\0'; cptr++) { + if (*cptr == '\n') { + nlines++; + ntokens += intoken; + intoken = 0; + if (max_nlntokens < ntokens-oldntokens) + max_nlntokens = ntokens-oldntokens; + oldntokens = ntokens; + } + else if (*cptr == ' ' || *cptr == '\t') { + ntokens += intoken; + intoken = 0; + } + else { + intoken = 1; + } + } + } + ntokens += intoken; + if (max_nlntokens < ntokens-oldntokens) + max_nlntokens = ntokens-oldntokens; + + gk_fclose(fpin); + + if (r_nlines != NULL) + *r_nlines = nlines; + if (r_ntokens != NULL) + *r_ntokens = ntokens; + if (r_max_nlntokens != NULL) + *r_max_nlntokens = max_nlntokens; + if (r_nbytes != NULL) + *r_nbytes = nbytes; +} + + +/************************************************************************* +* This function takes in a potentially full path specification of a file +* and just returns a string containing just the basename of the file. +* The basename is derived from the actual filename by stripping the last +* .ext part. +**************************************************************************/ +char *gk_getbasename(char *path) +{ + char *startptr, *endptr; + char *basename; + + if ((startptr = strrchr(path, '/')) == NULL) + startptr = path; + else + startptr = startptr+1; + + basename = gk_strdup(startptr); + + if ((endptr = strrchr(basename, '.')) != NULL) + *endptr = '\0'; + + return basename; +} + +/************************************************************************* +* This function takes in a potentially full path specification of a file +* and just returns a string corresponding to its file extension. The +* extension of a file is considered to be the string right after the +* last '.' character. +**************************************************************************/ +char *gk_getextname(char *path) +{ + char *startptr; + + if ((startptr = strrchr(path, '.')) == NULL) + return gk_strdup(path); + else + return gk_strdup(startptr+1); +} + +/************************************************************************* +* This function takes in a potentially full path specification of a file +* and just returns a string containing just the filename. +**************************************************************************/ +char *gk_getfilename(char *path) +{ + char *startptr; + + if ((startptr = strrchr(path, '/')) == NULL) + return gk_strdup(path); + else + return gk_strdup(startptr+1); +} + +/************************************************************************* +* This function takes in a potentially full path specification of a file +* and extracts the directory path component if it exists, otherwise it +* returns "./" as the path. The memory for it is dynamically allocated. +**************************************************************************/ +char *getpathname(char *path) +{ + char *endptr, *tmp; + + if ((endptr = strrchr(path, '/')) == NULL) { + return gk_strdup("."); + } + else { + tmp = gk_strdup(path); + *(strrchr(tmp, '/')) = '\0'; + return tmp; + } +} + + + +/************************************************************************* +* This function creates a path +**************************************************************************/ +int gk_mkpath(char *pathname) +{ + char tmp[2048]; + + sprintf(tmp, "mkdir -p %s", pathname); + return system(tmp); +} + + +/************************************************************************* +* This function deletes a directory tree and all of its contents +**************************************************************************/ +int gk_rmpath(char *pathname) +{ + char tmp[2048]; + + sprintf(tmp, "rm -r %s", pathname); + return system(tmp); +} diff --git a/src/metis/GKlib/getopt.c b/src/metis/GKlib/getopt.c new file mode 100644 index 0000000..437befc --- /dev/null +++ b/src/metis/GKlib/getopt.c @@ -0,0 +1,854 @@ +/*************************************************************************/ +/*! \file getopt.c +\brief Command line parsing + +This file contains a implementation of GNU's Getopt facility. The purpose +for including it here is to ensure portability across different unix- and +windows-based systems. + +\warning +The implementation provided here uses the \c gk_ prefix for all variables +used by the standard Getopt facility to communicate with the program. +So, do read the documentation here. + +\verbatim + Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 + Free Software Foundation, Inc. This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. +\endverbatim +*/ +/*************************************************************************/ + + +#include + +/*************************************************************************/ +/* Local function prototypes */ +/*************************************************************************/ +static void exchange (char **); +static char *gk_getopt_initialize (int, char **, char *); +static int gk_getopt_internal(int argc, char **argv, char *optstring, + struct gk_option *longopts, int *longind, int long_only); + + + +/*************************************************************************/ +/*! \brief For communication arguments to the caller. + +This variable is set by getopt to point at the value of the option argument, +for those options that accept arguments. +*/ +/*************************************************************************/ +char *gk_optarg; + + +/*************************************************************************/ +/*! \brief Index in ARGV of the next element to be scanned. + +This variable is set by getopt to the index of the next element of the argv +array to be processed. Once getopt has found all of the option arguments, +you can use this variable to determine where the remaining non-option arguments +begin. +*/ +/*************************************************************************/ +int gk_optind = 1; + + +/*************************************************************************/ +/*! \brief Controls error reporting for unrecognized options. + +If the value of this variable is nonzero, then getopt prints an error +message to the standard error stream if it encounters an unknown option +character or an option with a missing required argument. This is the default +behavior. If you set this variable to zero, getopt does not print any messages, +but it still returns the character ? to indicate an error. +*/ +/*************************************************************************/ +int gk_opterr = 1; + + +/*************************************************************************/ +/*! \brief Stores unknown option characters + +When getopt encounters an unknown option character or an option with a +missing required argument, it stores that option character in this +variable. You can use this for providing your own diagnostic messages. +*/ +/*************************************************************************/ +int gk_optopt = '?'; + + +/*************************************************************************/ +/* +Records that the getopt facility has been initialized. +*/ +/*************************************************************************/ +int gk_getopt_initialized; + + +/*************************************************************************/ +/* +The next char to be scanned in the option-element in which the last option +character we returned was found. This allows us to pick up the scan where +we left off. + +If this is zero, or a null string, it means resume the scan by advancing +to the next ARGV-element. +*/ +/*************************************************************************/ +static char *nextchar; + + +/*************************************************************************/ +/* +Value of POSIXLY_CORRECT environment variable. +*/ +/*************************************************************************/ +static char *posixly_correct; + + +/*************************************************************************/ +/* +Describe how to deal with options that follow non-option ARGV-elements. + +If the caller did not specify anything, the default is REQUIRE_ORDER if +the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. + +REQUIRE_ORDER means don't recognize them as options; stop option processing +when the first non-option is seen. This is what Unix does. This mode of +operation is selected by either setting the environment variable +POSIXLY_CORRECT, or using `+' as the first character of the list of +option characters. + +PERMUTE is the default. We permute the contents of ARGV as we scan, so +that eventually all the non-options are at the end. This allows options +to be given in any order, even with programs that were not written to +expect this. + +RETURN_IN_ORDER is an option available to programs that were written +to expect options and other ARGV-elements in any order and that care +about the ordering of the two. We describe each non-option ARGV-element +as if it were the argument of an option with character code 1. +Using `-' as the first character of the list of option characters +selects this mode of operation. + +The special argument `--' forces an end of option-scanning regardless +of the value of `ordering'. In the case of RETURN_IN_ORDER, only +`--' can cause `getopt' to return -1 with `gk_optind' != ARGC. +*/ +/*************************************************************************/ +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + + + +/*************************************************************************/ +/* +Describe the part of ARGV that contains non-options that have +been skipped. `first_nonopt' is the index in ARGV of the first of them; +`last_nonopt' is the index after the last of them. +*/ +/*************************************************************************/ +static int first_nonopt; +static int last_nonopt; + + + + + +/*************************************************************************/ +/* +Handle permutation of arguments. + +Exchange two adjacent subsequences of ARGV. +One subsequence is elements [first_nonopt,last_nonopt) +which contains all the non-options that have been skipped so far. +The other is elements [last_nonopt,gk_optind), which contains all +the options processed since those non-options were skipped. + +`first_nonopt' and `last_nonopt' are relocated so that they describe +the new indices of the non-options in ARGV after they are moved. +*/ +/*************************************************************************/ +static void exchange (char **argv) +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = gk_optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) { + if (top - middle > middle - bottom) { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (gk_optind - last_nonopt); + last_nonopt = gk_optind; +} + + + +/*************************************************************************/ +/* +Initialize the internal data when the first call is made. +*/ +/*************************************************************************/ +static char *gk_getopt_initialize (int argc, char **argv, char *optstring) +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = gk_optind; + + nextchar = NULL; + + posixly_correct = getenv("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + if (optstring[0] == '-') { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + + +/*************************************************************************/ +/* + Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `gk_optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `gk_optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `gk_opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `gk_optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `gk_optarg', otherwise `gk_optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + LONGOPTS is a vector of `struct gk_option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. +*/ +/*************************************************************************/ +static int gk_getopt_internal(int argc, char **argv, char *optstring, + struct gk_option *longopts, int *longind, int long_only) +{ + int print_errors = gk_opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + gk_optarg = NULL; + + if (gk_optind == 0 || !gk_getopt_initialized) { + if (gk_optind == 0) + gk_optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = gk_getopt_initialize (argc, argv, optstring); + gk_getopt_initialized = 1; + } + + /* Test whether ARGV[gk_optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +# define NONOPTION_P (argv[gk_optind][0] != '-' || argv[gk_optind][1] == '\0') + + if (nextchar == NULL || *nextchar == '\0') { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > gk_optind) + last_nonopt = gk_optind; + if (first_nonopt > gk_optind) + first_nonopt = gk_optind; + + if (ordering == PERMUTE) { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != gk_optind) + exchange ((char **) argv); + else if (last_nonopt != gk_optind) + first_nonopt = gk_optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (gk_optind < argc && NONOPTION_P) + gk_optind++; + + last_nonopt = gk_optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (gk_optind != argc && !strcmp (argv[gk_optind], "--")) { + gk_optind++; + + if (first_nonopt != last_nonopt && last_nonopt != gk_optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = gk_optind; + last_nonopt = argc; + + gk_optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (gk_optind == argc) { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + gk_optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) { + if (ordering == REQUIRE_ORDER) + return -1; + gk_optarg = argv[gk_optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[gk_optind] + 1 + (longopts != NULL && argv[gk_optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL && (argv[gk_optind][1] == '-' || (long_only && (argv[gk_optind][2] || !strchr(optstring, argv[gk_optind][1]))))) { + char *nameend; + struct gk_option *p; + struct gk_option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) { + if (!strncmp (p->name, nextchar, nameend - nextchar)) { + if ((unsigned int) (nameend - nextchar) == (unsigned int) strlen (p->name)) { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + } + + if (ambig && !exact) { + if (print_errors) + fprintf(stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[gk_optind]); + + nextchar += strlen (nextchar); + gk_optind++; + gk_optopt = 0; + return '?'; + } + + if (pfound != NULL) { + option_index = indfound; + gk_optind++; + if (*nameend) { + /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ + if (pfound->has_arg) + gk_optarg = nameend + 1; + else { + if (print_errors) { + if (argv[gk_optind - 1][1] == '-') + /* --option */ + fprintf(stderr, "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name); + else + /* +option or -option */ + fprintf(stderr, "%s: option `%c%s' doesn't allow an argument\n", argv[0], argv[gk_optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + gk_optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) { + if (gk_optind < argc) + gk_optarg = argv[gk_optind++]; + else { + if (print_errors) + fprintf(stderr, "%s: option `%s' requires an argument\n", argv[0], argv[gk_optind - 1]); + nextchar += strlen (nextchar); + gk_optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. Otherwise interpret it as a short option. */ + if (!long_only || argv[gk_optind][1] == '-' || strchr(optstring, *nextchar) == NULL) { + if (print_errors) { + if (argv[gk_optind][1] == '-') + /* --option */ + fprintf(stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar); + else + /* +option or -option */ + fprintf(stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[gk_optind][0], nextchar); + } + nextchar = (char *) ""; + gk_optind++; + gk_optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + { + char c = *nextchar++; + char *temp = strchr(optstring, c); + + /* Increment `gk_optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++gk_optind; + + if (temp == NULL || c == ':') { + if (print_errors) { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf(stderr, "%s: illegal option -- %c\n", argv[0], c); + else + fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); + } + gk_optopt = c; + return '?'; + } + + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') { + char *nameend; + struct gk_option *p; + struct gk_option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') { + gk_optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + gk_optind++; + } + else if (gk_optind == argc) { + if (print_errors) { + /* 1003.2 specifies the format of this message. */ + fprintf(stderr, "%s: option requires an argument -- %c\n", argv[0], c); + } + gk_optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `gk_optind' once; increment it again when taking next ARGV-elt as argument. */ + gk_optarg = argv[gk_optind++]; + + /* gk_optarg is now the argument, see if it's in the table of longopts. */ + + for (nextchar = nameend = gk_optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) { + if (!strncmp (p->name, nextchar, nameend - nextchar)) { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + } + if (ambig && !exact) { + if (print_errors) + fprintf(stderr, "%s: option `-W %s' is ambiguous\n", argv[0], argv[gk_optind]); + nextchar += strlen (nextchar); + gk_optind++; + return '?'; + } + if (pfound != NULL) { + option_index = indfound; + if (*nameend) { + /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ + if (pfound->has_arg) + gk_optarg = nameend + 1; + else { + if (print_errors) + fprintf(stderr, "%s: option `-W %s' doesn't allow an argument\n", argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) { + if (gk_optind < argc) + gk_optarg = argv[gk_optind++]; + else { + if (print_errors) + fprintf(stderr, "%s: option `%s' requires an argument\n", argv[0], argv[gk_optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + + if (temp[1] == ':') { + if (temp[2] == ':') { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') { + gk_optarg = nextchar; + gk_optind++; + } + else + gk_optarg = NULL; + nextchar = NULL; + } + else { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') { + gk_optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ + gk_optind++; + } + else if (gk_optind == argc) { + if (print_errors) { + /* 1003.2 specifies the format of this message. */ + fprintf(stderr, "%s: option requires an argument -- %c\n", argv[0], c); + } + gk_optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `gk_optind' once; increment it again when taking next ARGV-elt as argument. */ + gk_optarg = argv[gk_optind++]; + nextchar = NULL; + } + } + return c; + } +} + + + +/*************************************************************************/ +/*! \brief Parse command-line arguments + +The gk_getopt() function gets the next option argument from the argument +list specified by the \c argv and \c argc arguments. Normally these values +come directly from the arguments received by main(). + +\param argc is the number of command line arguments passed to main(). +\param argv is an array of strings storing the above command line + arguments. +\param options is a string that specifies the option characters that + are valid for this program. An option character in this string + can be followed by a colon (`:') to indicate that it takes a + required argument. If an option character is followed by two + colons (`::'), its argument is optional; this is a GNU extension. + +\return +It returns the option character for the next command line option. When no +more option arguments are available, it returns -1. There may still be +more non-option arguments; you must compare the external variable +#gk_optind against the \c argc parameter to check this. + +\return +If the option has an argument, gk_getopt() returns the argument by storing +it in the variable #gk_optarg. You don't ordinarily need to copy the +#gk_optarg string, since it is a pointer into the original \c argv array, +not into a static area that might be overwritten. + +\return +If gk_getopt() finds an option character in \c argv that was not included +in options, or a missing option argument, it returns `?' and sets the +external variable #gk_optopt to the actual option character. +If the first character of options is a colon (`:'), then gk_getopt() +returns `:' instead of `?' to indicate a missing option argument. +In addition, if the external variable #gk_opterr is nonzero (which is +the default), gk_getopt() prints an error message. This variable is +set by gk_getopt() to point at the value of the option argument, +for those options that accept arguments. + + +gk_getopt() has three ways to deal with options that follow non-options +\c argv elements. The special argument `--' forces in all cases +the end of option scanning. + - The default is to permute the contents of \c argv while scanning it + so that eventually all the non-options are at the end. This allows + options to be given in any order, even with programs that were not + written to expect this. + - If the options argument string begins with a hyphen (`-'), this is + treated specially. It permits arguments that are not options to be + returned as if they were associated with option character `\\1'. + - POSIX demands the following behavior: The first non-option stops + option processing. This mode is selected by either setting the + environment variable POSIXLY_CORRECT or beginning the options + argument string with a plus sign (`+'). + +*/ +/*************************************************************************/ +int gk_getopt(int argc, char **argv, char *options) +{ + return gk_getopt_internal(argc, argv, options, NULL, NULL, 0); +} + + +/*************************************************************************/ +/*! \brief Parse command-line arguments with long options + +This function accepts GNU-style long options as well as single-character +options. + +\param argc is the number of command line arguments passed to main(). +\param argv is an array of strings storing the above command line + arguments. +\param options describes the short options to accept, just as it does + in gk_getopt(). +\param long_options describes the long options to accept. See the + defintion of ::gk_option for more information. +\param opt_index this is a returned variable. For any long option, + gk_getopt_long() tells you the index in the array \c long_options + of the options definition, by storing it into *opt_index. + You can get the name of the option with longopts[*opt_index].name. + So you can distinguish among long options either by the values + in their val fields or by their indices. You can also distinguish + in this way among long options that set flags. + + +\return +When gk_getopt_long() encounters a short option, it does the same thing +that gk_getopt() would do: it returns the character code for the option, +and stores the options argument (if it has one) in #gk_optarg. + +\return +When gk_getopt_long() encounters a long option, it takes actions based +on the flag and val fields of the definition of that option. + +\return +If flag is a null pointer, then gk_getopt_long() returns the contents +of val to indicate which option it found. You should arrange distinct +values in the val field for options with different meanings, so you +can decode these values after gk_getopt_long() returns. If the long +option is equivalent to a short option, you can use the short option's +character code in val. + +\return +If flag is not a null pointer, that means this option should just set +a flag in the program. The flag is a variable of type int that you +define. Put the address of the flag in the flag field. Put in the +val field the value you would like this option to store in the flag. +In this case, gk_getopt_long() returns 0. + +\return +When a long option has an argument, gk_getopt_long() puts the argument +value in the variable #gk_optarg before returning. When the option has +no argument, the value in #gk_optarg is a null pointer. This is +how you can tell whether an optional argument was supplied. + +\return +When gk_getopt_long() has no more options to handle, it returns -1, +and leaves in the variable #gk_optind the index in argv of the next +remaining argument. +*/ +/*************************************************************************/ +int gk_getopt_long( int argc, char **argv, char *options, + struct gk_option *long_options, int *opt_index) +{ + return gk_getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + + + +/*************************************************************************/ +/*! \brief Parse command-line arguments with only long options + +Like gk_getopt_long(), but '-' as well as '--' can indicate a long option. +If an option that starts with '-' (not '--') doesn't match a long option, +but does match a short option, it is parsed as a short option instead. +*/ +/*************************************************************************/ +int gk_getopt_long_only(int argc, char **argv, char *options, + struct gk_option *long_options, int *opt_index) +{ + return gk_getopt_internal(argc, argv, options, long_options, opt_index, 1); +} + diff --git a/src/metis/GKlib/gk_arch.h b/src/metis/GKlib/gk_arch.h new file mode 100644 index 0000000..2cb80cc --- /dev/null +++ b/src/metis/GKlib/gk_arch.h @@ -0,0 +1,71 @@ +/*! +\file gk_arch.h +\brief This file contains various architecture-specific declerations + +\date Started 3/27/2007 +\author George +\version\verbatim $Id: gk_arch.h 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + +#ifndef _GK_ARCH_H_ +#define _GK_ARCH_H_ + +/************************************************************************* +* Architecture-specific differences in header files +**************************************************************************/ +#ifdef LINUX +#if !defined(__USE_XOPEN) +#define __USE_XOPEN +#endif +#if !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE 600 +#endif +#if !defined(__USE_XOPEN2K) +#define __USE_XOPEN2K +#endif +#endif + + +#ifdef HAVE_EXECINFO_H +#include +#endif + + +#ifdef __MSC__ + #include "ms_stdint.h" + #include "ms_inttypes.h" + #include "ms_stat.h" +#else +#ifndef SUNOS + #include +#endif + #include + #include + #include + #include +#endif + + +/************************************************************************* +* Architecture-specific modifications +**************************************************************************/ +#ifdef WIN32 +typedef ptrdiff_t ssize_t; +#endif + + +#ifdef SUNOS +#define PTRDIFF_MAX INT64_MAX +#endif + +#ifdef __MSC__ +/* MSC does not have rint() function */ +#define rint(x) ((int)((x)+0.5)) + +/* MSC does not have INFINITY defined */ +#ifndef INFINITY +#define INFINITY FLT_MAX +#endif +#endif + +#endif diff --git a/src/metis/GKlib/gk_defs.h b/src/metis/GKlib/gk_defs.h new file mode 100644 index 0000000..d75e72d --- /dev/null +++ b/src/metis/GKlib/gk_defs.h @@ -0,0 +1,69 @@ +/*! +\file gk_defs.h +\brief This file contains various constants definitions + +\date Started 3/27/2007 +\author George +\version\verbatim $Id: gk_defs.h 12732 2012-09-24 20:54:50Z karypis $ \endverbatim +*/ + +#ifndef _GK_DEFS_H_ +#define _GK_DEFS_H_ + + +#define LTERM (void **) 0 /* List terminator for GKfree() */ + +/* mopt_t types */ +#define GK_MOPT_MARK 1 +#define GK_MOPT_CORE 2 +#define GK_MOPT_HEAP 3 + +#define HTABLE_EMPTY -1 +#define HTABLE_DELETED -2 +#define HTABLE_FIRST 1 +#define HTABLE_NEXT 2 + +/* pdb corruption bit switches */ +#define CRP_ALTLOCS 1 +#define CRP_MISSINGCA 2 +#define CRP_MISSINGBB 4 +#define CRP_MULTICHAIN 8 +#define CRP_MULTICA 16 +#define CRP_MULTIBB 32 + +#define MAXLINELEN 300000 + +/* GKlib signals to standard signal mapping */ +#define SIGMEM SIGABRT +#define SIGERR SIGTERM + + +/* CSR-related defines */ +#define GK_CSR_ROW 1 +#define GK_CSR_COL 2 + +#define GK_CSR_MAXTF 1 +#define GK_CSR_SQRT 2 +#define GK_CSR_POW25 3 +#define GK_CSR_POW65 4 +#define GK_CSR_POW75 5 +#define GK_CSR_POW85 6 +#define GK_CSR_LOG 7 +#define GK_CSR_IDF 8 +#define GK_CSR_IDF2 9 +#define GK_CSR_MAXTF2 10 + +#define GK_CSR_COS 1 +#define GK_CSR_JAC 2 +#define GK_CSR_MIN 3 +#define GK_CSR_AMIN 4 + +#define GK_CSR_FMT_CLUTO 1 +#define GK_CSR_FMT_CSR 2 +#define GK_CSR_FMT_METIS 3 +#define GK_CSR_FMT_BINROW 4 +#define GK_CSR_FMT_BINCOL 5 + +#define GK_GRAPH_FMT_METIS 1 + +#endif diff --git a/src/metis/GKlib/gk_externs.h b/src/metis/GKlib/gk_externs.h new file mode 100644 index 0000000..2c0fdd9 --- /dev/null +++ b/src/metis/GKlib/gk_externs.h @@ -0,0 +1,25 @@ +/*! +\file gk_externs.h +\brief This file contains definitions of external variables created by GKlib + +\date Started 3/27/2007 +\author George +\version\verbatim $Id: gk_externs.h 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + +#ifndef _GK_EXTERNS_H_ +#define _GK_EXTERNS_H_ + + +/************************************************************************* +* Extern variable definition. Hopefully, the __thread makes them thread-safe. +**************************************************************************/ +#ifndef _GK_ERROR_C_ +/* declared in error.c */ +extern __thread int gk_cur_jbufs; +extern __thread jmp_buf gk_jbufs[]; +extern __thread jmp_buf gk_jbuf; + +#endif + +#endif diff --git a/src/metis/GKlib/gk_getopt.h b/src/metis/GKlib/gk_getopt.h new file mode 100644 index 0000000..4bb8611 --- /dev/null +++ b/src/metis/GKlib/gk_getopt.h @@ -0,0 +1,64 @@ +/*! +\file gk_getopt.h +\brief This file contains GNU's externs/structs/prototypes + +\date Started 3/27/2007 +\author George +\version\verbatim $Id: gk_getopt.h 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + +#ifndef _GK_GETOPT_H_ +#define _GK_GETOPT_H_ + + +/* Externals from getopt.c */ +extern char *gk_optarg; +extern int gk_optind; +extern int gk_opterr; +extern int gk_optopt; + + +/*! \brief The structure that stores the information about the command-line options + +This structure describes a single long option name for the sake of +gk_getopt_long(). The argument long_options must be an array +of these structures, one for each long option. Terminate the array with +an element containing all zeros. +*/ +struct gk_option { + char *name; /*!< This field is the name of the option. */ + int has_arg; /*!< This field says whether the option takes an argument. + It is an integer, and there are three legitimate values: + no_argument, required_argument and optional_argument. + */ + int *flag; /*!< See the discussion on ::gk_option#val */ + int val; /*!< These fields control how to report or act on the option + when it occurs. + + If flag is a null pointer, then the val is a value which + identifies this option. Often these values are chosen + to uniquely identify particular long options. + + If flag is not a null pointer, it should be the address + of an int variable which is the flag for this option. + The value in val is the value to store in the flag to + indicate that the option was seen. */ +}; + +/* Names for the values of the `has_arg' field of `struct gk_option'. */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + + +/* Function prototypes */ +extern int gk_getopt(int __argc, char **__argv, char *__shortopts); +extern int gk_getopt_long(int __argc, char **__argv, char *__shortopts, + struct gk_option *__longopts, int *__longind); +extern int gk_getopt_long_only (int __argc, char **__argv, + char *__shortopts, struct gk_option *__longopts, int *__longind); + + + +#endif + diff --git a/src/metis/GKlib/gk_macros.h b/src/metis/GKlib/gk_macros.h new file mode 100644 index 0000000..d1e288b --- /dev/null +++ b/src/metis/GKlib/gk_macros.h @@ -0,0 +1,153 @@ +/*! +\file gk_macros.h +\brief This file contains various macros + +\date Started 3/27/2007 +\author George +\version\verbatim $Id: gk_macros.h 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + +#ifndef _GK_MACROS_H_ +#define _GK_MACROS_H_ + +/*------------------------------------------------------------- + * Usefull commands + *-------------------------------------------------------------*/ +#define gk_max(a, b) ((a) >= (b) ? (a) : (b)) +#define gk_min(a, b) ((a) >= (b) ? (b) : (a)) +#define gk_max3(a, b, c) ((a) >= (b) && (a) >= (c) ? (a) : ((b) >= (a) && (b) >= (c) ? (b) : (c))) +#define gk_SWAP(a, b, tmp) do {(tmp) = (a); (a) = (b); (b) = (tmp);} while(0) +#define INC_DEC(a, b, val) do {(a) += (val); (b) -= (val);} while(0) +#define sign(a, b) ((a >= 0 ? b : -b)) + +#define ONEOVERRANDMAX (1.0/(RAND_MAX+1.0)) +#define RandomInRange(u) ((int) (ONEOVERRANDMAX*(u)*rand())) + +#define gk_abs(x) ((x) >= 0 ? (x) : -(x)) + + +/*------------------------------------------------------------- + * Timing macros + *-------------------------------------------------------------*/ +#define gk_clearcputimer(tmr) (tmr = 0.0) +#define gk_startcputimer(tmr) (tmr -= gk_CPUSeconds()) +#define gk_stopcputimer(tmr) (tmr += gk_CPUSeconds()) +#define gk_getcputimer(tmr) (tmr) + +#define gk_clearwctimer(tmr) (tmr = 0.0) +#define gk_startwctimer(tmr) (tmr -= gk_WClockSeconds()) +#define gk_stopwctimer(tmr) (tmr += gk_WClockSeconds()) +#define gk_getwctimer(tmr) (tmr) + +/*------------------------------------------------------------- + * dbglvl handling macros + *-------------------------------------------------------------*/ +#define IFSET(a, flag, cmd) if ((a)&(flag)) (cmd); + + +/*------------------------------------------------------------- + * gracefull library exit macro + *-------------------------------------------------------------*/ +#define GKSETJMP() (setjmp(gk_return_to_entry)) +#define gk_sigcatch() (setjmp(gk_jbufs[gk_cur_jbufs])) + + +/*------------------------------------------------------------- + * Debuging memory leaks + *-------------------------------------------------------------*/ +#ifdef DMALLOC +# define MALLOC_CHECK(ptr) \ + if (malloc_verify((ptr)) == DMALLOC_VERIFY_ERROR) { \ + printf("***MALLOC_CHECK failed on line %d of file %s: " #ptr "\n", \ + __LINE__, __FILE__); \ + abort(); \ + } +#else +# define MALLOC_CHECK(ptr) ; +#endif + + +/*------------------------------------------------------------- + * CSR conversion macros + *-------------------------------------------------------------*/ +#define MAKECSR(i, n, a) \ + do { \ + for (i=1; i0; i--) a[i] = a[i-1]; \ + a[0] = 0; \ + } while(0) + +#define SHIFTCSR(i, n, a) \ + do { \ + for (i=n; i>0; i--) a[i] = a[i-1]; \ + a[0] = 0; \ + } while(0) + + +/*------------------------------------------------------------- + * ASSERTS that cannot be turned off! + *-------------------------------------------------------------*/ +#define GKASSERT(expr) \ + if (!(expr)) { \ + printf("***ASSERTION failed on line %d of file %s: " #expr "\n", \ + __LINE__, __FILE__); \ + abort(); \ + } + +#define GKASSERTP(expr,msg) \ + if (!(expr)) { \ + printf("***ASSERTION failed on line %d of file %s: " #expr "\n", \ + __LINE__, __FILE__); \ + printf msg ; \ + printf("\n"); \ + abort(); \ + } + +#define GKCUASSERT(expr) \ + if (!(expr)) { \ + printf("***ASSERTION failed on line %d of file %s: " #expr "\n", \ + __LINE__, __FILE__); \ + } + +#define GKCUASSERTP(expr,msg) \ + if (!(expr)) { \ + printf("***ASSERTION failed on line %d of file %s: " #expr "\n", \ + __LINE__, __FILE__); \ + printf msg ; \ + printf("\n"); \ + } + +/*------------------------------------------------------------- + * Program Assertions + *-------------------------------------------------------------*/ +#ifndef NDEBUG +# define ASSERT(expr) \ + if (!(expr)) { \ + printf("***ASSERTION failed on line %d of file %s: " #expr "\n", \ + __LINE__, __FILE__); \ + assert(expr); \ + } + +# define ASSERTP(expr,msg) \ + if (!(expr)) { \ + printf("***ASSERTION failed on line %d of file %s: " #expr "\n", \ + __LINE__, __FILE__); \ + printf msg ; \ + printf("\n"); \ + assert(expr); \ + } +#else +# define ASSERT(expr) ; +# define ASSERTP(expr,msg) ; +#endif + +#ifndef NDEBUG2 +# define ASSERT2 ASSERT +# define ASSERTP2 ASSERTP +#else +# define ASSERT2(expr) ; +# define ASSERTP2(expr,msg) ; +#endif + + +#endif diff --git a/src/metis/GKlib/gk_mkblas.h b/src/metis/GKlib/gk_mkblas.h new file mode 100644 index 0000000..7dd96df --- /dev/null +++ b/src/metis/GKlib/gk_mkblas.h @@ -0,0 +1,201 @@ +/*! +\file gk_mkblas.h +\brief Templates for BLAS-like routines + +\date Started 3/28/07 +\author George +\version\verbatim $Id: gk_mkblas.h 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + +#ifndef _GK_MKBLAS_H_ +#define _GK_MKBLAS_H_ + + +#define GK_MKBLAS(PRFX, TYPE, OUTTYPE) \ +/*************************************************************************/\ +/*! The macro for gk_?incset()-class of routines */\ +/*************************************************************************/\ +TYPE *PRFX ## incset(size_t n, TYPE baseval, TYPE *x)\ +{\ + size_t i;\ +\ + for (i=0; i x[max] ? i : max);\ +\ + return x[max];\ +}\ +\ +\ +/*************************************************************************/\ +/*! The macro for gk_?min()-class of routines */\ +/*************************************************************************/\ +TYPE PRFX ## min(size_t n, TYPE *x)\ +{\ + size_t i, min=0;\ +\ + if (n <= 0) return (TYPE) 0;\ +\ + for (i=1; i x[max] ? i : max);\ +\ + return max;\ +}\ +\ +\ +/*************************************************************************/\ +/*! The macro for gk_?argmin()-class of routines */\ +/*************************************************************************/\ +size_t PRFX ## argmin(size_t n, TYPE *x)\ +{\ + size_t i, min=0;\ +\ + for (i=1; i 0 ? (OUTTYPE)sqrt((double)partial) : (OUTTYPE)0);\ +}\ +\ +\ +/*************************************************************************/\ +/*! The macro for gk_?dot()-class of routines */\ +/**************************************************************************/\ +OUTTYPE PRFX ## dot(size_t n, TYPE *x, size_t incx, TYPE *y, size_t incy)\ +{\ + size_t i;\ + OUTTYPE partial = 0.0;\ + \ + for (i=0; innodes = 0;\ + queue->maxnodes = maxnodes;\ +\ + queue->heap = KVMALLOC(maxnodes, "gk_PQInit: heap");\ + queue->locator = gk_idxsmalloc(maxnodes, -1, "gk_PQInit: locator");\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function resets the priority queue */\ +/**************************************************************************/\ +void FPRFX ## Reset(PQT *queue)\ +{\ + gk_idx_t i;\ + gk_idx_t *locator=queue->locator;\ + KVT *heap=queue->heap;\ +\ + for (i=queue->nnodes-1; i>=0; i--)\ + locator[heap[i].val] = -1;\ + queue->nnodes = 0;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function frees the internal datastructures of the priority queue */\ +/**************************************************************************/\ +void FPRFX ## Free(PQT *queue)\ +{\ + if (queue == NULL) return;\ + gk_free((void **)&queue->heap, &queue->locator, LTERM);\ + queue->maxnodes = 0;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function frees the internal datastructures of the priority queue \ + and the queue itself */\ +/**************************************************************************/\ +void FPRFX ## Destroy(PQT *queue)\ +{\ + if (queue == NULL) return;\ + FPRFX ## Free(queue);\ + gk_free((void **)&queue, LTERM);\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the length of the queue */\ +/**************************************************************************/\ +size_t FPRFX ## Length(PQT *queue)\ +{\ + return queue->nnodes;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function adds an item in the priority queue */\ +/**************************************************************************/\ +int FPRFX ## Insert(PQT *queue, VT node, KT key)\ +{\ + gk_idx_t i, j;\ + gk_idx_t *locator=queue->locator;\ + KVT *heap=queue->heap;\ +\ + ASSERT2(FPRFX ## CheckHeap(queue));\ +\ + ASSERT(locator[node] == -1);\ +\ + i = queue->nnodes++;\ + while (i > 0) {\ + j = (i-1)>>1;\ + if (KEY_LT(key, heap[j].key)) {\ + heap[i] = heap[j];\ + locator[heap[i].val] = i;\ + i = j;\ + }\ + else\ + break;\ + }\ + ASSERT(i >= 0);\ + heap[i].key = key;\ + heap[i].val = node;\ + locator[node] = i;\ +\ + ASSERT2(FPRFX ## CheckHeap(queue));\ +\ + return 0;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function deletes an item from the priority queue */\ +/**************************************************************************/\ +int FPRFX ## Delete(PQT *queue, VT node)\ +{\ + gk_idx_t i, j, nnodes;\ + KT newkey, oldkey;\ + gk_idx_t *locator=queue->locator;\ + KVT *heap=queue->heap;\ +\ + ASSERT(locator[node] != -1);\ + ASSERT(heap[locator[node]].val == node);\ +\ + ASSERT2(FPRFX ## CheckHeap(queue));\ +\ + i = locator[node];\ + locator[node] = -1;\ +\ + if (--queue->nnodes > 0 && heap[queue->nnodes].val != node) {\ + node = heap[queue->nnodes].val;\ + newkey = heap[queue->nnodes].key;\ + oldkey = heap[i].key;\ +\ + if (KEY_LT(newkey, oldkey)) { /* Filter-up */\ + while (i > 0) {\ + j = (i-1)>>1;\ + if (KEY_LT(newkey, heap[j].key)) {\ + heap[i] = heap[j];\ + locator[heap[i].val] = i;\ + i = j;\ + }\ + else\ + break;\ + }\ + }\ + else { /* Filter down */\ + nnodes = queue->nnodes;\ + while ((j=(i<<1)+1) < nnodes) {\ + if (KEY_LT(heap[j].key, newkey)) {\ + if (j+1 < nnodes && KEY_LT(heap[j+1].key, heap[j].key))\ + j++;\ + heap[i] = heap[j];\ + locator[heap[i].val] = i;\ + i = j;\ + }\ + else if (j+1 < nnodes && KEY_LT(heap[j+1].key, newkey)) {\ + j++;\ + heap[i] = heap[j];\ + locator[heap[i].val] = i;\ + i = j;\ + }\ + else\ + break;\ + }\ + }\ +\ + heap[i].key = newkey;\ + heap[i].val = node;\ + locator[node] = i;\ + }\ +\ + ASSERT2(FPRFX ## CheckHeap(queue));\ +\ + return 0;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function updates the key values associated for a particular item */ \ +/**************************************************************************/\ +void FPRFX ## Update(PQT *queue, VT node, KT newkey)\ +{\ + gk_idx_t i, j, nnodes;\ + KT oldkey;\ + gk_idx_t *locator=queue->locator;\ + KVT *heap=queue->heap;\ +\ + oldkey = heap[locator[node]].key;\ +\ + ASSERT(locator[node] != -1);\ + ASSERT(heap[locator[node]].val == node);\ + ASSERT2(FPRFX ## CheckHeap(queue));\ +\ + i = locator[node];\ +\ + if (KEY_LT(newkey, oldkey)) { /* Filter-up */\ + while (i > 0) {\ + j = (i-1)>>1;\ + if (KEY_LT(newkey, heap[j].key)) {\ + heap[i] = heap[j];\ + locator[heap[i].val] = i;\ + i = j;\ + }\ + else\ + break;\ + }\ + }\ + else { /* Filter down */\ + nnodes = queue->nnodes;\ + while ((j=(i<<1)+1) < nnodes) {\ + if (KEY_LT(heap[j].key, newkey)) {\ + if (j+1 < nnodes && KEY_LT(heap[j+1].key, heap[j].key))\ + j++;\ + heap[i] = heap[j];\ + locator[heap[i].val] = i;\ + i = j;\ + }\ + else if (j+1 < nnodes && KEY_LT(heap[j+1].key, newkey)) {\ + j++;\ + heap[i] = heap[j];\ + locator[heap[i].val] = i;\ + i = j;\ + }\ + else\ + break;\ + }\ + }\ +\ + heap[i].key = newkey;\ + heap[i].val = node;\ + locator[node] = i;\ +\ + ASSERT2(FPRFX ## CheckHeap(queue));\ +\ + return;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the item at the top of the queue and removes\ + it from the priority queue */\ +/**************************************************************************/\ +VT FPRFX ## GetTop(PQT *queue)\ +{\ + gk_idx_t i, j;\ + gk_idx_t *locator;\ + KVT *heap;\ + VT vtx, node;\ + KT key;\ +\ + ASSERT2(FPRFX ## CheckHeap(queue));\ +\ + if (queue->nnodes == 0)\ + return -1;\ +\ + queue->nnodes--;\ +\ + heap = queue->heap;\ + locator = queue->locator;\ +\ + vtx = heap[0].val;\ + locator[vtx] = -1;\ +\ + if ((i = queue->nnodes) > 0) {\ + key = heap[i].key;\ + node = heap[i].val;\ + i = 0;\ + while ((j=2*i+1) < queue->nnodes) {\ + if (KEY_LT(heap[j].key, key)) {\ + if (j+1 < queue->nnodes && KEY_LT(heap[j+1].key, heap[j].key))\ + j = j+1;\ + heap[i] = heap[j];\ + locator[heap[i].val] = i;\ + i = j;\ + }\ + else if (j+1 < queue->nnodes && KEY_LT(heap[j+1].key, key)) {\ + j = j+1;\ + heap[i] = heap[j];\ + locator[heap[i].val] = i;\ + i = j;\ + }\ + else\ + break;\ + }\ +\ + heap[i].key = key;\ + heap[i].val = node;\ + locator[node] = i;\ + }\ +\ + ASSERT2(FPRFX ## CheckHeap(queue));\ + return vtx;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the item at the top of the queue. The item is not\ + deleted from the queue. */\ +/**************************************************************************/\ +VT FPRFX ## SeeTopVal(PQT *queue)\ +{\ + return (queue->nnodes == 0 ? -1 : queue->heap[0].val);\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the key of the top item. The item is not\ + deleted from the queue. */\ +/**************************************************************************/\ +KT FPRFX ## SeeTopKey(PQT *queue)\ +{\ + return (queue->nnodes == 0 ? KMAX : queue->heap[0].key);\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the key of a specific item */\ +/**************************************************************************/\ +KT FPRFX ## SeeKey(PQT *queue, VT node)\ +{\ + gk_idx_t *locator;\ + KVT *heap;\ +\ + heap = queue->heap;\ + locator = queue->locator;\ +\ + return heap[locator[node]].key;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the first item in a breadth-first traversal of\ + the heap whose key is less than maxwgt. This function is here due to\ + hMETIS and is not general!*/\ +/**************************************************************************/\ +/*\ +VT FPRFX ## SeeConstraintTop(PQT *queue, KT maxwgt, KT *wgts)\ +{\ + gk_idx_t i;\ +\ + if (queue->nnodes == 0)\ + return -1;\ +\ + if (maxwgt <= 1000)\ + return FPRFX ## SeeTopVal(queue);\ +\ + for (i=0; innodes; i++) {\ + if (queue->heap[i].key > 0) {\ + if (wgts[queue->heap[i].val] <= maxwgt)\ + return queue->heap[i].val;\ + }\ + else {\ + if (queue->heap[i/2].key <= 0)\ + break;\ + }\ + }\ +\ + return queue->heap[0].val;\ +\ +}\ +*/\ +\ +\ +/*************************************************************************/\ +/*! This functions checks the consistency of the heap */\ +/**************************************************************************/\ +int FPRFX ## CheckHeap(PQT *queue)\ +{\ + gk_idx_t i, j;\ + size_t nnodes;\ + gk_idx_t *locator;\ + KVT *heap;\ +\ + heap = queue->heap;\ + locator = queue->locator;\ + nnodes = queue->nnodes;\ +\ + if (nnodes == 0)\ + return 1;\ +\ + ASSERT(locator[heap[0].val] == 0);\ + for (i=1; imaxnodes; i++) {\ + if (locator[i] != -1)\ + j++;\ + }\ + ASSERTP(j == nnodes, ("%jd %jd\n", (intmax_t)j, (intmax_t)nnodes));\ +\ + return 1;\ +}\ + + +#define GK_MKPQUEUE_PROTO(FPRFX, PQT, KT, VT)\ + PQT * FPRFX ## Create(size_t maxnodes);\ + void FPRFX ## Init(PQT *queue, size_t maxnodes);\ + void FPRFX ## Reset(PQT *queue);\ + void FPRFX ## Free(PQT *queue);\ + void FPRFX ## Destroy(PQT *queue);\ + size_t FPRFX ## Length(PQT *queue);\ + int FPRFX ## Insert(PQT *queue, VT node, KT key);\ + int FPRFX ## Delete(PQT *queue, VT node);\ + void FPRFX ## Update(PQT *queue, VT node, KT newkey);\ + VT FPRFX ## GetTop(PQT *queue);\ + VT FPRFX ## SeeTopVal(PQT *queue);\ + KT FPRFX ## SeeTopKey(PQT *queue);\ + KT FPRFX ## SeeKey(PQT *queue, VT node);\ + VT FPRFX ## SeeConstraintTop(PQT *queue, KT maxwgt, KT *wgts);\ + int FPRFX ## CheckHeap(PQT *queue);\ + + +/* This is how these macros are used +GK_MKPQUEUE(gk_dkvPQ, gk_dkvPQ_t, double, gk_idx_t, gk_dkvmalloc, DBL_MAX) +GK_MKPQUEUE_PROTO(gk_dkvPQ, gk_dkvPQ_t, double, gk_idx_t) +*/ + + +#endif diff --git a/src/metis/GKlib/gk_mkpqueue2.h b/src/metis/GKlib/gk_mkpqueue2.h new file mode 100644 index 0000000..10e8ee4 --- /dev/null +++ b/src/metis/GKlib/gk_mkpqueue2.h @@ -0,0 +1,215 @@ +/*! +\file gk_mkpqueue2.h +\brief Templates for priority queues that do not utilize locators and as such + they can use different types of values. + +\date Started 4/09/07 +\author George +\version\verbatim $Id: gk_mkpqueue2.h 13005 2012-10-23 22:34:36Z karypis $ \endverbatim +*/ + + +#ifndef _GK_MKPQUEUE2_H +#define _GK_MKPQUEUE2_H + + +#define GK_MKPQUEUE2(FPRFX, PQT, KT, VT, KMALLOC, VMALLOC, KMAX, KEY_LT)\ +/*************************************************************************/\ +/*! This function creates and initializes a priority queue */\ +/**************************************************************************/\ +PQT *FPRFX ## Create2(ssize_t maxnodes)\ +{\ + PQT *queue; \ +\ + if ((queue = (PQT *)gk_malloc(sizeof(PQT), "gk_pqCreate2: queue")) != NULL) {\ + memset(queue, 0, sizeof(PQT));\ + queue->nnodes = 0;\ + queue->maxnodes = maxnodes;\ + queue->keys = KMALLOC(maxnodes, "gk_pqCreate2: keys");\ + queue->vals = VMALLOC(maxnodes, "gk_pqCreate2: vals");\ +\ + if (queue->keys == NULL || queue->vals == NULL)\ + gk_free((void **)&queue->keys, &queue->vals, &queue, LTERM);\ + }\ +\ + return queue;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function resets the priority queue */\ +/**************************************************************************/\ +void FPRFX ## Reset2(PQT *queue)\ +{\ + queue->nnodes = 0;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function frees the internal datastructures of the priority queue */\ +/**************************************************************************/\ +void FPRFX ## Destroy2(PQT **r_queue)\ +{\ + PQT *queue = *r_queue; \ + if (queue == NULL) return;\ + gk_free((void **)&queue->keys, &queue->vals, &queue, LTERM);\ + *r_queue = NULL;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the length of the queue */\ +/**************************************************************************/\ +size_t FPRFX ## Length2(PQT *queue)\ +{\ + return queue->nnodes;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function adds an item in the priority queue. */\ +/**************************************************************************/\ +int FPRFX ## Insert2(PQT *queue, VT val, KT key)\ +{\ + ssize_t i, j;\ + KT *keys=queue->keys;\ + VT *vals=queue->vals;\ +\ + ASSERT2(FPRFX ## CheckHeap2(queue));\ +\ + if (queue->nnodes == queue->maxnodes) \ + return 0;\ +\ + ASSERT2(FPRFX ## CheckHeap2(queue));\ +\ + i = queue->nnodes++;\ + while (i > 0) {\ + j = (i-1)>>1;\ + if (KEY_LT(key, keys[j])) {\ + keys[i] = keys[j];\ + vals[i] = vals[j];\ + i = j;\ + }\ + else\ + break;\ + }\ + ASSERT(i >= 0);\ + keys[i] = key;\ + vals[i] = val;\ +\ + ASSERT2(FPRFX ## CheckHeap2(queue));\ +\ + return 1;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the item at the top of the queue and removes\ + it from the priority queue */\ +/**************************************************************************/\ +int FPRFX ## GetTop2(PQT *queue, VT *r_val)\ +{\ + ssize_t i, j;\ + KT key, *keys=queue->keys;\ + VT val, *vals=queue->vals;\ +\ + ASSERT2(FPRFX ## CheckHeap2(queue));\ +\ + if (queue->nnodes == 0)\ + return 0;\ +\ + queue->nnodes--;\ +\ + *r_val = vals[0];\ +\ + if ((i = queue->nnodes) > 0) {\ + key = keys[i];\ + val = vals[i];\ + i = 0;\ + while ((j=2*i+1) < queue->nnodes) {\ + if (KEY_LT(keys[j], key)) {\ + if (j+1 < queue->nnodes && KEY_LT(keys[j+1], keys[j]))\ + j = j+1;\ + keys[i] = keys[j];\ + vals[i] = vals[j];\ + i = j;\ + }\ + else if (j+1 < queue->nnodes && KEY_LT(keys[j+1], key)) {\ + j = j+1;\ + keys[i] = keys[j];\ + vals[i] = vals[j];\ + i = j;\ + }\ + else\ + break;\ + }\ +\ + keys[i] = key;\ + vals[i] = val;\ + }\ +\ + ASSERT2(FPRFX ## CheckHeap2(queue));\ +\ + return 1;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the item at the top of the queue. The item is not\ + deleted from the queue. */\ +/**************************************************************************/\ +int FPRFX ## SeeTopVal2(PQT *queue, VT *r_val)\ +{\ + if (queue->nnodes == 0) \ + return 0;\ +\ + *r_val = queue->vals[0];\ +\ + return 1;\ +}\ +\ +\ +/*************************************************************************/\ +/*! This function returns the key of the top item. The item is not\ + deleted from the queue. */\ +/**************************************************************************/\ +KT FPRFX ## SeeTopKey2(PQT *queue)\ +{\ + return (queue->nnodes == 0 ? KMAX : queue->keys[0]);\ +}\ +\ +\ +/*************************************************************************/\ +/*! This functions checks the consistency of the heap */\ +/**************************************************************************/\ +int FPRFX ## CheckHeap2(PQT *queue)\ +{\ + ssize_t i;\ + KT *keys=queue->keys;\ +\ + if (queue->nnodes == 0)\ + return 1;\ +\ + for (i=1; innodes; i++) {\ + ASSERT(!KEY_LT(keys[i], keys[(i-1)/2]));\ + }\ + for (i=1; innodes; i++)\ + ASSERT(!KEY_LT(keys[i], keys[0]));\ +\ + return 1;\ +}\ + + +#define GK_MKPQUEUE2_PROTO(FPRFX, PQT, KT, VT)\ + PQT * FPRFX ## Create2(ssize_t maxnodes);\ + void FPRFX ## Reset2(PQT *queue);\ + void FPRFX ## Destroy2(PQT **r_queue);\ + size_t FPRFX ## Length2(PQT *queue);\ + int FPRFX ## Insert2(PQT *queue, VT node, KT key);\ + int FPRFX ## GetTop2(PQT *queue, VT *r_val);\ + int FPRFX ## SeeTopVal2(PQT *queue, VT *r_val);\ + KT FPRFX ## SeeTopKey2(PQT *queue);\ + int FPRFX ## CheckHeap2(PQT *queue);\ + + +#endif diff --git a/src/metis/GKlib/gk_mkrandom.h b/src/metis/GKlib/gk_mkrandom.h new file mode 100644 index 0000000..68d54fa --- /dev/null +++ b/src/metis/GKlib/gk_mkrandom.h @@ -0,0 +1,123 @@ +/*! +\file +\brief Templates for portable random number generation + +\date Started 5/17/07 +\author George +\version\verbatim $Id: gk_mkrandom.h 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + + +#ifndef _GK_MKRANDOM_H +#define _GK_MKRANDOM_H + +/*************************************************************************/\ +/*! The generator for the rand() related routines. \ + \params RNGT the datatype that defines the range of values over which\ + random numbers will be generated\ + \params VALT the datatype that defines the contents of the array to \ + be permuted by randArrayPermute() \ + \params FPRFX the function prefix \ +*/\ +/**************************************************************************/\ +#define GK_MKRANDOM(FPRFX, RNGT, VALT)\ +/*************************************************************************/\ +/*! Initializes the generator */ \ +/**************************************************************************/\ +void FPRFX ## srand(RNGT seed) \ +{\ + gk_randinit((uint64_t) seed);\ +}\ +\ +\ +/*************************************************************************/\ +/*! Returns a random number */ \ +/**************************************************************************/\ +RNGT FPRFX ## rand() \ +{\ + if (sizeof(RNGT) <= sizeof(int32_t)) \ + return (RNGT)gk_randint32(); \ + else \ + return (RNGT)gk_randint64(); \ +}\ +\ +\ +/*************************************************************************/\ +/*! Returns a random number between [0, max) */ \ +/**************************************************************************/\ +RNGT FPRFX ## randInRange(RNGT max) \ +{\ + return (RNGT)((FPRFX ## rand())%max); \ +}\ +\ +\ +/*************************************************************************/\ +/*! Randomly permutes the elements of an array p[]. \ + flag == 1, p[i] = i prior to permutation, \ + flag == 0, p[] is not initialized. */\ +/**************************************************************************/\ +void FPRFX ## randArrayPermute(RNGT n, VALT *p, RNGT nshuffles, int flag)\ +{\ + RNGT i, u, v;\ + VALT tmp;\ +\ + if (flag == 1) {\ + for (i=0; ikey < (b)->key) + * GKQSORT(struct elt, arr, n, elt_lt); + * } + * + * And so on. + */ + +/* Swap two items pointed to by A and B using temporary buffer t. */ +#define _GKQSORT_SWAP(a, b, t) ((void)((t = *a), (*a = *b), (*b = t))) + +/* Discontinue quicksort algorithm when partition gets below this size. + This particular magic number was chosen to work best on a Sun 4/260. */ +#define _GKQSORT_MAX_THRESH 4 + +/* The next 4 #defines implement a very fast in-line stack abstraction. */ +#define _GKQSORT_STACK_SIZE (8 * sizeof(size_t)) +#define _GKQSORT_PUSH(top, low, high) (((top->_lo = (low)), (top->_hi = (high)), ++top)) +#define _GKQSORT_POP(low, high, top) ((--top, (low = top->_lo), (high = top->_hi))) +#define _GKQSORT_STACK_NOT_EMPTY (_stack < _top) + + +/* The main code starts here... */ +#define GK_MKQSORT(GKQSORT_TYPE,GKQSORT_BASE,GKQSORT_NELT,GKQSORT_LT) \ +{ \ + GKQSORT_TYPE *const _base = (GKQSORT_BASE); \ + const size_t _elems = (GKQSORT_NELT); \ + GKQSORT_TYPE _hold; \ + \ + if (_elems == 0) \ + return; \ + \ + /* Don't declare two variables of type GKQSORT_TYPE in a single \ + * statement: eg `TYPE a, b;', in case if TYPE is a pointer, \ + * expands to `type* a, b;' wich isn't what we want. \ + */ \ + \ + if (_elems > _GKQSORT_MAX_THRESH) { \ + GKQSORT_TYPE *_lo = _base; \ + GKQSORT_TYPE *_hi = _lo + _elems - 1; \ + struct { \ + GKQSORT_TYPE *_hi; GKQSORT_TYPE *_lo; \ + } _stack[_GKQSORT_STACK_SIZE], *_top = _stack + 1; \ + \ + while (_GKQSORT_STACK_NOT_EMPTY) { \ + GKQSORT_TYPE *_left_ptr; GKQSORT_TYPE *_right_ptr; \ + \ + /* Select median value from among LO, MID, and HI. Rearrange \ + LO and HI so the three values are sorted. This lowers the \ + probability of picking a pathological pivot value and \ + skips a comparison for both the LEFT_PTR and RIGHT_PTR in \ + the while loops. */ \ + \ + GKQSORT_TYPE *_mid = _lo + ((_hi - _lo) >> 1); \ + \ + if (GKQSORT_LT (_mid, _lo)) \ + _GKQSORT_SWAP (_mid, _lo, _hold); \ + if (GKQSORT_LT (_hi, _mid)) \ + _GKQSORT_SWAP (_mid, _hi, _hold); \ + else \ + goto _jump_over; \ + if (GKQSORT_LT (_mid, _lo)) \ + _GKQSORT_SWAP (_mid, _lo, _hold); \ + _jump_over:; \ + \ + _left_ptr = _lo + 1; \ + _right_ptr = _hi - 1; \ + \ + /* Here's the famous ``collapse the walls'' section of quicksort. \ + Gotta like those tight inner loops! They are the main reason \ + that this algorithm runs much faster than others. */ \ + do { \ + while (GKQSORT_LT (_left_ptr, _mid)) \ + ++_left_ptr; \ + \ + while (GKQSORT_LT (_mid, _right_ptr)) \ + --_right_ptr; \ + \ + if (_left_ptr < _right_ptr) { \ + _GKQSORT_SWAP (_left_ptr, _right_ptr, _hold); \ + if (_mid == _left_ptr) \ + _mid = _right_ptr; \ + else if (_mid == _right_ptr) \ + _mid = _left_ptr; \ + ++_left_ptr; \ + --_right_ptr; \ + } \ + else if (_left_ptr == _right_ptr) { \ + ++_left_ptr; \ + --_right_ptr; \ + break; \ + } \ + } while (_left_ptr <= _right_ptr); \ + \ + /* Set up pointers for next iteration. First determine whether \ + left and right partitions are below the threshold size. If so, \ + ignore one or both. Otherwise, push the larger partition's \ + bounds on the stack and continue sorting the smaller one. */ \ + \ + if (_right_ptr - _lo <= _GKQSORT_MAX_THRESH) { \ + if (_hi - _left_ptr <= _GKQSORT_MAX_THRESH) \ + /* Ignore both small partitions. */ \ + _GKQSORT_POP (_lo, _hi, _top); \ + else \ + /* Ignore small left partition. */ \ + _lo = _left_ptr; \ + } \ + else if (_hi - _left_ptr <= _GKQSORT_MAX_THRESH) \ + /* Ignore small right partition. */ \ + _hi = _right_ptr; \ + else if (_right_ptr - _lo > _hi - _left_ptr) { \ + /* Push larger left partition indices. */ \ + _GKQSORT_PUSH (_top, _lo, _right_ptr); \ + _lo = _left_ptr; \ + } \ + else { \ + /* Push larger right partition indices. */ \ + _GKQSORT_PUSH (_top, _left_ptr, _hi); \ + _hi = _right_ptr; \ + } \ + } \ + } \ + \ + /* Once the BASE array is partially sorted by quicksort the rest \ + is completely sorted using insertion sort, since this is efficient \ + for partitions below MAX_THRESH size. BASE points to the \ + beginning of the array to sort, and END_PTR points at the very \ + last element in the array (*not* one beyond it!). */ \ + \ + { \ + GKQSORT_TYPE *const _end_ptr = _base + _elems - 1; \ + GKQSORT_TYPE *_tmp_ptr = _base; \ + register GKQSORT_TYPE *_run_ptr; \ + GKQSORT_TYPE *_thresh; \ + \ + _thresh = _base + _GKQSORT_MAX_THRESH; \ + if (_thresh > _end_ptr) \ + _thresh = _end_ptr; \ + \ + /* Find smallest element in first threshold and place it at the \ + array's beginning. This is the smallest array element, \ + and the operation speeds up insertion sort's inner loop. */ \ + \ + for (_run_ptr = _tmp_ptr + 1; _run_ptr <= _thresh; ++_run_ptr) \ + if (GKQSORT_LT (_run_ptr, _tmp_ptr)) \ + _tmp_ptr = _run_ptr; \ + \ + if (_tmp_ptr != _base) \ + _GKQSORT_SWAP (_tmp_ptr, _base, _hold); \ + \ + /* Insertion sort, running from left-hand-side \ + * up to right-hand-side. */ \ + \ + _run_ptr = _base + 1; \ + while (++_run_ptr <= _end_ptr) { \ + _tmp_ptr = _run_ptr - 1; \ + while (GKQSORT_LT (_run_ptr, _tmp_ptr)) \ + --_tmp_ptr; \ + \ + ++_tmp_ptr; \ + if (_tmp_ptr != _run_ptr) { \ + GKQSORT_TYPE *_trav = _run_ptr + 1; \ + while (--_trav >= _run_ptr) { \ + GKQSORT_TYPE *_hi; GKQSORT_TYPE *_lo; \ + _hold = *_trav; \ + \ + for (_hi = _lo = _trav; --_lo >= _tmp_ptr; _hi = _lo) \ + *_hi = *_lo; \ + *_hi = _hold; \ + } \ + } \ + } \ + } \ + \ +} + +#endif diff --git a/src/metis/GKlib/gk_mkutils.h b/src/metis/GKlib/gk_mkutils.h new file mode 100644 index 0000000..a092f22 --- /dev/null +++ b/src/metis/GKlib/gk_mkutils.h @@ -0,0 +1,40 @@ +/*! +\file +\brief Templates for various utility routines + +\date Started 5/28/07 +\author George +\version\verbatim $Id: gk_mkutils.h 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + +#ifndef _GK_MKUTILS_H_ +#define _GK_MKUTILS_H_ + + +#define GK_MKARRAY2CSR(PRFX, TYPE)\ +/*************************************************************************/\ +/*! The macro for gk_?array2csr() routine */\ +/**************************************************************************/\ +void PRFX ## array2csr(TYPE n, TYPE range, TYPE *array, TYPE *ptr, TYPE *ind)\ +{\ + TYPE i;\ +\ + for (i=0; i<=range; i++)\ + ptr[i] = 0;\ +\ + for (i=0; i>1)-2) + +#define PRIGKIDX "zd" +#define SCNGKIDX "zd" + + +#endif diff --git a/src/metis/GKlib/gkregex.c b/src/metis/GKlib/gkregex.c new file mode 100644 index 0000000..8a09caa --- /dev/null +++ b/src/metis/GKlib/gkregex.c @@ -0,0 +1,10704 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* this is for removing a compiler warning */ +void gkfooo() { return; } + +#ifdef USE_GKREGEX + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _LIBC +/* We have to keep the namespace clean. */ +# define regfree(preg) __regfree (preg) +# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef) +# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags) +# define regerror(errcode, preg, errbuf, errbuf_size) \ + __regerror(errcode, preg, errbuf, errbuf_size) +# define re_set_registers(bu, re, nu, st, en) \ + __re_set_registers (bu, re, nu, st, en) +# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \ + __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) +# define re_match(bufp, string, size, pos, regs) \ + __re_match (bufp, string, size, pos, regs) +# define re_search(bufp, string, size, startpos, range, regs) \ + __re_search (bufp, string, size, startpos, range, regs) +# define re_compile_pattern(pattern, length, bufp) \ + __re_compile_pattern (pattern, length, bufp) +# define re_set_syntax(syntax) __re_set_syntax (syntax) +# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \ + __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop) +# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp) + +# include "../locale/localeinfo.h" +#endif + +#include "GKlib.h" + + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* GKINCLUDE #include "regex_internal.h" */ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _REGEX_INTERNAL_H +#define _REGEX_INTERNAL_H 1 + +#include +#include +#include +#include +#include + +#if defined(__MINGW32_VERSION) || defined(_MSC_VER) +#define strcasecmp stricmp +#endif + +#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC +# include +#endif +#if defined HAVE_LOCALE_H || defined _LIBC +# include +#endif +#if defined HAVE_WCHAR_H || defined _LIBC +# include +#endif /* HAVE_WCHAR_H || _LIBC */ +#if defined HAVE_WCTYPE_H || defined _LIBC +# include +#endif /* HAVE_WCTYPE_H || _LIBC */ +#if defined HAVE_STDBOOL_H || defined _LIBC +# include +#else +typedef enum { false, true } bool; +#endif /* HAVE_STDBOOL_H || _LIBC */ +#if defined HAVE_STDINT_H || defined _LIBC +# include +#endif /* HAVE_STDINT_H || _LIBC */ +#if defined _LIBC +# include +#else +# define __libc_lock_define(CLASS,NAME) +# define __libc_lock_init(NAME) do { } while (0) +# define __libc_lock_lock(NAME) do { } while (0) +# define __libc_lock_unlock(NAME) do { } while (0) +#endif + +/* In case that the system doesn't have isblank(). */ +#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank +# define isblank(ch) ((ch) == ' ' || (ch) == '\t') +#endif + +#ifdef _LIBC +# ifndef _RE_DEFINE_LOCALE_FUNCTIONS +# define _RE_DEFINE_LOCALE_FUNCTIONS 1 +# include +# include +# include +# endif +#endif + +/* This is for other GNU distributions with internationalized messages. */ +#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC +# include +# ifdef _LIBC +# undef gettext +# define gettext(msgid) \ + INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES) +# endif +#else +# define gettext(msgid) (msgid) +#endif + +#ifndef gettext_noop +/* This define is so xgettext can find the internationalizable + strings. */ +# define gettext_noop(String) String +#endif + +/* For loser systems without the definition. */ +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_WCRTOMB && HAVE_MBRTOWC && HAVE_WCSCOLL) || _LIBC +# define RE_ENABLE_I18N +#endif + +#if __GNUC__ >= 3 +# define BE(expr, val) __builtin_expect (expr, val) +#else +# define BE(expr, val) (expr) +# define inline +#endif + +/* Number of single byte character. */ +#define SBC_MAX 256 + +#define COLL_ELEM_LEN_MAX 8 + +/* The character which represents newline. */ +#define NEWLINE_CHAR '\n' +#define WIDE_NEWLINE_CHAR L'\n' + +/* Rename to standard API for using out of glibc. */ +#ifndef _LIBC +# define __wctype wctype +# define __iswctype iswctype +# define __btowc btowc +# define __mempcpy mempcpy +# define __wcrtomb wcrtomb +# define __regfree regfree +# define attribute_hidden +#endif /* not _LIBC */ + +#ifdef __GNUC__ +# define __attribute(arg) __attribute__ (arg) +#else +# define __attribute(arg) +#endif + +extern const char __re_error_msgid[] attribute_hidden; +extern const size_t __re_error_msgid_idx[] attribute_hidden; + +/* An integer used to represent a set of bits. It must be unsigned, + and must be at least as wide as unsigned int. */ +typedef unsigned long int bitset_word_t; +/* All bits set in a bitset_word_t. */ +#define BITSET_WORD_MAX ULONG_MAX +/* Number of bits in a bitset_word_t. */ +#define BITSET_WORD_BITS (sizeof (bitset_word_t) * CHAR_BIT) +/* Number of bitset_word_t in a bit_set. */ +#define BITSET_WORDS (SBC_MAX / BITSET_WORD_BITS) +typedef bitset_word_t bitset_t[BITSET_WORDS]; +typedef bitset_word_t *re_bitset_ptr_t; +typedef const bitset_word_t *re_const_bitset_ptr_t; + +#define bitset_set(set,i) \ + (set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS) +#define bitset_clear(set,i) \ + (set[i / BITSET_WORD_BITS] &= ~((bitset_word_t) 1 << i % BITSET_WORD_BITS)) +#define bitset_contain(set,i) \ + (set[i / BITSET_WORD_BITS] & ((bitset_word_t) 1 << i % BITSET_WORD_BITS)) +#define bitset_empty(set) memset (set, '\0', sizeof (bitset_t)) +#define bitset_set_all(set) memset (set, '\xff', sizeof (bitset_t)) +#define bitset_copy(dest,src) memcpy (dest, src, sizeof (bitset_t)) + +#define PREV_WORD_CONSTRAINT 0x0001 +#define PREV_NOTWORD_CONSTRAINT 0x0002 +#define NEXT_WORD_CONSTRAINT 0x0004 +#define NEXT_NOTWORD_CONSTRAINT 0x0008 +#define PREV_NEWLINE_CONSTRAINT 0x0010 +#define NEXT_NEWLINE_CONSTRAINT 0x0020 +#define PREV_BEGBUF_CONSTRAINT 0x0040 +#define NEXT_ENDBUF_CONSTRAINT 0x0080 +#define WORD_DELIM_CONSTRAINT 0x0100 +#define NOT_WORD_DELIM_CONSTRAINT 0x0200 + +typedef enum +{ + INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, + INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, + LINE_FIRST = PREV_NEWLINE_CONSTRAINT, + LINE_LAST = NEXT_NEWLINE_CONSTRAINT, + BUF_FIRST = PREV_BEGBUF_CONSTRAINT, + BUF_LAST = NEXT_ENDBUF_CONSTRAINT, + WORD_DELIM = WORD_DELIM_CONSTRAINT, + NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT +} re_context_type; + +typedef struct +{ + int alloc; + int nelem; + int *elems; +} re_node_set; + +typedef enum +{ + NON_TYPE = 0, + + /* Node type, These are used by token, node, tree. */ + CHARACTER = 1, + END_OF_RE = 2, + SIMPLE_BRACKET = 3, + OP_BACK_REF = 4, + OP_PERIOD = 5, +#ifdef RE_ENABLE_I18N + COMPLEX_BRACKET = 6, + OP_UTF8_PERIOD = 7, +#endif /* RE_ENABLE_I18N */ + + /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used + when the debugger shows values of this enum type. */ +#define EPSILON_BIT 8 + OP_OPEN_SUBEXP = EPSILON_BIT | 0, + OP_CLOSE_SUBEXP = EPSILON_BIT | 1, + OP_ALT = EPSILON_BIT | 2, + OP_DUP_ASTERISK = EPSILON_BIT | 3, + ANCHOR = EPSILON_BIT | 4, + + /* Tree type, these are used only by tree. */ + CONCAT = 16, + SUBEXP = 17, + + /* Token type, these are used only by token. */ + OP_DUP_PLUS = 18, + OP_DUP_QUESTION, + OP_OPEN_BRACKET, + OP_CLOSE_BRACKET, + OP_CHARSET_RANGE, + OP_OPEN_DUP_NUM, + OP_CLOSE_DUP_NUM, + OP_NON_MATCH_LIST, + OP_OPEN_COLL_ELEM, + OP_CLOSE_COLL_ELEM, + OP_OPEN_EQUIV_CLASS, + OP_CLOSE_EQUIV_CLASS, + OP_OPEN_CHAR_CLASS, + OP_CLOSE_CHAR_CLASS, + OP_WORD, + OP_NOTWORD, + OP_SPACE, + OP_NOTSPACE, + BACK_SLASH + +} re_token_type_t; + +#ifdef RE_ENABLE_I18N +typedef struct +{ + /* Multibyte characters. */ + wchar_t *mbchars; + + /* Collating symbols. */ +# ifdef _LIBC + int32_t *coll_syms; +# endif + + /* Equivalence classes. */ +# ifdef _LIBC + int32_t *equiv_classes; +# endif + + /* Range expressions. */ +# ifdef _LIBC + uint32_t *range_starts; + uint32_t *range_ends; +# else /* not _LIBC */ + wchar_t *range_starts; + wchar_t *range_ends; +# endif /* not _LIBC */ + + /* Character classes. */ + wctype_t *char_classes; + + /* If this character set is the non-matching list. */ + unsigned int non_match : 1; + + /* # of multibyte characters. */ + int nmbchars; + + /* # of collating symbols. */ + int ncoll_syms; + + /* # of equivalence classes. */ + int nequiv_classes; + + /* # of range expressions. */ + int nranges; + + /* # of character classes. */ + int nchar_classes; +} re_charset_t; +#endif /* RE_ENABLE_I18N */ + +typedef struct +{ + union + { + unsigned char c; /* for CHARACTER */ + re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */ +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; /* for COMPLEX_BRACKET */ +#endif /* RE_ENABLE_I18N */ + int idx; /* for BACK_REF */ + re_context_type ctx_type; /* for ANCHOR */ + } opr; +#if __GNUC__ >= 2 + re_token_type_t type : 8; +#else + re_token_type_t type; +#endif + unsigned int constraint : 10; /* context constraint */ + unsigned int duplicated : 1; + unsigned int opt_subexp : 1; +#ifdef RE_ENABLE_I18N + unsigned int accept_mb : 1; + /* These 2 bits can be moved into the union if needed (e.g. if running out + of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */ + unsigned int mb_partial : 1; +#endif + unsigned int word_char : 1; +} re_token_t; + +#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT) + +struct re_string_t +{ + /* Indicate the raw buffer which is the original string passed as an + argument of regexec(), re_search(), etc.. */ + const unsigned char *raw_mbs; + /* Store the multibyte string. In case of "case insensitive mode" like + REG_ICASE, upper cases of the string are stored, otherwise MBS points + the same address that RAW_MBS points. */ + unsigned char *mbs; +#ifdef RE_ENABLE_I18N + /* Store the wide character string which is corresponding to MBS. */ + wint_t *wcs; + int *offsets; + mbstate_t cur_state; +#endif + /* Index in RAW_MBS. Each character mbs[i] corresponds to + raw_mbs[raw_mbs_idx + i]. */ + int raw_mbs_idx; + /* The length of the valid characters in the buffers. */ + int valid_len; + /* The corresponding number of bytes in raw_mbs array. */ + int valid_raw_len; + /* The length of the buffers MBS and WCS. */ + int bufs_len; + /* The index in MBS, which is updated by re_string_fetch_byte. */ + int cur_idx; + /* length of RAW_MBS array. */ + int raw_len; + /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */ + int len; + /* End of the buffer may be shorter than its length in the cases such + as re_match_2, re_search_2. Then, we use STOP for end of the buffer + instead of LEN. */ + int raw_stop; + /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */ + int stop; + + /* The context of mbs[0]. We store the context independently, since + the context of mbs[0] may be different from raw_mbs[0], which is + the beginning of the input string. */ + unsigned int tip_context; + /* The translation passed as a part of an argument of re_compile_pattern. */ + RE_TRANSLATE_TYPE trans; + /* Copy of re_dfa_t's word_char. */ + re_const_bitset_ptr_t word_char; + /* 1 if REG_ICASE. */ + unsigned char icase; + unsigned char is_utf8; + unsigned char map_notascii; + unsigned char mbs_allocated; + unsigned char offsets_needed; + unsigned char newline_anchor; + unsigned char word_ops_used; + int mb_cur_max; +}; +typedef struct re_string_t re_string_t; + + +struct re_dfa_t; +typedef struct re_dfa_t re_dfa_t; + +#ifndef _LIBC +# ifdef __i386__ +# define internal_function __attribute ((regparm (3), stdcall)) +# else +# define internal_function +# endif +#endif + +static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr, + int new_buf_len) + internal_function; +#ifdef RE_ENABLE_I18N +static void build_wcs_buffer (re_string_t *pstr) internal_function; +static int build_wcs_upper_buffer (re_string_t *pstr) internal_function; +#endif /* RE_ENABLE_I18N */ +static void build_upper_buffer (re_string_t *pstr) internal_function; +static void re_string_translate_buffer (re_string_t *pstr) internal_function; +static unsigned int re_string_context_at (const re_string_t *input, int idx, + int eflags) + internal_function __attribute ((pure)); +#define re_string_peek_byte(pstr, offset) \ + ((pstr)->mbs[(pstr)->cur_idx + offset]) +#define re_string_fetch_byte(pstr) \ + ((pstr)->mbs[(pstr)->cur_idx++]) +#define re_string_first_byte(pstr, idx) \ + ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF) +#define re_string_is_single_byte_char(pstr, idx) \ + ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \ + || (pstr)->wcs[(idx) + 1] != WEOF)) +#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx) +#define re_string_cur_idx(pstr) ((pstr)->cur_idx) +#define re_string_get_buffer(pstr) ((pstr)->mbs) +#define re_string_length(pstr) ((pstr)->len) +#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx]) +#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx)) +#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx)) + +#ifdef __GNUC__ +# define alloca(size) __builtin_alloca (size) +# define HAVE_ALLOCA 1 +#elif defined(_MSC_VER) +# include +# define alloca _alloca +# define HAVE_ALLOCA 1 +#else +# error No alloca() +#endif + +#ifndef _LIBC +# if HAVE_ALLOCA +/* The OS usually guarantees only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + allocate anything larger than 4096 bytes. Also care for the possibility + of a few compiler-allocated temporary stack slots. */ +# define __libc_use_alloca(n) ((n) < 4032) +# else +/* alloca is implemented with malloc, so just use malloc. */ +# define __libc_use_alloca(n) 0 +# endif +#endif + +#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t))) +#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t))) +#define re_free(p) free (p) + +struct bin_tree_t +{ + struct bin_tree_t *parent; + struct bin_tree_t *left; + struct bin_tree_t *right; + struct bin_tree_t *first; + struct bin_tree_t *next; + + re_token_t token; + + /* `node_idx' is the index in dfa->nodes, if `type' == 0. + Otherwise `type' indicate the type of this node. */ + int node_idx; +}; +typedef struct bin_tree_t bin_tree_t; + +#define BIN_TREE_STORAGE_SIZE \ + ((1024 - sizeof (void *)) / sizeof (bin_tree_t)) + +struct bin_tree_storage_t +{ + struct bin_tree_storage_t *next; + bin_tree_t data[BIN_TREE_STORAGE_SIZE]; +}; +typedef struct bin_tree_storage_t bin_tree_storage_t; + +#define CONTEXT_WORD 1 +#define CONTEXT_NEWLINE (CONTEXT_WORD << 1) +#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1) +#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1) + +#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD) +#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE) +#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF) +#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF) +#define IS_ORDINARY_CONTEXT(c) ((c) == 0) + +#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_') +#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR) +#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_') +#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR) + +#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \ + ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\ + || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context))) + +#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \ + ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \ + || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context))) + +struct re_dfastate_t +{ + unsigned int hash; + re_node_set nodes; + re_node_set non_eps_nodes; + re_node_set inveclosure; + re_node_set *entrance_nodes; + struct re_dfastate_t **trtable, **word_trtable; + unsigned int context : 4; + unsigned int halt : 1; + /* If this state can accept `multi byte'. + Note that we refer to multibyte characters, and multi character + collating elements as `multi byte'. */ + unsigned int accept_mb : 1; + /* If this state has backreference node(s). */ + unsigned int has_backref : 1; + unsigned int has_constraint : 1; +}; +typedef struct re_dfastate_t re_dfastate_t; + +struct re_state_table_entry +{ + int num; + int alloc; + re_dfastate_t **array; +}; + +/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */ + +typedef struct +{ + int next_idx; + int alloc; + re_dfastate_t **array; +} state_array_t; + +/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */ + +typedef struct +{ + int node; + int str_idx; /* The position NODE match at. */ + state_array_t path; +} re_sub_match_last_t; + +/* Store information about the node NODE whose type is OP_OPEN_SUBEXP. + And information about the node, whose type is OP_CLOSE_SUBEXP, + corresponding to NODE is stored in LASTS. */ + +typedef struct +{ + int str_idx; + int node; + state_array_t *path; + int alasts; /* Allocation size of LASTS. */ + int nlasts; /* The number of LASTS. */ + re_sub_match_last_t **lasts; +} re_sub_match_top_t; + +struct re_backref_cache_entry +{ + int node; + int str_idx; + int subexp_from; + int subexp_to; + char more; + char unused; + unsigned short int eps_reachable_subexps_map; +}; + +typedef struct +{ + /* The string object corresponding to the input string. */ + re_string_t input; +#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) + const re_dfa_t *const dfa; +#else + const re_dfa_t *dfa; +#endif + /* EFLAGS of the argument of regexec. */ + int eflags; + /* Where the matching ends. */ + int match_last; + int last_node; + /* The state log used by the matcher. */ + re_dfastate_t **state_log; + int state_log_top; + /* Back reference cache. */ + int nbkref_ents; + int abkref_ents; + struct re_backref_cache_entry *bkref_ents; + int max_mb_elem_len; + int nsub_tops; + int asub_tops; + re_sub_match_top_t **sub_tops; +} re_match_context_t; + +typedef struct +{ + re_dfastate_t **sifted_states; + re_dfastate_t **limited_states; + int last_node; + int last_str_idx; + re_node_set limits; +} re_sift_context_t; + +struct re_fail_stack_ent_t +{ + int idx; + int node; + regmatch_t *regs; + re_node_set eps_via_nodes; +}; + +struct re_fail_stack_t +{ + int num; + int alloc; + struct re_fail_stack_ent_t *stack; +}; + +struct re_dfa_t +{ + re_token_t *nodes; + size_t nodes_alloc; + size_t nodes_len; + int *nexts; + int *org_indices; + re_node_set *edests; + re_node_set *eclosures; + re_node_set *inveclosures; + struct re_state_table_entry *state_table; + re_dfastate_t *init_state; + re_dfastate_t *init_state_word; + re_dfastate_t *init_state_nl; + re_dfastate_t *init_state_begbuf; + bin_tree_t *str_tree; + bin_tree_storage_t *str_tree_storage; + re_bitset_ptr_t sb_char; + int str_tree_storage_idx; + + /* number of subexpressions `re_nsub' is in regex_t. */ + unsigned int state_hash_mask; + int init_node; + int nbackref; /* The number of backreference in this dfa. */ + + /* Bitmap expressing which backreference is used. */ + bitset_word_t used_bkref_map; + bitset_word_t completed_bkref_map; + + unsigned int has_plural_match : 1; + /* If this dfa has "multibyte node", which is a backreference or + a node which can accept multibyte character or multi character + collating element. */ + unsigned int has_mb_node : 1; + unsigned int is_utf8 : 1; + unsigned int map_notascii : 1; + unsigned int word_ops_used : 1; + int mb_cur_max; + bitset_t word_char; + reg_syntax_t syntax; + int *subexp_map; +#ifdef DEBUG + char* re_str; +#endif + __libc_lock_define (, lock) +}; + +#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set)) +#define re_node_set_remove(set,id) \ + (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1)) +#define re_node_set_empty(p) ((p)->nelem = 0) +#define re_node_set_free(set) re_free ((set)->elems) + + +typedef enum +{ + SB_CHAR, + MB_CHAR, + EQUIV_CLASS, + COLL_SYM, + CHAR_CLASS +} bracket_elem_type; + +typedef struct +{ + bracket_elem_type type; + union + { + unsigned char ch; + unsigned char *name; + wchar_t wch; + } opr; +} bracket_elem_t; + + +/* Inline functions for bitset operation. */ +static inline void +bitset_not (bitset_t set) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) + set[bitset_i] = ~set[bitset_i]; +} + +static inline void +bitset_merge (bitset_t dest, const bitset_t src) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) + dest[bitset_i] |= src[bitset_i]; +} + +static inline void +bitset_mask (bitset_t dest, const bitset_t src) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) + dest[bitset_i] &= src[bitset_i]; +} + +#ifdef RE_ENABLE_I18N +/* Inline functions for re_string. */ +static inline int +internal_function __attribute ((pure)) +re_string_char_size_at (const re_string_t *pstr, int idx) +{ + int byte_idx; + if (pstr->mb_cur_max == 1) + return 1; + for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx) + if (pstr->wcs[idx + byte_idx] != WEOF) + break; + return byte_idx; +} + +static inline wint_t +internal_function __attribute ((pure)) +re_string_wchar_at (const re_string_t *pstr, int idx) +{ + if (pstr->mb_cur_max == 1) + return (wint_t) pstr->mbs[idx]; + return (wint_t) pstr->wcs[idx]; +} + +static int +internal_function __attribute ((pure)) +re_string_elem_size_at (const re_string_t *pstr, int idx) +{ +# ifdef _LIBC + const unsigned char *p, *extra; + const int32_t *table, *indirect; + int32_t tmp; +# include + uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + + if (nrules != 0) + { + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + p = pstr->mbs + idx; + tmp = findidx (&p); + return p - pstr->mbs - idx; + } + else +# endif /* _LIBC */ + return 1; +} +#endif /* RE_ENABLE_I18N */ + +#endif /* _REGEX_INTERNAL_H */ + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* GKINCLUDE #include "regex_internal.c" */ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +static void re_string_construct_common (const char *str, int len, + re_string_t *pstr, + RE_TRANSLATE_TYPE trans, int icase, + const re_dfa_t *dfa) internal_function; +static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int hash) internal_function; +static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int context, + unsigned int hash) internal_function; + +/* Functions for string operation. */ + +/* This function allocate the buffers. It is necessary to call + re_string_reconstruct before using the object. */ + +static reg_errcode_t +internal_function +re_string_allocate (re_string_t *pstr, const char *str, int len, int init_len, + RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa) +{ + reg_errcode_t ret; + int init_buf_len; + + /* Ensure at least one character fits into the buffers. */ + if (init_len < dfa->mb_cur_max) + init_len = dfa->mb_cur_max; + init_buf_len = (len + 1 < init_len) ? len + 1: init_len; + re_string_construct_common (str, len, pstr, trans, icase, dfa); + + ret = re_string_realloc_buffers (pstr, init_buf_len); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + pstr->word_char = dfa->word_char; + pstr->word_ops_used = dfa->word_ops_used; + pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; + pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len; + pstr->valid_raw_len = pstr->valid_len; + return REG_NOERROR; +} + +/* This function allocate the buffers, and initialize them. */ + +static reg_errcode_t +internal_function +re_string_construct (re_string_t *pstr, const char *str, int len, + RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa) +{ + reg_errcode_t ret; + memset (pstr, '\0', sizeof (re_string_t)); + re_string_construct_common (str, len, pstr, trans, icase, dfa); + + if (len > 0) + { + ret = re_string_realloc_buffers (pstr, len + 1); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; + + if (icase) + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + while (1) + { + ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + if (pstr->valid_raw_len >= len) + break; + if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max) + break; + ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + } + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (trans != NULL) + re_string_translate_buffer (pstr); + else + { + pstr->valid_len = pstr->bufs_len; + pstr->valid_raw_len = pstr->bufs_len; + } + } + } + + return REG_NOERROR; +} + +/* Helper functions for re_string_allocate, and re_string_construct. */ + +static reg_errcode_t +internal_function +re_string_realloc_buffers (re_string_t *pstr, int new_buf_len) +{ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + wint_t *new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len); + if (BE (new_wcs == NULL, 0)) + return REG_ESPACE; + pstr->wcs = new_wcs; + if (pstr->offsets != NULL) + { + int *new_offsets = re_realloc (pstr->offsets, int, new_buf_len); + if (BE (new_offsets == NULL, 0)) + return REG_ESPACE; + pstr->offsets = new_offsets; + } + } +#endif /* RE_ENABLE_I18N */ + if (pstr->mbs_allocated) + { + unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char, + new_buf_len); + if (BE (new_mbs == NULL, 0)) + return REG_ESPACE; + pstr->mbs = new_mbs; + } + pstr->bufs_len = new_buf_len; + return REG_NOERROR; +} + + +static void +internal_function +re_string_construct_common (const char *str, int len, re_string_t *pstr, + RE_TRANSLATE_TYPE trans, int icase, + const re_dfa_t *dfa) +{ + pstr->raw_mbs = (const unsigned char *) str; + pstr->len = len; + pstr->raw_len = len; + pstr->trans = trans; + pstr->icase = icase ? 1 : 0; + pstr->mbs_allocated = (trans != NULL || icase); + pstr->mb_cur_max = dfa->mb_cur_max; + pstr->is_utf8 = dfa->is_utf8; + pstr->map_notascii = dfa->map_notascii; + pstr->stop = pstr->len; + pstr->raw_stop = pstr->stop; +} + +#ifdef RE_ENABLE_I18N + +/* Build wide character buffer PSTR->WCS. + If the byte sequence of the string are: + (0), (1), (0), (1), + Then wide character buffer will be: + , WEOF , , WEOF , + We use WEOF for padding, they indicate that the position isn't + a first byte of a multibyte character. + + Note that this function assumes PSTR->VALID_LEN elements are already + built and starts from PSTR->VALID_LEN. */ + +static void +internal_function +build_wcs_buffer (re_string_t *pstr) +{ +#ifdef _LIBC + unsigned char buf[MB_LEN_MAX]; + assert (MB_LEN_MAX >= pstr->mb_cur_max); +#else + unsigned char buf[64]; +#endif + mbstate_t prev_st; + int byte_idx, end_idx, remain_len; + size_t mbclen; + + /* Build the buffers from pstr->valid_len to either pstr->len or + pstr->bufs_len. */ + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + for (byte_idx = pstr->valid_len; byte_idx < end_idx;) + { + wchar_t wc; + const char *p; + + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + /* Apply the translation if we need. */ + if (BE (pstr->trans != NULL, 0)) + { + int i, ch; + + for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) + { + ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i]; + buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch]; + } + p = (const char *) buf; + } + else + p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx; + mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2, 0)) + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a singlebyte character. */ + mbclen = 1; + wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + if (BE (pstr->trans != NULL, 0)) + wc = pstr->trans[wc]; + pstr->cur_state = prev_st; + } + + /* Write wide character and padding. */ + pstr->wcs[byte_idx++] = wc; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = byte_idx; +} + +/* Build wide character buffer PSTR->WCS like build_wcs_buffer, + but for REG_ICASE. */ + +static reg_errcode_t +internal_function +build_wcs_upper_buffer (re_string_t *pstr) +{ + mbstate_t prev_st; + int src_idx, byte_idx, end_idx, remain_len; + size_t mbclen; +#ifdef _LIBC + char buf[MB_LEN_MAX]; + assert (MB_LEN_MAX >= pstr->mb_cur_max); +#else + char buf[64]; +#endif + + byte_idx = pstr->valid_len; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + /* The following optimization assumes that ASCII characters can be + mapped to wide characters with a simple cast. */ + if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed) + { + while (byte_idx < end_idx) + { + wchar_t wc; + + if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]) + && mbsinit (&pstr->cur_state)) + { + /* In case of a singlebyte character. */ + pstr->mbs[byte_idx] + = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]); + /* The next step uses the assumption that wchar_t is encoded + ASCII-safe: all ASCII values can be converted like this. */ + pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx]; + ++byte_idx; + continue; + } + + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + mbclen = mbrtowc (&wc, + ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx + + byte_idx), remain_len, &pstr->cur_state); + if (BE (mbclen + 2 > 2, 1)) + { + wchar_t wcu = wc; + if (iswlower (wc)) + { + size_t mbcdlen; + + wcu = towupper (wc); + mbcdlen = wcrtomb (buf, wcu, &prev_st); + if (BE (mbclen == mbcdlen, 1)) + memcpy (pstr->mbs + byte_idx, buf, mbclen); + else + { + src_idx = byte_idx; + goto offsets_needed; + } + } + else + memcpy (pstr->mbs + byte_idx, + pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen); + pstr->wcs[byte_idx++] = wcu; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + else if (mbclen == (size_t) -1 || mbclen == 0) + { + /* It is an invalid character or '\0'. Just use the byte. */ + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + pstr->mbs[byte_idx] = ch; + /* And also cast it to wide char. */ + pstr->wcs[byte_idx++] = (wchar_t) ch; + if (BE (mbclen == (size_t) -1, 0)) + pstr->cur_state = prev_st; + } + else + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = byte_idx; + return REG_NOERROR; + } + else + for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;) + { + wchar_t wc; + const char *p; + offsets_needed: + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + if (BE (pstr->trans != NULL, 0)) + { + int i, ch; + + for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) + { + ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i]; + buf[i] = pstr->trans[ch]; + } + p = (const char *) buf; + } + else + p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx; + mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state); + if (BE (mbclen + 2 > 2, 1)) + { + wchar_t wcu = wc; + if (iswlower (wc)) + { + size_t mbcdlen; + + wcu = towupper (wc); + mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st); + if (BE (mbclen == mbcdlen, 1)) + memcpy (pstr->mbs + byte_idx, buf, mbclen); + else if (mbcdlen != (size_t) -1) + { + size_t i; + + if (byte_idx + mbcdlen > pstr->bufs_len) + { + pstr->cur_state = prev_st; + break; + } + + if (pstr->offsets == NULL) + { + pstr->offsets = re_malloc (int, pstr->bufs_len); + + if (pstr->offsets == NULL) + return REG_ESPACE; + } + if (!pstr->offsets_needed) + { + for (i = 0; i < (size_t) byte_idx; ++i) + pstr->offsets[i] = i; + pstr->offsets_needed = 1; + } + + memcpy (pstr->mbs + byte_idx, buf, mbcdlen); + pstr->wcs[byte_idx] = wcu; + pstr->offsets[byte_idx] = src_idx; + for (i = 1; i < mbcdlen; ++i) + { + pstr->offsets[byte_idx + i] + = src_idx + (i < mbclen ? i : mbclen - 1); + pstr->wcs[byte_idx + i] = WEOF; + } + pstr->len += mbcdlen - mbclen; + if (pstr->raw_stop > src_idx) + pstr->stop += mbcdlen - mbclen; + end_idx = (pstr->bufs_len > pstr->len) + ? pstr->len : pstr->bufs_len; + byte_idx += mbcdlen; + src_idx += mbclen; + continue; + } + else + memcpy (pstr->mbs + byte_idx, p, mbclen); + } + else + memcpy (pstr->mbs + byte_idx, p, mbclen); + + if (BE (pstr->offsets_needed != 0, 0)) + { + size_t i; + for (i = 0; i < mbclen; ++i) + pstr->offsets[byte_idx + i] = src_idx + i; + } + src_idx += mbclen; + + pstr->wcs[byte_idx++] = wcu; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + else if (mbclen == (size_t) -1 || mbclen == 0) + { + /* It is an invalid character or '\0'. Just use the byte. */ + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx]; + + if (BE (pstr->trans != NULL, 0)) + ch = pstr->trans [ch]; + pstr->mbs[byte_idx] = ch; + + if (BE (pstr->offsets_needed != 0, 0)) + pstr->offsets[byte_idx] = src_idx; + ++src_idx; + + /* And also cast it to wide char. */ + pstr->wcs[byte_idx++] = (wchar_t) ch; + if (BE (mbclen == (size_t) -1, 0)) + pstr->cur_state = prev_st; + } + else + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = src_idx; + return REG_NOERROR; +} + +/* Skip characters until the index becomes greater than NEW_RAW_IDX. + Return the index. */ + +static int +internal_function +re_string_skip_chars (re_string_t *pstr, int new_raw_idx, wint_t *last_wc) +{ + mbstate_t prev_st; + int rawbuf_idx; + size_t mbclen; + wchar_t wc = WEOF; + + /* Skip the characters which are not necessary to check. */ + for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len; + rawbuf_idx < new_raw_idx;) + { + int remain_len; + remain_len = pstr->len - rawbuf_idx; + prev_st = pstr->cur_state; + mbclen = mbrtowc (&wc, (const char *) pstr->raw_mbs + rawbuf_idx, + remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a single byte character. */ + if (mbclen == 0 || remain_len == 0) + wc = L'\0'; + else + wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx); + mbclen = 1; + pstr->cur_state = prev_st; + } + /* Then proceed the next character. */ + rawbuf_idx += mbclen; + } + *last_wc = (wint_t) wc; + return rawbuf_idx; +} +#endif /* RE_ENABLE_I18N */ + +/* Build the buffer PSTR->MBS, and apply the translation if we need. + This function is used in case of REG_ICASE. */ + +static void +internal_function +build_upper_buffer (re_string_t *pstr) +{ + int char_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx]; + if (BE (pstr->trans != NULL, 0)) + ch = pstr->trans[ch]; + if (islower (ch)) + pstr->mbs[char_idx] = toupper (ch); + else + pstr->mbs[char_idx] = ch; + } + pstr->valid_len = char_idx; + pstr->valid_raw_len = char_idx; +} + +/* Apply TRANS to the buffer in PSTR. */ + +static void +internal_function +re_string_translate_buffer (re_string_t *pstr) +{ + int buf_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx]; + pstr->mbs[buf_idx] = pstr->trans[ch]; + } + + pstr->valid_len = buf_idx; + pstr->valid_raw_len = buf_idx; +} + +/* This function re-construct the buffers. + Concretely, convert to wide character in case of pstr->mb_cur_max > 1, + convert to upper case in case of REG_ICASE, apply translation. */ + +static reg_errcode_t +internal_function +re_string_reconstruct (re_string_t *pstr, int idx, int eflags) +{ + int offset = idx - pstr->raw_mbs_idx; + if (BE (offset < 0, 0)) + { + /* Reset buffer. */ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); +#endif /* RE_ENABLE_I18N */ + pstr->len = pstr->raw_len; + pstr->stop = pstr->raw_stop; + pstr->valid_len = 0; + pstr->raw_mbs_idx = 0; + pstr->valid_raw_len = 0; + pstr->offsets_needed = 0; + pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF); + if (!pstr->mbs_allocated) + pstr->mbs = (unsigned char *) pstr->raw_mbs; + offset = idx; + } + + if (BE (offset != 0, 1)) + { + /* Should the already checked characters be kept? */ + if (BE (offset < pstr->valid_raw_len, 1)) + { + /* Yes, move them to the front of the buffer. */ +#ifdef RE_ENABLE_I18N + if (BE (pstr->offsets_needed, 0)) + { + int low = 0, high = pstr->valid_len, mid; + do + { + mid = (high + low) / 2; + if (pstr->offsets[mid] > offset) + high = mid; + else if (pstr->offsets[mid] < offset) + low = mid + 1; + else + break; + } + while (low < high); + if (pstr->offsets[mid] < offset) + ++mid; + pstr->tip_context = re_string_context_at (pstr, mid - 1, + eflags); + /* This can be quite complicated, so handle specially + only the common and easy case where the character with + different length representation of lower and upper + case is present at or after offset. */ + if (pstr->valid_len > offset + && mid == offset && pstr->offsets[mid] == offset) + { + memmove (pstr->wcs, pstr->wcs + offset, + (pstr->valid_len - offset) * sizeof (wint_t)); + memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset); + pstr->valid_len -= offset; + pstr->valid_raw_len -= offset; + for (low = 0; low < pstr->valid_len; low++) + pstr->offsets[low] = pstr->offsets[low + offset] - offset; + } + else + { + /* Otherwise, just find out how long the partial multibyte + character at offset is and fill it with WEOF/255. */ + pstr->len = pstr->raw_len - idx + offset; + pstr->stop = pstr->raw_stop - idx + offset; + pstr->offsets_needed = 0; + while (mid > 0 && pstr->offsets[mid - 1] == offset) + --mid; + while (mid < pstr->valid_len) + if (pstr->wcs[mid] != WEOF) + break; + else + ++mid; + if (mid == pstr->valid_len) + pstr->valid_len = 0; + else + { + pstr->valid_len = pstr->offsets[mid] - offset; + if (pstr->valid_len) + { + for (low = 0; low < pstr->valid_len; ++low) + pstr->wcs[low] = WEOF; + memset (pstr->mbs, 255, pstr->valid_len); + } + } + pstr->valid_raw_len = pstr->valid_len; + } + } + else +#endif + { + pstr->tip_context = re_string_context_at (pstr, offset - 1, + eflags); +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + memmove (pstr->wcs, pstr->wcs + offset, + (pstr->valid_len - offset) * sizeof (wint_t)); +#endif /* RE_ENABLE_I18N */ + if (BE (pstr->mbs_allocated, 0)) + memmove (pstr->mbs, pstr->mbs + offset, + pstr->valid_len - offset); + pstr->valid_len -= offset; + pstr->valid_raw_len -= offset; +#if DEBUG + assert (pstr->valid_len > 0); +#endif + } + } + else + { + /* No, skip all characters until IDX. */ + int prev_valid_len = pstr->valid_len; + +#ifdef RE_ENABLE_I18N + if (BE (pstr->offsets_needed, 0)) + { + pstr->len = pstr->raw_len - idx + offset; + pstr->stop = pstr->raw_stop - idx + offset; + pstr->offsets_needed = 0; + } +#endif + pstr->valid_len = 0; +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + int wcs_idx; + wint_t wc = WEOF; + + if (pstr->is_utf8) + { + const unsigned char *raw, *p, *q, *end; + + /* Special case UTF-8. Multi-byte chars start with any + byte other than 0x80 - 0xbf. */ + raw = pstr->raw_mbs + pstr->raw_mbs_idx; + end = raw + (offset - pstr->mb_cur_max); + if (end < pstr->raw_mbs) + end = pstr->raw_mbs; + p = raw + offset - 1; +#ifdef _LIBC + /* We know the wchar_t encoding is UCS4, so for the simple + case, ASCII characters, skip the conversion step. */ + if (isascii (*p) && BE (pstr->trans == NULL, 1)) + { + memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); + /* pstr->valid_len = 0; */ + wc = (wchar_t) *p; + } + else +#endif + for (; p >= end; --p) + if ((*p & 0xc0) != 0x80) + { + mbstate_t cur_state; + wchar_t wc2; + int mlen = raw + pstr->len - p; + unsigned char buf[6]; + size_t mbclen; + + q = p; + if (BE (pstr->trans != NULL, 0)) + { + int i = mlen < 6 ? mlen : 6; + while (--i >= 0) + buf[i] = pstr->trans[p[i]]; + q = buf; + } + /* XXX Don't use mbrtowc, we know which conversion + to use (UTF-8 -> UCS4). */ + memset (&cur_state, 0, sizeof (cur_state)); + mbclen = mbrtowc (&wc2, (const char *) p, mlen, + &cur_state); + if (raw + offset - p <= mbclen + && mbclen < (size_t) -2) + { + memset (&pstr->cur_state, '\0', + sizeof (mbstate_t)); + pstr->valid_len = mbclen - (raw + offset - p); + wc = wc2; + } + break; + } + } + + if (wc == WEOF) + pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx; + if (wc == WEOF) + pstr->tip_context + = re_string_context_at (pstr, prev_valid_len - 1, eflags); + else + pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0) + && IS_WIDE_WORD_CHAR (wc)) + ? CONTEXT_WORD + : ((IS_WIDE_NEWLINE (wc) + && pstr->newline_anchor) + ? CONTEXT_NEWLINE : 0)); + if (BE (pstr->valid_len, 0)) + { + for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx) + pstr->wcs[wcs_idx] = WEOF; + if (pstr->mbs_allocated) + memset (pstr->mbs, 255, pstr->valid_len); + } + pstr->valid_raw_len = pstr->valid_len; + } + else +#endif /* RE_ENABLE_I18N */ + { + int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1]; + pstr->valid_raw_len = 0; + if (pstr->trans) + c = pstr->trans[c]; + pstr->tip_context = (bitset_contain (pstr->word_char, c) + ? CONTEXT_WORD + : ((IS_NEWLINE (c) && pstr->newline_anchor) + ? CONTEXT_NEWLINE : 0)); + } + } + if (!BE (pstr->mbs_allocated, 0)) + pstr->mbs += offset; + } + pstr->raw_mbs_idx = idx; + pstr->len -= offset; + pstr->stop -= offset; + + /* Then build the buffers. */ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + if (pstr->icase) + { + reg_errcode_t ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + else + build_wcs_buffer (pstr); + } + else +#endif /* RE_ENABLE_I18N */ + if (BE (pstr->mbs_allocated, 0)) + { + if (pstr->icase) + build_upper_buffer (pstr); + else if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + } + else + pstr->valid_len = pstr->len; + + pstr->cur_idx = 0; + return REG_NOERROR; +} + +static unsigned char +internal_function __attribute ((pure)) +re_string_peek_byte_case (const re_string_t *pstr, int idx) +{ + int ch, off; + + /* Handle the common (easiest) cases first. */ + if (BE (!pstr->mbs_allocated, 1)) + return re_string_peek_byte (pstr, idx); + +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1 + && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx)) + return re_string_peek_byte (pstr, idx); +#endif + + off = pstr->cur_idx + idx; +#ifdef RE_ENABLE_I18N + if (pstr->offsets_needed) + off = pstr->offsets[off]; +#endif + + ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; + +#ifdef RE_ENABLE_I18N + /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I + this function returns CAPITAL LETTER I instead of first byte of + DOTLESS SMALL LETTER I. The latter would confuse the parser, + since peek_byte_case doesn't advance cur_idx in any way. */ + if (pstr->offsets_needed && !isascii (ch)) + return re_string_peek_byte (pstr, idx); +#endif + + return ch; +} + +static unsigned char +internal_function __attribute ((pure)) +re_string_fetch_byte_case (re_string_t *pstr) +{ + if (BE (!pstr->mbs_allocated, 1)) + return re_string_fetch_byte (pstr); + +#ifdef RE_ENABLE_I18N + if (pstr->offsets_needed) + { + int off, ch; + + /* For tr_TR.UTF-8 [[:islower:]] there is + [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip + in that case the whole multi-byte character and return + the original letter. On the other side, with + [[: DOTLESS SMALL LETTER I return [[:I, as doing + anything else would complicate things too much. */ + + if (!re_string_first_byte (pstr, pstr->cur_idx)) + return re_string_fetch_byte (pstr); + + off = pstr->offsets[pstr->cur_idx]; + ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; + + if (! isascii (ch)) + return re_string_fetch_byte (pstr); + + re_string_skip_bytes (pstr, + re_string_char_size_at (pstr, pstr->cur_idx)); + return ch; + } +#endif + + return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++]; +} + +static void +internal_function +re_string_destruct (re_string_t *pstr) +{ +#ifdef RE_ENABLE_I18N + re_free (pstr->wcs); + re_free (pstr->offsets); +#endif /* RE_ENABLE_I18N */ + if (pstr->mbs_allocated) + re_free (pstr->mbs); +} + +/* Return the context at IDX in INPUT. */ + +static unsigned int +internal_function +re_string_context_at (const re_string_t *input, int idx, int eflags) +{ + int c; + if (BE (idx < 0, 0)) + /* In this case, we use the value stored in input->tip_context, + since we can't know the character in input->mbs[-1] here. */ + return input->tip_context; + if (BE (idx == input->len, 0)) + return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF + : CONTEXT_NEWLINE | CONTEXT_ENDBUF); +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc; + int wc_idx = idx; + while(input->wcs[wc_idx] == WEOF) + { +#ifdef DEBUG + /* It must not happen. */ + assert (wc_idx >= 0); +#endif + --wc_idx; + if (wc_idx < 0) + return input->tip_context; + } + wc = input->wcs[wc_idx]; + if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc)) + return CONTEXT_WORD; + return (IS_WIDE_NEWLINE (wc) && input->newline_anchor + ? CONTEXT_NEWLINE : 0); + } + else +#endif + { + c = re_string_byte_at (input, idx); + if (bitset_contain (input->word_char, c)) + return CONTEXT_WORD; + return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0; + } +} + +/* Functions for set operation. */ + +static reg_errcode_t +internal_function +re_node_set_alloc (re_node_set *set, int size) +{ + set->alloc = size; + set->nelem = 0; + set->elems = re_malloc (int, size); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +re_node_set_init_1 (re_node_set *set, int elem) +{ + set->alloc = 1; + set->nelem = 1; + set->elems = re_malloc (int, 1); + if (BE (set->elems == NULL, 0)) + { + set->alloc = set->nelem = 0; + return REG_ESPACE; + } + set->elems[0] = elem; + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +re_node_set_init_2 (re_node_set *set, int elem1, int elem2) +{ + set->alloc = 2; + set->elems = re_malloc (int, 2); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + if (elem1 == elem2) + { + set->nelem = 1; + set->elems[0] = elem1; + } + else + { + set->nelem = 2; + if (elem1 < elem2) + { + set->elems[0] = elem1; + set->elems[1] = elem2; + } + else + { + set->elems[0] = elem2; + set->elems[1] = elem1; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +re_node_set_init_copy (re_node_set *dest, const re_node_set *src) +{ + dest->nelem = src->nelem; + if (src->nelem > 0) + { + dest->alloc = dest->nelem; + dest->elems = re_malloc (int, dest->alloc); + if (BE (dest->elems == NULL, 0)) + { + dest->alloc = dest->nelem = 0; + return REG_ESPACE; + } + memcpy (dest->elems, src->elems, src->nelem * sizeof (int)); + } + else + re_node_set_init_empty (dest); + return REG_NOERROR; +} + +/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. + Note: We assume dest->elems is NULL, when dest->alloc is 0. */ + +static reg_errcode_t +internal_function +re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1, + const re_node_set *src2) +{ + int i1, i2, is, id, delta, sbase; + if (src1->nelem == 0 || src2->nelem == 0) + return REG_NOERROR; + + /* We need dest->nelem + 2 * elems_in_intersection; this is a + conservative estimate. */ + if (src1->nelem + src2->nelem + dest->nelem > dest->alloc) + { + int new_alloc = src1->nelem + src2->nelem + dest->alloc; + int *new_elems = re_realloc (dest->elems, int, new_alloc); + if (BE (new_elems == NULL, 0)) + return REG_ESPACE; + dest->elems = new_elems; + dest->alloc = new_alloc; + } + + /* Find the items in the intersection of SRC1 and SRC2, and copy + into the top of DEST those that are not already in DEST itself. */ + sbase = dest->nelem + src1->nelem + src2->nelem; + i1 = src1->nelem - 1; + i2 = src2->nelem - 1; + id = dest->nelem - 1; + for (;;) + { + if (src1->elems[i1] == src2->elems[i2]) + { + /* Try to find the item in DEST. Maybe we could binary search? */ + while (id >= 0 && dest->elems[id] > src1->elems[i1]) + --id; + + if (id < 0 || dest->elems[id] != src1->elems[i1]) + dest->elems[--sbase] = src1->elems[i1]; + + if (--i1 < 0 || --i2 < 0) + break; + } + + /* Lower the highest of the two items. */ + else if (src1->elems[i1] < src2->elems[i2]) + { + if (--i2 < 0) + break; + } + else + { + if (--i1 < 0) + break; + } + } + + id = dest->nelem - 1; + is = dest->nelem + src1->nelem + src2->nelem - 1; + delta = is - sbase + 1; + + /* Now copy. When DELTA becomes zero, the remaining + DEST elements are already in place; this is more or + less the same loop that is in re_node_set_merge. */ + dest->nelem += delta; + if (delta > 0 && id >= 0) + for (;;) + { + if (dest->elems[is] > dest->elems[id]) + { + /* Copy from the top. */ + dest->elems[id + delta--] = dest->elems[is--]; + if (delta == 0) + break; + } + else + { + /* Slide from the bottom. */ + dest->elems[id + delta] = dest->elems[id]; + if (--id < 0) + break; + } + } + + /* Copy remaining SRC elements. */ + memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int)); + + return REG_NOERROR; +} + +/* Calculate the union set of the sets SRC1 and SRC2. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +internal_function +re_node_set_init_union (re_node_set *dest, const re_node_set *src1, + const re_node_set *src2) +{ + int i1, i2, id; + if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0) + { + dest->alloc = src1->nelem + src2->nelem; + dest->elems = re_malloc (int, dest->alloc); + if (BE (dest->elems == NULL, 0)) + return REG_ESPACE; + } + else + { + if (src1 != NULL && src1->nelem > 0) + return re_node_set_init_copy (dest, src1); + else if (src2 != NULL && src2->nelem > 0) + return re_node_set_init_copy (dest, src2); + else + re_node_set_init_empty (dest); + return REG_NOERROR; + } + for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;) + { + if (src1->elems[i1] > src2->elems[i2]) + { + dest->elems[id++] = src2->elems[i2++]; + continue; + } + if (src1->elems[i1] == src2->elems[i2]) + ++i2; + dest->elems[id++] = src1->elems[i1++]; + } + if (i1 < src1->nelem) + { + memcpy (dest->elems + id, src1->elems + i1, + (src1->nelem - i1) * sizeof (int)); + id += src1->nelem - i1; + } + else if (i2 < src2->nelem) + { + memcpy (dest->elems + id, src2->elems + i2, + (src2->nelem - i2) * sizeof (int)); + id += src2->nelem - i2; + } + dest->nelem = id; + return REG_NOERROR; +} + +/* Calculate the union set of the sets DEST and SRC. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +internal_function +re_node_set_merge (re_node_set *dest, const re_node_set *src) +{ + int is, id, sbase, delta; + if (src == NULL || src->nelem == 0) + return REG_NOERROR; + if (dest->alloc < 2 * src->nelem + dest->nelem) + { + int new_alloc = 2 * (src->nelem + dest->alloc); + int *new_buffer = re_realloc (dest->elems, int, new_alloc); + if (BE (new_buffer == NULL, 0)) + return REG_ESPACE; + dest->elems = new_buffer; + dest->alloc = new_alloc; + } + + if (BE (dest->nelem == 0, 0)) + { + dest->nelem = src->nelem; + memcpy (dest->elems, src->elems, src->nelem * sizeof (int)); + return REG_NOERROR; + } + + /* Copy into the top of DEST the items of SRC that are not + found in DEST. Maybe we could binary search in DEST? */ + for (sbase = dest->nelem + 2 * src->nelem, + is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; ) + { + if (dest->elems[id] == src->elems[is]) + is--, id--; + else if (dest->elems[id] < src->elems[is]) + dest->elems[--sbase] = src->elems[is--]; + else /* if (dest->elems[id] > src->elems[is]) */ + --id; + } + + if (is >= 0) + { + /* If DEST is exhausted, the remaining items of SRC must be unique. */ + sbase -= is + 1; + memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int)); + } + + id = dest->nelem - 1; + is = dest->nelem + 2 * src->nelem - 1; + delta = is - sbase + 1; + if (delta == 0) + return REG_NOERROR; + + /* Now copy. When DELTA becomes zero, the remaining + DEST elements are already in place. */ + dest->nelem += delta; + for (;;) + { + if (dest->elems[is] > dest->elems[id]) + { + /* Copy from the top. */ + dest->elems[id + delta--] = dest->elems[is--]; + if (delta == 0) + break; + } + else + { + /* Slide from the bottom. */ + dest->elems[id + delta] = dest->elems[id]; + if (--id < 0) + { + /* Copy remaining SRC elements. */ + memcpy (dest->elems, dest->elems + sbase, + delta * sizeof (int)); + break; + } + } + } + + return REG_NOERROR; +} + +/* Insert the new element ELEM to the re_node_set* SET. + SET should not already have ELEM. + return -1 if an error is occured, return 1 otherwise. */ + +static int +internal_function +re_node_set_insert (re_node_set *set, int elem) +{ + int idx; + /* In case the set is empty. */ + if (set->alloc == 0) + { + if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1)) + return 1; + else + return -1; + } + + if (BE (set->nelem, 0) == 0) + { + /* We already guaranteed above that set->alloc != 0. */ + set->elems[0] = elem; + ++set->nelem; + return 1; + } + + /* Realloc if we need. */ + if (set->alloc == set->nelem) + { + int *new_elems; + set->alloc = set->alloc * 2; + new_elems = re_realloc (set->elems, int, set->alloc); + if (BE (new_elems == NULL, 0)) + return -1; + set->elems = new_elems; + } + + /* Move the elements which follows the new element. Test the + first element separately to skip a check in the inner loop. */ + if (elem < set->elems[0]) + { + idx = 0; + for (idx = set->nelem; idx > 0; idx--) + set->elems[idx] = set->elems[idx - 1]; + } + else + { + for (idx = set->nelem; set->elems[idx - 1] > elem; idx--) + set->elems[idx] = set->elems[idx - 1]; + } + + /* Insert the new element. */ + set->elems[idx] = elem; + ++set->nelem; + return 1; +} + +/* Insert the new element ELEM to the re_node_set* SET. + SET should not already have any element greater than or equal to ELEM. + Return -1 if an error is occured, return 1 otherwise. */ + +static int +internal_function +re_node_set_insert_last (re_node_set *set, int elem) +{ + /* Realloc if we need. */ + if (set->alloc == set->nelem) + { + int *new_elems; + set->alloc = (set->alloc + 1) * 2; + new_elems = re_realloc (set->elems, int, set->alloc); + if (BE (new_elems == NULL, 0)) + return -1; + set->elems = new_elems; + } + + /* Insert the new element. */ + set->elems[set->nelem++] = elem; + return 1; +} + +/* Compare two node sets SET1 and SET2. + return 1 if SET1 and SET2 are equivalent, return 0 otherwise. */ + +static int +internal_function __attribute ((pure)) +re_node_set_compare (const re_node_set *set1, const re_node_set *set2) +{ + int i; + if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem) + return 0; + for (i = set1->nelem ; --i >= 0 ; ) + if (set1->elems[i] != set2->elems[i]) + return 0; + return 1; +} + +/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */ + +static int +internal_function __attribute ((pure)) +re_node_set_contains (const re_node_set *set, int elem) +{ + unsigned int idx, right, mid; + if (set->nelem <= 0) + return 0; + + /* Binary search the element. */ + idx = 0; + right = set->nelem - 1; + while (idx < right) + { + mid = (idx + right) / 2; + if (set->elems[mid] < elem) + idx = mid + 1; + else + right = mid; + } + return set->elems[idx] == elem ? idx + 1 : 0; +} + +static void +internal_function +re_node_set_remove_at (re_node_set *set, int idx) +{ + if (idx < 0 || idx >= set->nelem) + return; + --set->nelem; + for (; idx < set->nelem; idx++) + set->elems[idx] = set->elems[idx + 1]; +} + + +/* Add the token TOKEN to dfa->nodes, and return the index of the token. + Or return -1, if an error will be occured. */ + +static int +internal_function +re_dfa_add_node (re_dfa_t *dfa, re_token_t token) +{ + int type = token.type; + if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0)) + { + size_t new_nodes_alloc = dfa->nodes_alloc * 2; + int *new_nexts, *new_indices; + re_node_set *new_edests, *new_eclosures; + re_token_t *new_nodes; + + /* Avoid overflows. */ + if (BE (new_nodes_alloc < dfa->nodes_alloc, 0)) + return -1; + + new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc); + if (BE (new_nodes == NULL, 0)) + return -1; + dfa->nodes = new_nodes; + new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc); + new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc); + new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc); + new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc); + if (BE (new_nexts == NULL || new_indices == NULL + || new_edests == NULL || new_eclosures == NULL, 0)) + return -1; + dfa->nexts = new_nexts; + dfa->org_indices = new_indices; + dfa->edests = new_edests; + dfa->eclosures = new_eclosures; + dfa->nodes_alloc = new_nodes_alloc; + } + dfa->nodes[dfa->nodes_len] = token; + dfa->nodes[dfa->nodes_len].constraint = 0; +#ifdef RE_ENABLE_I18N + dfa->nodes[dfa->nodes_len].accept_mb = + (type == OP_PERIOD && dfa->mb_cur_max > 1) || type == COMPLEX_BRACKET; +#endif + dfa->nexts[dfa->nodes_len] = -1; + re_node_set_init_empty (dfa->edests + dfa->nodes_len); + re_node_set_init_empty (dfa->eclosures + dfa->nodes_len); + return dfa->nodes_len++; +} + +static inline unsigned int +internal_function +calc_state_hash (const re_node_set *nodes, unsigned int context) +{ + unsigned int hash = nodes->nelem + context; + int i; + for (i = 0 ; i < nodes->nelem ; i++) + hash += nodes->elems[i]; + return hash; +} + +/* Search for the state whose node_set is equivalent to NODES. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t * +internal_function +re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa, + const re_node_set *nodes) +{ + unsigned int hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + int i; + if (BE (nodes->nelem == 0, 0)) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, 0); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (hash != state->hash) + continue; + if (re_node_set_compare (&state->nodes, nodes)) + return state; + } + + /* There are no appropriate state in the dfa, create the new one. */ + new_state = create_ci_newstate (dfa, nodes, hash); + if (BE (new_state == NULL, 0)) + *err = REG_ESPACE; + + return new_state; +} + +/* Search for the state whose node_set is equivalent to NODES and + whose context is equivalent to CONTEXT. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t * +internal_function +re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa, + const re_node_set *nodes, unsigned int context) +{ + unsigned int hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + int i; + if (nodes->nelem == 0) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, context); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (state->hash == hash + && state->context == context + && re_node_set_compare (state->entrance_nodes, nodes)) + return state; + } + /* There are no appropriate state in `dfa', create the new one. */ + new_state = create_cd_newstate (dfa, nodes, context, hash); + if (BE (new_state == NULL, 0)) + *err = REG_ESPACE; + + return new_state; +} + +/* Finish initialization of the new state NEWSTATE, and using its hash value + HASH put in the appropriate bucket of DFA's state table. Return value + indicates the error code if failed. */ + +static reg_errcode_t +register_state (const re_dfa_t *dfa, re_dfastate_t *newstate, + unsigned int hash) +{ + struct re_state_table_entry *spot; + reg_errcode_t err; + int i; + + newstate->hash = hash; + err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem); + if (BE (err != REG_NOERROR, 0)) + return REG_ESPACE; + for (i = 0; i < newstate->nodes.nelem; i++) + { + int elem = newstate->nodes.elems[i]; + if (!IS_EPSILON_NODE (dfa->nodes[elem].type)) + re_node_set_insert_last (&newstate->non_eps_nodes, elem); + } + + spot = dfa->state_table + (hash & dfa->state_hash_mask); + if (BE (spot->alloc <= spot->num, 0)) + { + int new_alloc = 2 * spot->num + 2; + re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *, + new_alloc); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + spot->array = new_array; + spot->alloc = new_alloc; + } + spot->array[spot->num++] = newstate; + return REG_NOERROR; +} + +static void +free_state (re_dfastate_t *state) +{ + re_node_set_free (&state->non_eps_nodes); + re_node_set_free (&state->inveclosure); + if (state->entrance_nodes != &state->nodes) + { + re_node_set_free (state->entrance_nodes); + re_free (state->entrance_nodes); + } + re_node_set_free (&state->nodes); + re_free (state->word_trtable); + re_free (state->trtable); + re_free (state); +} + +/* Create the new state which is independ of contexts. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +internal_function +create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes, + unsigned int hash) +{ + int i; + reg_errcode_t err; + re_dfastate_t *newstate; + + newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); + if (BE (newstate == NULL, 0)) + return NULL; + err = re_node_set_init_copy (&newstate->nodes, nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_free (newstate); + return NULL; + } + + newstate->entrance_nodes = &newstate->nodes; + for (i = 0 ; i < nodes->nelem ; i++) + { + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + if (type == CHARACTER && !node->constraint) + continue; +#ifdef RE_ENABLE_I18N + newstate->accept_mb |= node->accept_mb; +#endif /* RE_ENABLE_I18N */ + + /* If the state has the halt node, the state is a halt state. */ + if (type == END_OF_RE) + newstate->halt = 1; + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + else if (type == ANCHOR || node->constraint) + newstate->has_constraint = 1; + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} + +/* Create the new state which is depend on the context CONTEXT. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +internal_function +create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes, + unsigned int context, unsigned int hash) +{ + int i, nctx_nodes = 0; + reg_errcode_t err; + re_dfastate_t *newstate; + + newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); + if (BE (newstate == NULL, 0)) + return NULL; + err = re_node_set_init_copy (&newstate->nodes, nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_free (newstate); + return NULL; + } + + newstate->context = context; + newstate->entrance_nodes = &newstate->nodes; + + for (i = 0 ; i < nodes->nelem ; i++) + { + unsigned int constraint = 0; + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + if (node->constraint) + constraint = node->constraint; + + if (type == CHARACTER && !constraint) + continue; +#ifdef RE_ENABLE_I18N + newstate->accept_mb |= node->accept_mb; +#endif /* RE_ENABLE_I18N */ + + /* If the state has the halt node, the state is a halt state. */ + if (type == END_OF_RE) + newstate->halt = 1; + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + else if (type == ANCHOR) + constraint = node->opr.ctx_type; + + if (constraint) + { + if (newstate->entrance_nodes == &newstate->nodes) + { + newstate->entrance_nodes = re_malloc (re_node_set, 1); + if (BE (newstate->entrance_nodes == NULL, 0)) + { + free_state (newstate); + return NULL; + } + re_node_set_init_copy (newstate->entrance_nodes, nodes); + nctx_nodes = 0; + newstate->has_constraint = 1; + } + + if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context)) + { + re_node_set_remove_at (&newstate->nodes, i - nctx_nodes); + ++nctx_nodes; + } + } + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* GKINCLUDE #include "regcomp.c" */ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* Extended regular expression matching and search library. + Copyright (C) 2002,2003,2004,2005,2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, + size_t length, reg_syntax_t syntax); +static void re_compile_fastmap_iter (regex_t *bufp, + const re_dfastate_t *init_state, + char *fastmap); +static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len); +#ifdef RE_ENABLE_I18N +static void free_charset (re_charset_t *cset); +#endif /* RE_ENABLE_I18N */ +static void free_workarea_compile (regex_t *preg); +static reg_errcode_t create_initial_state (re_dfa_t *dfa); +#ifdef RE_ENABLE_I18N +static void optimize_utf8 (re_dfa_t *dfa); +#endif +static reg_errcode_t analyze (regex_t *preg); +static reg_errcode_t preorder (bin_tree_t *root, + reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra); +static reg_errcode_t postorder (bin_tree_t *root, + reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra); +static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node); +static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node); +static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg, + bin_tree_t *node); +static reg_errcode_t calc_first (void *extra, bin_tree_t *node); +static reg_errcode_t calc_next (void *extra, bin_tree_t *node); +static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node); +static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint); +static int search_duplicated_node (const re_dfa_t *dfa, int org_node, + unsigned int constraint); +static reg_errcode_t calc_eclosure (re_dfa_t *dfa); +static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, + int node, int root); +static reg_errcode_t calc_inveclosure (re_dfa_t *dfa); +static int fetch_number (re_string_t *input, re_token_t *token, + reg_syntax_t syntax); +static int peek_token (re_token_t *token, re_string_t *input, + reg_syntax_t syntax) internal_function; +static bin_tree_t *parse (re_string_t *regexp, regex_t *preg, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp, + re_dfa_t *dfa, re_token_t *token, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, + re_token_t *token, reg_syntax_t syntax, + reg_errcode_t *err); +static reg_errcode_t parse_bracket_element (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token, int token_len, + re_dfa_t *dfa, + reg_syntax_t syntax, + int accept_hyphen); +static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token); +#ifdef RE_ENABLE_I18N +static reg_errcode_t build_equiv_class (bitset_t sbcset, + re_charset_t *mbcset, + int *equiv_class_alloc, + const unsigned char *name); +static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, + bitset_t sbcset, + re_charset_t *mbcset, + int *char_class_alloc, + const unsigned char *class_name, + reg_syntax_t syntax); +#else /* not RE_ENABLE_I18N */ +static reg_errcode_t build_equiv_class (bitset_t sbcset, + const unsigned char *name); +static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, + bitset_t sbcset, + const unsigned char *class_name, + reg_syntax_t syntax); +#endif /* not RE_ENABLE_I18N */ +static bin_tree_t *build_charclass_op (re_dfa_t *dfa, + RE_TRANSLATE_TYPE trans, + const unsigned char *class_name, + const unsigned char *extra, + int non_match, reg_errcode_t *err); +static bin_tree_t *create_tree (re_dfa_t *dfa, + bin_tree_t *left, bin_tree_t *right, + re_token_type_t type); +static bin_tree_t *create_token_tree (re_dfa_t *dfa, + bin_tree_t *left, bin_tree_t *right, + const re_token_t *token); +static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa); +static void free_token (re_token_t *node); +static reg_errcode_t free_tree (void *extra, bin_tree_t *node); +static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node); + +/* This table gives an error message for each of the error codes listed + in regex.h. Obviously the order here has to be same as there. + POSIX doesn't require that we do anything for REG_NOERROR, + but why not be nice? */ + +const char __re_error_msgid[] attribute_hidden = + { +#define REG_NOERROR_IDX 0 + gettext_noop ("Success") /* REG_NOERROR */ + "\0" +#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success") + gettext_noop ("No match") /* REG_NOMATCH */ + "\0" +#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match") + gettext_noop ("Invalid regular expression") /* REG_BADPAT */ + "\0" +#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression") + gettext_noop ("Invalid collation character") /* REG_ECOLLATE */ + "\0" +#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character") + gettext_noop ("Invalid character class name") /* REG_ECTYPE */ + "\0" +#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name") + gettext_noop ("Trailing backslash") /* REG_EESCAPE */ + "\0" +#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash") + gettext_noop ("Invalid back reference") /* REG_ESUBREG */ + "\0" +#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference") + gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */ + "\0" +#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^") + gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */ + "\0" +#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(") + gettext_noop ("Unmatched \\{") /* REG_EBRACE */ + "\0" +#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{") + gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */ + "\0" +#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}") + gettext_noop ("Invalid range end") /* REG_ERANGE */ + "\0" +#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end") + gettext_noop ("Memory exhausted") /* REG_ESPACE */ + "\0" +#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted") + gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */ + "\0" +#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression") + gettext_noop ("Premature end of regular expression") /* REG_EEND */ + "\0" +#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression") + gettext_noop ("Regular expression too big") /* REG_ESIZE */ + "\0" +#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big") + gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */ + }; + +const size_t __re_error_msgid_idx[] attribute_hidden = + { + REG_NOERROR_IDX, + REG_NOMATCH_IDX, + REG_BADPAT_IDX, + REG_ECOLLATE_IDX, + REG_ECTYPE_IDX, + REG_EESCAPE_IDX, + REG_ESUBREG_IDX, + REG_EBRACK_IDX, + REG_EPAREN_IDX, + REG_EBRACE_IDX, + REG_BADBR_IDX, + REG_ERANGE_IDX, + REG_ESPACE_IDX, + REG_BADRPT_IDX, + REG_EEND_IDX, + REG_ESIZE_IDX, + REG_ERPAREN_IDX + }; + +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length LENGTH) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. */ + +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + size_t length; + struct re_pattern_buffer *bufp; +{ + reg_errcode_t ret; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub, unless RE_NO_SUB is set. */ + bufp->no_sub = !!(re_syntax_options & RE_NO_SUB); + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = re_compile_internal (bufp, pattern, length, re_syntax_options); + + if (!ret) + return NULL; + return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} +#ifdef _LIBC +weak_alias (__re_compile_pattern, re_compile_pattern) +#endif + +/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can + also be assigned to arbitrarily: each pattern buffer stores its own + syntax, so it can be changed between regex compilations. */ +/* This has no initializer because initialized variables in Emacs + become read-only after dumping. */ +reg_syntax_t re_syntax_options; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; +{ + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; + return ret; +} +#ifdef _LIBC +weak_alias (__re_set_syntax, re_set_syntax) +#endif + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + char *fastmap = bufp->fastmap; + + memset (fastmap, '\0', sizeof (char) * SBC_MAX); + re_compile_fastmap_iter (bufp, dfa->init_state, fastmap); + if (dfa->init_state != dfa->init_state_word) + re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap); + if (dfa->init_state != dfa->init_state_nl) + re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap); + if (dfa->init_state != dfa->init_state_begbuf) + re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap); + bufp->fastmap_accurate = 1; + return 0; +} +#ifdef _LIBC +weak_alias (__re_compile_fastmap, re_compile_fastmap) +#endif + +static inline void +__attribute ((always_inline)) +re_set_fastmap (char *fastmap, int icase, int ch) +{ + fastmap[ch] = 1; + if (icase) + fastmap[tolower (ch)] = 1; +} + +/* Helper function for re_compile_fastmap. + Compile fastmap for the initial_state INIT_STATE. */ + +static void +re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state, + char *fastmap) +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + int node_cnt; + int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE)); + for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt) + { + int node = init_state->nodes.elems[node_cnt]; + re_token_type_t type = dfa->nodes[node].type; + + if (type == CHARACTER) + { + re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c); +#ifdef RE_ENABLE_I18N + if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) + { + unsigned char *buf = alloca (dfa->mb_cur_max), *p; + wchar_t wc; + mbstate_t state; + + p = buf; + *p++ = dfa->nodes[node].opr.c; + while (++node < dfa->nodes_len + && dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].mb_partial) + *p++ = dfa->nodes[node].opr.c; + memset (&state, '\0', sizeof (state)); + if (mbrtowc (&wc, (const char *) buf, p - buf, + &state) == p - buf + && (__wcrtomb ((char *) buf, towlower (wc), &state) + != (size_t) -1)) + re_set_fastmap (fastmap, 0, buf[0]); + } +#endif + } + else if (type == SIMPLE_BRACKET) + { + int i, ch; + for (i = 0, ch = 0; i < BITSET_WORDS; ++i) + { + int j; + bitset_word_t w = dfa->nodes[node].opr.sbcset[i]; + for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) + if (w & ((bitset_word_t) 1 << j)) + re_set_fastmap (fastmap, icase, ch); + } + } +#ifdef RE_ENABLE_I18N + else if (type == COMPLEX_BRACKET) + { + int i; + re_charset_t *cset = dfa->nodes[node].opr.mbcset; + if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes + || cset->nranges || cset->nchar_classes) + { +# ifdef _LIBC + if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0) + { + /* In this case we want to catch the bytes which are + the first byte of any collation elements. + e.g. In da_DK, we want to catch 'a' since "aa" + is a valid collation element, and don't catch + 'b' since 'b' is the only collation element + which starts from 'b'. */ + const int32_t *table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + for (i = 0; i < SBC_MAX; ++i) + if (table[i] < 0) + re_set_fastmap (fastmap, icase, i); + } +# else + if (dfa->mb_cur_max > 1) + for (i = 0; i < SBC_MAX; ++i) + if (__btowc (i) == WEOF) + re_set_fastmap (fastmap, icase, i); +# endif /* not _LIBC */ + } + for (i = 0; i < cset->nmbchars; ++i) + { + char buf[256]; + mbstate_t state; + memset (&state, '\0', sizeof (state)); + if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1) + re_set_fastmap (fastmap, icase, *(unsigned char *) buf); + if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) + { + if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state) + != (size_t) -1) + re_set_fastmap (fastmap, 0, *(unsigned char *) buf); + } + } + } +#endif /* RE_ENABLE_I18N */ + else if (type == OP_PERIOD +#ifdef RE_ENABLE_I18N + || type == OP_UTF8_PERIOD +#endif /* RE_ENABLE_I18N */ + || type == END_OF_RE) + { + memset (fastmap, '\1', sizeof (char) * SBC_MAX); + if (type == END_OF_RE) + bufp->can_be_null = 1; + return; + } + } +} + +/* Entry point for POSIX code. */ +/* regcomp takes a regular expression as a string and compiles it. + + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set + + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' to an allocated space for the fastmap; + `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. + + PATTERN is the address of the pattern string. + + CFLAGS is a series of bits which affect compilation. + + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. + + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. + + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. + + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *__restrict preg; + const char *__restrict pattern; + int cflags; +{ + reg_errcode_t ret; + reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED + : RE_SYNTAX_POSIX_BASIC); + + preg->buffer = NULL; + preg->allocated = 0; + preg->used = 0; + + /* Try to allocate space for the fastmap. */ + preg->fastmap = re_malloc (char, SBC_MAX); + if (BE (preg->fastmap == NULL, 0)) + return REG_ESPACE; + + syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + preg->no_sub = !!(cflags & REG_NOSUB); + preg->translate = NULL; + + ret = re_compile_internal (preg, pattern, strlen (pattern), syntax); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) + ret = REG_EPAREN; + + /* We have already checked preg->fastmap != NULL. */ + if (BE (ret == REG_NOERROR, 1)) + /* Compute the fastmap now, since regexec cannot modify the pattern + buffer. This function never fails in this implementation. */ + (void) re_compile_fastmap (preg); + else + { + /* Some error occurred while compiling the expression. */ + re_free (preg->fastmap); + preg->fastmap = NULL; + } + + return (int) ret; +} +#ifdef _LIBC +weak_alias (__regcomp, regcomp) +#endif + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +/* regerror ( int errcode, preg, errbuf, errbuf_size) */ +size_t +regerror ( + int errcode, + const regex_t *__restrict preg, + char *__restrict errbuf, + size_t errbuf_size) +{ + const char *msg; + size_t msg_size; + + if (BE (errcode < 0 + || errcode >= (int) (sizeof (__re_error_msgid_idx) + / sizeof (__re_error_msgid_idx[0])), 0)) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]); + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (BE (errbuf_size != 0, 1)) + { + if (BE (msg_size > errbuf_size, 0)) + { +#if defined HAVE_MEMPCPY || defined _LIBC + *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0'; +#else + memcpy (errbuf, msg, errbuf_size - 1); + errbuf[errbuf_size - 1] = 0; +#endif + } + else + memcpy (errbuf, msg, msg_size); + } + + return msg_size; +} +#ifdef _LIBC +weak_alias (__regerror, regerror) +#endif + + +#ifdef RE_ENABLE_I18N +/* This static array is used for the map to single-byte characters when + UTF-8 is used. Otherwise we would allocate memory just to initialize + it the same all the time. UTF-8 is the preferred encoding so this is + a worthwhile optimization. */ +static const bitset_t utf8_sb_map = +{ + /* Set the first 128 bits. */ + [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX +}; +#endif + + +static void +free_dfa_content (re_dfa_t *dfa) +{ + int i, j; + + if (dfa->nodes) + for (i = 0; i < dfa->nodes_len; ++i) + free_token (dfa->nodes + i); + re_free (dfa->nexts); + for (i = 0; i < dfa->nodes_len; ++i) + { + if (dfa->eclosures != NULL) + re_node_set_free (dfa->eclosures + i); + if (dfa->inveclosures != NULL) + re_node_set_free (dfa->inveclosures + i); + if (dfa->edests != NULL) + re_node_set_free (dfa->edests + i); + } + re_free (dfa->edests); + re_free (dfa->eclosures); + re_free (dfa->inveclosures); + re_free (dfa->nodes); + + if (dfa->state_table) + for (i = 0; i <= dfa->state_hash_mask; ++i) + { + struct re_state_table_entry *entry = dfa->state_table + i; + for (j = 0; j < entry->num; ++j) + { + re_dfastate_t *state = entry->array[j]; + free_state (state); + } + re_free (entry->array); + } + re_free (dfa->state_table); +#ifdef RE_ENABLE_I18N + if (dfa->sb_char != utf8_sb_map) + re_free (dfa->sb_char); +#endif + re_free (dfa->subexp_map); +#ifdef DEBUG + re_free (dfa->re_str); +#endif + + re_free (dfa); +} + + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + if (BE (dfa != NULL, 1)) + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + + re_free (preg->fastmap); + preg->fastmap = NULL; + + re_free (preg->translate); + preg->translate = NULL; +} +#ifdef _LIBC +weak_alias (__regfree, regfree) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC + +/* BSD has one and only one pattern buffer. */ +static struct re_pattern_buffer re_comp_buf; + +char * +# ifdef _LIBC +/* Make these definitions weak in libc, so POSIX programs can redefine + these names if they don't use our functions, and still use + regcomp/regexec above without link errors. */ +weak_function +# endif +re_comp (s) + const char *s; +{ + reg_errcode_t ret; + char *fastmap; + + if (!s) + { + if (!re_comp_buf.buffer) + return gettext ("No previous regular expression"); + return 0; + } + + if (re_comp_buf.buffer) + { + fastmap = re_comp_buf.fastmap; + re_comp_buf.fastmap = NULL; + __regfree (&re_comp_buf); + memset (&re_comp_buf, '\0', sizeof (re_comp_buf)); + re_comp_buf.fastmap = fastmap; + } + + if (re_comp_buf.fastmap == NULL) + { + re_comp_buf.fastmap = (char *) malloc (SBC_MAX); + if (re_comp_buf.fastmap == NULL) + return (char *) gettext (__re_error_msgid + + __re_error_msgid_idx[(int) REG_ESPACE]); + } + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options); + + if (!ret) + return NULL; + + /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} + +#ifdef _LIBC +libc_freeres_fn (free_mem) +{ + __regfree (&re_comp_buf); +} +#endif + +#endif /* _REGEX_RE_COMP */ + +/* Internal entry point. + Compile the regular expression PATTERN, whose length is LENGTH. + SYNTAX indicate regular expression's syntax. */ + +static reg_errcode_t +re_compile_internal (regex_t *preg, const char * pattern, size_t length, + reg_syntax_t syntax) +{ + reg_errcode_t err = REG_NOERROR; + re_dfa_t *dfa; + re_string_t regexp; + + /* Initialize the pattern buffer. */ + preg->fastmap_accurate = 0; + preg->syntax = syntax; + preg->not_bol = preg->not_eol = 0; + preg->used = 0; + preg->re_nsub = 0; + preg->can_be_null = 0; + preg->regs_allocated = REGS_UNALLOCATED; + + /* Initialize the dfa. */ + dfa = (re_dfa_t *) preg->buffer; + if (BE (preg->allocated < sizeof (re_dfa_t), 0)) + { + /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. If ->buffer is NULL this + is a simple allocation. */ + dfa = re_realloc (preg->buffer, re_dfa_t, 1); + if (dfa == NULL) + return REG_ESPACE; + preg->allocated = sizeof (re_dfa_t); + preg->buffer = (unsigned char *) dfa; + } + preg->used = sizeof (re_dfa_t); + + err = init_dfa (dfa, length); + if (BE (err != REG_NOERROR, 0)) + { + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } +#ifdef DEBUG + /* Note: length+1 will not overflow since it is checked in init_dfa. */ + dfa->re_str = re_malloc (char, length + 1); + strncpy (dfa->re_str, pattern, length + 1); +#endif + + __libc_lock_init (dfa->lock); + + err = re_string_construct (®exp, pattern, length, preg->translate, + syntax & RE_ICASE, dfa); + if (BE (err != REG_NOERROR, 0)) + { + re_compile_internal_free_return: + free_workarea_compile (preg); + re_string_destruct (®exp); + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } + + /* Parse the regular expression, and build a structure tree. */ + preg->re_nsub = 0; + dfa->str_tree = parse (®exp, preg, syntax, &err); + if (BE (dfa->str_tree == NULL, 0)) + goto re_compile_internal_free_return; + + /* Analyze the tree and create the nfa. */ + err = analyze (preg); + if (BE (err != REG_NOERROR, 0)) + goto re_compile_internal_free_return; + +#ifdef RE_ENABLE_I18N + /* If possible, do searching in single byte encoding to speed things up. */ + if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL) + optimize_utf8 (dfa); +#endif + + /* Then create the initial state of the dfa. */ + err = create_initial_state (dfa); + + /* Release work areas. */ + free_workarea_compile (preg); + re_string_destruct (®exp); + + if (BE (err != REG_NOERROR, 0)) + { + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + } + + return err; +} + +/* Initialize DFA. We use the length of the regular expression PAT_LEN + as the initial length of some arrays. */ + +static reg_errcode_t +init_dfa (re_dfa_t *dfa, size_t pat_len) +{ + unsigned int table_size; +#ifndef _LIBC + char *codeset_name; +#endif + + memset (dfa, '\0', sizeof (re_dfa_t)); + + /* Force allocation of str_tree_storage the first time. */ + dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; + + /* Avoid overflows. */ + if (pat_len == SIZE_MAX) + return REG_ESPACE; + + dfa->nodes_alloc = pat_len + 1; + dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc); + + /* table_size = 2 ^ ceil(log pat_len) */ + for (table_size = 1; ; table_size <<= 1) + if (table_size > pat_len) + break; + + dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size); + dfa->state_hash_mask = table_size - 1; + + dfa->mb_cur_max = MB_CUR_MAX; +#ifdef _LIBC + if (dfa->mb_cur_max == 6 + && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0) + dfa->is_utf8 = 1; + dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII) + != 0); +#else +# ifdef HAVE_LANGINFO_CODESET + codeset_name = nl_langinfo (CODESET); +# else + codeset_name = getenv ("LC_ALL"); + if (codeset_name == NULL || codeset_name[0] == '\0') + codeset_name = getenv ("LC_CTYPE"); + if (codeset_name == NULL || codeset_name[0] == '\0') + codeset_name = getenv ("LANG"); + if (codeset_name == NULL) + codeset_name = ""; + else if (strchr (codeset_name, '.') != NULL) + codeset_name = strchr (codeset_name, '.') + 1; +# endif + + if (strcasecmp (codeset_name, "UTF-8") == 0 + || strcasecmp (codeset_name, "UTF8") == 0) + dfa->is_utf8 = 1; + + /* We check exhaustively in the loop below if this charset is a + superset of ASCII. */ + dfa->map_notascii = 0; +#endif + +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + if (dfa->is_utf8) + dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map; + else + { + int i, j, ch; + + dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); + if (BE (dfa->sb_char == NULL, 0)) + return REG_ESPACE; + + /* Set the bits corresponding to single byte chars. */ + for (i = 0, ch = 0; i < BITSET_WORDS; ++i) + for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) + { + wint_t wch = __btowc (ch); + if (wch != WEOF) + dfa->sb_char[i] |= (bitset_word_t) 1 << j; +# ifndef _LIBC + if (isascii (ch) && wch != ch) + dfa->map_notascii = 1; +# endif + } + } + } +#endif + + if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0)) + return REG_ESPACE; + return REG_NOERROR; +} + +/* Initialize WORD_CHAR table, which indicate which character is + "word". In this case "word" means that it is the word construction + character used by some operators like "\<", "\>", etc. */ + +static void +internal_function +init_word_char (re_dfa_t *dfa) +{ + int i, j, ch; + dfa->word_ops_used = 1; + for (i = 0, ch = 0; i < BITSET_WORDS; ++i) + for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) + if (isalnum (ch) || ch == '_') + dfa->word_char[i] |= (bitset_word_t) 1 << j; +} + +/* Free the work area which are only used while compiling. */ + +static void +free_workarea_compile (regex_t *preg) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_storage_t *storage, *next; + for (storage = dfa->str_tree_storage; storage; storage = next) + { + next = storage->next; + re_free (storage); + } + dfa->str_tree_storage = NULL; + dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; + dfa->str_tree = NULL; + re_free (dfa->org_indices); + dfa->org_indices = NULL; +} + +/* Create initial states for all contexts. */ + +static reg_errcode_t +create_initial_state (re_dfa_t *dfa) +{ + int first, i; + reg_errcode_t err; + re_node_set init_nodes; + + /* Initial states have the epsilon closure of the node which is + the first node of the regular expression. */ + first = dfa->str_tree->first->node_idx; + dfa->init_node = first; + err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* The back-references which are in initial states can epsilon transit, + since in this case all of the subexpressions can be null. + Then we add epsilon closures of the nodes which are the next nodes of + the back-references. */ + if (dfa->nbackref > 0) + for (i = 0; i < init_nodes.nelem; ++i) + { + int node_idx = init_nodes.elems[i]; + re_token_type_t type = dfa->nodes[node_idx].type; + + int clexp_idx; + if (type != OP_BACK_REF) + continue; + for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx) + { + re_token_t *clexp_node; + clexp_node = dfa->nodes + init_nodes.elems[clexp_idx]; + if (clexp_node->type == OP_CLOSE_SUBEXP + && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx) + break; + } + if (clexp_idx == init_nodes.nelem) + continue; + + if (type == OP_BACK_REF) + { + int dest_idx = dfa->edests[node_idx].elems[0]; + if (!re_node_set_contains (&init_nodes, dest_idx)) + { + re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx); + i = 0; + } + } + } + + /* It must be the first time to invoke acquire_state. */ + dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0); + /* We don't check ERR here, since the initial state must not be NULL. */ + if (BE (dfa->init_state == NULL, 0)) + return err; + if (dfa->init_state->has_constraint) + { + dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_WORD); + dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_NEWLINE); + dfa->init_state_begbuf = re_acquire_state_context (&err, dfa, + &init_nodes, + CONTEXT_NEWLINE + | CONTEXT_BEGBUF); + if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return err; + } + else + dfa->init_state_word = dfa->init_state_nl + = dfa->init_state_begbuf = dfa->init_state; + + re_node_set_free (&init_nodes); + return REG_NOERROR; +} + +#ifdef RE_ENABLE_I18N +/* If it is possible to do searching in single byte encoding instead of UTF-8 + to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change + DFA nodes where needed. */ + +static void +optimize_utf8 (re_dfa_t *dfa) +{ + int node, i, mb_chars = 0, has_period = 0; + + for (node = 0; node < dfa->nodes_len; ++node) + switch (dfa->nodes[node].type) + { + case CHARACTER: + if (dfa->nodes[node].opr.c >= 0x80) + mb_chars = 1; + break; + case ANCHOR: + switch (dfa->nodes[node].opr.idx) + { + case LINE_FIRST: + case LINE_LAST: + case BUF_FIRST: + case BUF_LAST: + break; + default: + /* Word anchors etc. cannot be handled. */ + return; + } + break; + case OP_PERIOD: + has_period = 1; + break; + case OP_BACK_REF: + case OP_ALT: + case END_OF_RE: + case OP_DUP_ASTERISK: + case OP_OPEN_SUBEXP: + case OP_CLOSE_SUBEXP: + break; + case COMPLEX_BRACKET: + return; + case SIMPLE_BRACKET: + /* Just double check. The non-ASCII range starts at 0x80. */ + assert (0x80 % BITSET_WORD_BITS == 0); + for (i = 0x80 / BITSET_WORD_BITS; i < BITSET_WORDS; ++i) + if (dfa->nodes[node].opr.sbcset[i]) + return; + break; + default: + abort (); + } + + if (mb_chars || has_period) + for (node = 0; node < dfa->nodes_len; ++node) + { + if (dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].opr.c >= 0x80) + dfa->nodes[node].mb_partial = 0; + else if (dfa->nodes[node].type == OP_PERIOD) + dfa->nodes[node].type = OP_UTF8_PERIOD; + } + + /* The search can be in single byte locale. */ + dfa->mb_cur_max = 1; + dfa->is_utf8 = 0; + dfa->has_mb_node = dfa->nbackref > 0 || has_period; +} +#endif + +/* Analyze the structure tree, and calculate "first", "next", "edest", + "eclosure", and "inveclosure". */ + +static reg_errcode_t +analyze (regex_t *preg) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + reg_errcode_t ret; + + /* Allocate arrays. */ + dfa->nexts = re_malloc (int, dfa->nodes_alloc); + dfa->org_indices = re_malloc (int, dfa->nodes_alloc); + dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc); + dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc); + if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL + || dfa->eclosures == NULL, 0)) + return REG_ESPACE; + + dfa->subexp_map = re_malloc (int, preg->re_nsub); + if (dfa->subexp_map != NULL) + { + int i; + for (i = 0; i < preg->re_nsub; i++) + dfa->subexp_map[i] = i; + preorder (dfa->str_tree, optimize_subexps, dfa); + for (i = 0; i < preg->re_nsub; i++) + if (dfa->subexp_map[i] != i) + break; + if (i == preg->re_nsub) + { + free (dfa->subexp_map); + dfa->subexp_map = NULL; + } + } + + ret = postorder (dfa->str_tree, lower_subexps, preg); + if (BE (ret != REG_NOERROR, 0)) + return ret; + ret = postorder (dfa->str_tree, calc_first, dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + preorder (dfa->str_tree, calc_next, dfa); + ret = preorder (dfa->str_tree, link_nfa_nodes, dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + ret = calc_eclosure (dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + /* We only need this during the prune_impossible_nodes pass in regexec.c; + skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */ + if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match) + || dfa->nbackref) + { + dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len); + if (BE (dfa->inveclosures == NULL, 0)) + return REG_ESPACE; + ret = calc_inveclosure (dfa); + } + + return ret; +} + +/* Our parse trees are very unbalanced, so we cannot use a stack to + implement parse tree visits. Instead, we use parent pointers and + some hairy code in these two functions. */ +static reg_errcode_t +postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra) +{ + bin_tree_t *node, *prev; + + for (node = root; ; ) + { + /* Descend down the tree, preferably to the left (or to the right + if that's the only child). */ + while (node->left || node->right) + if (node->left) + node = node->left; + else + node = node->right; + + do + { + reg_errcode_t err = fn (extra, node); + if (BE (err != REG_NOERROR, 0)) + return err; + if (node->parent == NULL) + return REG_NOERROR; + prev = node; + node = node->parent; + } + /* Go up while we have a node that is reached from the right. */ + while (node->right == prev || node->right == NULL); + node = node->right; + } +} + +static reg_errcode_t +preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra) +{ + bin_tree_t *node; + + for (node = root; ; ) + { + reg_errcode_t err = fn (extra, node); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* Go to the left node, or up and to the right. */ + if (node->left) + node = node->left; + else + { + bin_tree_t *prev = NULL; + while (node->right == prev || node->right == NULL) + { + prev = node; + node = node->parent; + if (!node) + return REG_NOERROR; + } + node = node->right; + } + } +} + +/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell + re_search_internal to map the inner one's opr.idx to this one's. Adjust + backreferences as well. Requires a preorder visit. */ +static reg_errcode_t +optimize_subexps (void *extra, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + + if (node->token.type == OP_BACK_REF && dfa->subexp_map) + { + int idx = node->token.opr.idx; + node->token.opr.idx = dfa->subexp_map[idx]; + dfa->used_bkref_map |= 1 << node->token.opr.idx; + } + + else if (node->token.type == SUBEXP + && node->left && node->left->token.type == SUBEXP) + { + int other_idx = node->left->token.opr.idx; + + node->left = node->left->left; + if (node->left) + node->left->parent = node; + + dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx]; + if (other_idx < BITSET_WORD_BITS) + dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx); + } + + return REG_NOERROR; +} + +/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation + of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */ +static reg_errcode_t +lower_subexps (void *extra, bin_tree_t *node) +{ + regex_t *preg = (regex_t *) extra; + reg_errcode_t err = REG_NOERROR; + + if (node->left && node->left->token.type == SUBEXP) + { + node->left = lower_subexp (&err, preg, node->left); + if (node->left) + node->left->parent = node; + } + if (node->right && node->right->token.type == SUBEXP) + { + node->right = lower_subexp (&err, preg, node->right); + if (node->right) + node->right->parent = node; + } + + return err; +} + +static bin_tree_t * +lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *body = node->left; + bin_tree_t *op, *cls, *tree1, *tree; + + if (preg->no_sub + /* We do not optimize empty subexpressions, because otherwise we may + have bad CONCAT nodes with NULL children. This is obviously not + very common, so we do not lose much. An example that triggers + this case is the sed "script" /\(\)/x. */ + && node->left != NULL + && (node->token.opr.idx >= BITSET_WORD_BITS + || !(dfa->used_bkref_map + & ((bitset_word_t) 1 << node->token.opr.idx)))) + return node->left; + + /* Convert the SUBEXP node to the concatenation of an + OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */ + op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP); + cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP); + tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls; + tree = create_tree (dfa, op, tree1, CONCAT); + if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + + op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx; + op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp; + return tree; +} + +/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton + nodes. Requires a postorder visit. */ +static reg_errcode_t +calc_first (void *extra, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + if (node->token.type == CONCAT) + { + node->first = node->left->first; + node->node_idx = node->left->node_idx; + } + else + { + node->first = node; + node->node_idx = re_dfa_add_node (dfa, node->token); + if (BE (node->node_idx == -1, 0)) + return REG_ESPACE; + } + return REG_NOERROR; +} + +/* Pass 2: compute NEXT on the tree. Preorder visit. */ +static reg_errcode_t +calc_next (void *extra, bin_tree_t *node) +{ + switch (node->token.type) + { + case OP_DUP_ASTERISK: + node->left->next = node; + break; + case CONCAT: + node->left->next = node->right->first; + node->right->next = node->next; + break; + default: + if (node->left) + node->left->next = node->next; + if (node->right) + node->right->next = node->next; + break; + } + return REG_NOERROR; +} + +/* Pass 3: link all DFA nodes to their NEXT node (any order will do). */ +static reg_errcode_t +link_nfa_nodes (void *extra, bin_tree_t *node) +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + int idx = node->node_idx; + reg_errcode_t err = REG_NOERROR; + + switch (node->token.type) + { + case CONCAT: + break; + + case END_OF_RE: + assert (node->next == NULL); + break; + + case OP_DUP_ASTERISK: + case OP_ALT: + { + int left, right; + dfa->has_plural_match = 1; + if (node->left != NULL) + left = node->left->first->node_idx; + else + left = node->next->node_idx; + if (node->right != NULL) + right = node->right->first->node_idx; + else + right = node->next->node_idx; + assert (left > -1); + assert (right > -1); + err = re_node_set_init_2 (dfa->edests + idx, left, right); + } + break; + + case ANCHOR: + case OP_OPEN_SUBEXP: + case OP_CLOSE_SUBEXP: + err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx); + break; + + case OP_BACK_REF: + dfa->nexts[idx] = node->next->node_idx; + if (node->token.type == OP_BACK_REF) + re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]); + break; + + default: + assert (!IS_EPSILON_NODE (node->token.type)); + dfa->nexts[idx] = node->next->node_idx; + break; + } + + return err; +} + +/* Duplicate the epsilon closure of the node ROOT_NODE. + Note that duplicated nodes have constraint INIT_CONSTRAINT in addition + to their own constraint. */ + +static reg_errcode_t +internal_function +duplicate_node_closure (re_dfa_t *dfa, int top_org_node, int top_clone_node, + int root_node, unsigned int init_constraint) +{ + int org_node, clone_node, ret; + unsigned int constraint = init_constraint; + for (org_node = top_org_node, clone_node = top_clone_node;;) + { + int org_dest, clone_dest; + if (dfa->nodes[org_node].type == OP_BACK_REF) + { + /* If the back reference epsilon-transit, its destination must + also have the constraint. Then duplicate the epsilon closure + of the destination of the back reference, and store it in + edests of the back reference. */ + org_dest = dfa->nexts[org_node]; + re_node_set_empty (dfa->edests + clone_node); + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == -1, 0)) + return REG_ESPACE; + dfa->nexts[clone_node] = dfa->nexts[org_node]; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + else if (dfa->edests[org_node].nelem == 0) + { + /* In case of the node can't epsilon-transit, don't duplicate the + destination and store the original destination as the + destination of the node. */ + dfa->nexts[clone_node] = dfa->nexts[org_node]; + break; + } + else if (dfa->edests[org_node].nelem == 1) + { + /* In case of the node can epsilon-transit, and it has only one + destination. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + if (dfa->nodes[org_node].type == ANCHOR) + { + /* In case of the node has another constraint, append it. */ + if (org_node == root_node && clone_node != org_node) + { + /* ...but if the node is root_node itself, it means the + epsilon closure have a loop, then tie it to the + destination of the root_node. */ + ret = re_node_set_insert (dfa->edests + clone_node, + org_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + break; + } + constraint |= dfa->nodes[org_node].opr.ctx_type; + } + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == -1, 0)) + return REG_ESPACE; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + else /* dfa->edests[org_node].nelem == 2 */ + { + /* In case of the node can epsilon-transit, and it has two + destinations. In the bin_tree_t and DFA, that's '|' and '*'. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + /* Search for a duplicated node which satisfies the constraint. */ + clone_dest = search_duplicated_node (dfa, org_dest, constraint); + if (clone_dest == -1) + { + /* There are no such a duplicated node, create a new one. */ + reg_errcode_t err; + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == -1, 0)) + return REG_ESPACE; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + err = duplicate_node_closure (dfa, org_dest, clone_dest, + root_node, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + { + /* There are a duplicated node which satisfy the constraint, + use it to avoid infinite loop. */ + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + + org_dest = dfa->edests[org_node].elems[1]; + clone_dest = duplicate_node (dfa, org_dest, constraint); + if (BE (clone_dest == -1, 0)) + return REG_ESPACE; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + org_node = org_dest; + clone_node = clone_dest; + } + return REG_NOERROR; +} + +/* Search for a node which is duplicated from the node ORG_NODE, and + satisfies the constraint CONSTRAINT. */ + +static int +search_duplicated_node (const re_dfa_t *dfa, int org_node, + unsigned int constraint) +{ + int idx; + for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx) + { + if (org_node == dfa->org_indices[idx] + && constraint == dfa->nodes[idx].constraint) + return idx; /* Found. */ + } + return -1; /* Not found. */ +} + +/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT. + Return the index of the new node, or -1 if insufficient storage is + available. */ + +static int +duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint) +{ + int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]); + if (BE (dup_idx != -1, 1)) + { + dfa->nodes[dup_idx].constraint = constraint; + if (dfa->nodes[org_idx].type == ANCHOR) + dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type; + dfa->nodes[dup_idx].duplicated = 1; + + /* Store the index of the original node. */ + dfa->org_indices[dup_idx] = org_idx; + } + return dup_idx; +} + +static reg_errcode_t +calc_inveclosure (re_dfa_t *dfa) +{ + int src, idx, ret; + for (idx = 0; idx < dfa->nodes_len; ++idx) + re_node_set_init_empty (dfa->inveclosures + idx); + + for (src = 0; src < dfa->nodes_len; ++src) + { + int *elems = dfa->eclosures[src].elems; + for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx) + { + ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src); + if (BE (ret == -1, 0)) + return REG_ESPACE; + } + } + + return REG_NOERROR; +} + +/* Calculate "eclosure" for all the node in DFA. */ + +static reg_errcode_t +calc_eclosure (re_dfa_t *dfa) +{ + int node_idx, incomplete; +#ifdef DEBUG + assert (dfa->nodes_len > 0); +#endif + incomplete = 0; + /* For each nodes, calculate epsilon closure. */ + for (node_idx = 0; ; ++node_idx) + { + reg_errcode_t err; + re_node_set eclosure_elem; + if (node_idx == dfa->nodes_len) + { + if (!incomplete) + break; + incomplete = 0; + node_idx = 0; + } + +#ifdef DEBUG + assert (dfa->eclosures[node_idx].nelem != -1); +#endif + + /* If we have already calculated, skip it. */ + if (dfa->eclosures[node_idx].nelem != 0) + continue; + /* Calculate epsilon closure of `node_idx'. */ + err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (dfa->eclosures[node_idx].nelem == 0) + { + incomplete = 1; + re_node_set_free (&eclosure_elem); + } + } + return REG_NOERROR; +} + +/* Calculate epsilon closure of NODE. */ + +static reg_errcode_t +calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root) +{ + reg_errcode_t err; + unsigned int constraint; + int i, incomplete; + re_node_set eclosure; + incomplete = 0; + err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* This indicates that we are calculating this node now. + We reference this value to avoid infinite loop. */ + dfa->eclosures[node].nelem = -1; + + constraint = ((dfa->nodes[node].type == ANCHOR) + ? dfa->nodes[node].opr.ctx_type : 0); + /* If the current node has constraints, duplicate all nodes. + Since they must inherit the constraints. */ + if (constraint + && dfa->edests[node].nelem + && !dfa->nodes[dfa->edests[node].elems[0]].duplicated) + { + err = duplicate_node_closure (dfa, node, node, node, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Expand each epsilon destination nodes. */ + if (IS_EPSILON_NODE(dfa->nodes[node].type)) + for (i = 0; i < dfa->edests[node].nelem; ++i) + { + re_node_set eclosure_elem; + int edest = dfa->edests[node].elems[i]; + /* If calculating the epsilon closure of `edest' is in progress, + return intermediate result. */ + if (dfa->eclosures[edest].nelem == -1) + { + incomplete = 1; + continue; + } + /* If we haven't calculated the epsilon closure of `edest' yet, + calculate now. Otherwise use calculated epsilon closure. */ + if (dfa->eclosures[edest].nelem == 0) + { + err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + eclosure_elem = dfa->eclosures[edest]; + /* Merge the epsilon closure of `edest'. */ + re_node_set_merge (&eclosure, &eclosure_elem); + /* If the epsilon closure of `edest' is incomplete, + the epsilon closure of this node is also incomplete. */ + if (dfa->eclosures[edest].nelem == 0) + { + incomplete = 1; + re_node_set_free (&eclosure_elem); + } + } + + /* Epsilon closures include itself. */ + re_node_set_insert (&eclosure, node); + if (incomplete && !root) + dfa->eclosures[node].nelem = 0; + else + dfa->eclosures[node] = eclosure; + *new_set = eclosure; + return REG_NOERROR; +} + +/* Functions for token which are used in the parser. */ + +/* Fetch a token from INPUT. + We must not use this function inside bracket expressions. */ + +static void +internal_function +fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax) +{ + re_string_skip_bytes (input, peek_token (result, input, syntax)); +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function inside bracket expressions. */ + +static int +internal_function +peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax) +{ + unsigned char c; + + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + + c = re_string_peek_byte (input, 0); + token->opr.c = c; + + token->word_char = 0; +#ifdef RE_ENABLE_I18N + token->mb_partial = 0; + if (input->mb_cur_max > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + token->mb_partial = 1; + return 1; + } +#endif + if (c == '\\') + { + unsigned char c2; + if (re_string_cur_idx (input) + 1 >= re_string_length (input)) + { + token->type = BACK_SLASH; + return 1; + } + + c2 = re_string_peek_byte_case (input, 1); + token->opr.c = c2; + token->type = CHARACTER; +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc = re_string_wchar_at (input, + re_string_cur_idx (input) + 1); + token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; + } + else +#endif + token->word_char = IS_WORD_CHAR (c2) != 0; + + switch (c2) + { + case '|': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (!(syntax & RE_NO_BK_REFS)) + { + token->type = OP_BACK_REF; + token->opr.idx = c2 - '1'; + } + break; + case '<': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_FIRST; + } + break; + case '>': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_LAST; + } + break; + case 'b': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_DELIM; + } + break; + case 'B': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = NOT_WORD_DELIM; + } + break; + case 'w': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_WORD; + break; + case 'W': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_NOTWORD; + break; + case 's': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_SPACE; + break; + case 'S': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_NOTSPACE; + break; + case '`': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = BUF_FIRST; + } + break; + case '\'': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = BUF_LAST; + } + break; + case '(': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_CLOSE_SUBEXP; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_CLOSE_DUP_NUM; + break; + default: + break; + } + return 2; + } + + token->type = CHARACTER; +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input)); + token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; + } + else +#endif + token->word_char = IS_WORD_CHAR (token->opr.c); + + switch (c) + { + case '\n': + if (syntax & RE_NEWLINE_ALT) + token->type = OP_ALT; + break; + case '|': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '*': + token->type = OP_DUP_ASTERISK; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_CLOSE_DUP_NUM; + break; + case '(': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_CLOSE_SUBEXP; + break; + case '[': + token->type = OP_OPEN_BRACKET; + break; + case '.': + token->type = OP_PERIOD; + break; + case '^': + if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) && + re_string_cur_idx (input) != 0) + { + char prev = re_string_peek_byte (input, -1); + if (!(syntax & RE_NEWLINE_ALT) || prev != '\n') + break; + } + token->type = ANCHOR; + token->opr.ctx_type = LINE_FIRST; + break; + case '$': + if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) && + re_string_cur_idx (input) + 1 != re_string_length (input)) + { + re_token_t next; + re_string_skip_bytes (input, 1); + peek_token (&next, input, syntax); + re_string_skip_bytes (input, -1); + if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP) + break; + } + token->type = ANCHOR; + token->opr.ctx_type = LINE_LAST; + break; + default: + break; + } + return 1; +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function out of bracket expressions. */ + +static int +internal_function +peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax) +{ + unsigned char c; + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + c = re_string_peek_byte (input, 0); + token->opr.c = c; + +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + return 1; + } +#endif /* RE_ENABLE_I18N */ + + if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) + && re_string_cur_idx (input) + 1 < re_string_length (input)) + { + /* In this case, '\' escape a character. */ + unsigned char c2; + re_string_skip_bytes (input, 1); + c2 = re_string_peek_byte (input, 0); + token->opr.c = c2; + token->type = CHARACTER; + return 1; + } + if (c == '[') /* '[' is a special char in a bracket exps. */ + { + unsigned char c2; + int token_len; + if (re_string_cur_idx (input) + 1 < re_string_length (input)) + c2 = re_string_peek_byte (input, 1); + else + c2 = 0; + token->opr.c = c2; + token_len = 2; + switch (c2) + { + case '.': + token->type = OP_OPEN_COLL_ELEM; + break; + case '=': + token->type = OP_OPEN_EQUIV_CLASS; + break; + case ':': + if (syntax & RE_CHAR_CLASSES) + { + token->type = OP_OPEN_CHAR_CLASS; + break; + } + /* else fall through. */ + default: + token->type = CHARACTER; + token->opr.c = c; + token_len = 1; + break; + } + return token_len; + } + switch (c) + { + case '-': + token->type = OP_CHARSET_RANGE; + break; + case ']': + token->type = OP_CLOSE_BRACKET; + break; + case '^': + token->type = OP_NON_MATCH_LIST; + break; + default: + token->type = CHARACTER; + } + return 1; +} + +/* Functions for parser. */ + +/* Entry point of the parser. + Parse the regular expression REGEXP and return the structure tree. + If an error is occured, ERR is set by error code, and return NULL. + This function build the following tree, from regular expression : + CAT + / \ + / \ + EOR + + CAT means concatenation. + EOR means end of regular expression. */ + +static bin_tree_t * +parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax, + reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *eor, *root; + re_token_t current_token; + dfa->syntax = syntax; + fetch_token (¤t_token, regexp, syntax | RE_CARET_ANCHORS_HERE); + tree = parse_reg_exp (regexp, preg, ¤t_token, syntax, 0, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + eor = create_tree (dfa, NULL, NULL, END_OF_RE); + if (tree != NULL) + root = create_tree (dfa, tree, eor, CONCAT); + else + root = eor; + if (BE (eor == NULL || root == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + return root; +} + +/* This function build the following tree, from regular expression + |: + ALT + / \ + / \ + + + ALT means alternative, which represents the operator `|'. */ + +static bin_tree_t * +parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, int nest, reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *branch = NULL; + tree = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type == OP_ALT) + { + fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); + if (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + branch = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && branch == NULL, 0)) + return NULL; + } + else + branch = NULL; + tree = create_tree (dfa, tree, branch, OP_ALT); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + return tree; +} + +/* This function build the following tree, from regular expression + : + CAT + / \ + / \ + + + CAT means concatenation. */ + +static bin_tree_t * +parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, int nest, reg_errcode_t *err) +{ + bin_tree_t *tree, *exp; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + tree = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + exp = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && exp == NULL, 0)) + { + return NULL; + } + if (tree != NULL && exp != NULL) + { + tree = create_tree (dfa, tree, exp, CONCAT); + if (tree == NULL) + { + *err = REG_ESPACE; + return NULL; + } + } + else if (tree == NULL) + tree = exp; + /* Otherwise exp == NULL, we don't need to create new tree. */ + } + return tree; +} + +/* This function build the following tree, from regular expression a*: + * + | + a +*/ + +static bin_tree_t * +parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, int nest, reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree; + switch (token->type) + { + case CHARACTER: + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + while (!re_string_eoi (regexp) + && !re_string_first_byte (regexp, re_string_cur_idx (regexp))) + { + bin_tree_t *mbc_remain; + fetch_token (token, regexp, syntax); + mbc_remain = create_token_tree (dfa, NULL, NULL, token); + tree = create_tree (dfa, tree, mbc_remain, CONCAT); + if (BE (mbc_remain == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + } +#endif + break; + case OP_OPEN_SUBEXP: + tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_OPEN_BRACKET: + tree = parse_bracket_exp (regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_BACK_REF: + if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1)) + { + *err = REG_ESUBREG; + return NULL; + } + dfa->used_bkref_map |= 1 << token->opr.idx; + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + ++dfa->nbackref; + dfa->has_mb_node = 1; + break; + case OP_OPEN_DUP_NUM: + if (syntax & RE_CONTEXT_INVALID_DUP) + { + *err = REG_BADRPT; + return NULL; + } + /* FALLTHROUGH */ + case OP_DUP_ASTERISK: + case OP_DUP_PLUS: + case OP_DUP_QUESTION: + if (syntax & RE_CONTEXT_INVALID_OPS) + { + *err = REG_BADRPT; + return NULL; + } + else if (syntax & RE_CONTEXT_INDEP_OPS) + { + fetch_token (token, regexp, syntax); + return parse_expression (regexp, preg, token, syntax, nest, err); + } + /* else fall through */ + case OP_CLOSE_SUBEXP: + if ((token->type == OP_CLOSE_SUBEXP) && + !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)) + { + *err = REG_ERPAREN; + return NULL; + } + /* else fall through */ + case OP_CLOSE_DUP_NUM: + /* We treat it as a normal character. */ + + /* Then we can these characters as normal characters. */ + token->type = CHARACTER; + /* mb_partial and word_char bits should be initialized already + by peek_token. */ + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + break; + case ANCHOR: + if ((token->opr.ctx_type + & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST)) + && dfa->word_ops_used == 0) + init_word_char (dfa); + if (token->opr.ctx_type == WORD_DELIM + || token->opr.ctx_type == NOT_WORD_DELIM) + { + bin_tree_t *tree_first, *tree_last; + if (token->opr.ctx_type == WORD_DELIM) + { + token->opr.ctx_type = WORD_FIRST; + tree_first = create_token_tree (dfa, NULL, NULL, token); + token->opr.ctx_type = WORD_LAST; + } + else + { + token->opr.ctx_type = INSIDE_WORD; + tree_first = create_token_tree (dfa, NULL, NULL, token); + token->opr.ctx_type = INSIDE_NOTWORD; + } + tree_last = create_token_tree (dfa, NULL, NULL, token); + tree = create_tree (dfa, tree_first, tree_last, OP_ALT); + if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + else + { + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + /* We must return here, since ANCHORs can't be followed + by repetition operators. + eg. RE"^*" is invalid or "", + it must not be "". */ + fetch_token (token, regexp, syntax); + return tree; + case OP_PERIOD: + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + if (dfa->mb_cur_max > 1) + dfa->has_mb_node = 1; + break; + case OP_WORD: + case OP_NOTWORD: + tree = build_charclass_op (dfa, regexp->trans, + (const unsigned char *) "alnum", + (const unsigned char *) "_", + token->type == OP_NOTWORD, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_SPACE: + case OP_NOTSPACE: + tree = build_charclass_op (dfa, regexp->trans, + (const unsigned char *) "space", + (const unsigned char *) "", + token->type == OP_NOTSPACE, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_ALT: + case END_OF_RE: + return NULL; + case BACK_SLASH: + *err = REG_EESCAPE; + return NULL; + default: + /* Must not happen? */ +#ifdef DEBUG + assert (0); +#endif + return NULL; + } + fetch_token (token, regexp, syntax); + + while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS + || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM) + { + tree = parse_dup_op (tree, regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + /* In BRE consecutive duplications are not allowed. */ + if ((syntax & RE_CONTEXT_INVALID_DUP) + && (token->type == OP_DUP_ASTERISK + || token->type == OP_OPEN_DUP_NUM)) + { + *err = REG_BADRPT; + return NULL; + } + } + + return tree; +} + +/* This function build the following tree, from regular expression + (): + SUBEXP + | + +*/ + +static bin_tree_t * +parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, + reg_syntax_t syntax, int nest, reg_errcode_t *err) +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree; + size_t cur_nsub; + cur_nsub = preg->re_nsub++; + + fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); + + /* The subexpression may be a null string. */ + if (token->type == OP_CLOSE_SUBEXP) + tree = NULL; + else + { + tree = parse_reg_exp (regexp, preg, token, syntax, nest, err); + if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0)) + *err = REG_EPAREN; + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + + if (cur_nsub <= '9' - '1') + dfa->completed_bkref_map |= 1 << cur_nsub; + + tree = create_tree (dfa, tree, NULL, SUBEXP); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + tree->token.opr.idx = cur_nsub; + return tree; +} + +/* This function parse repetition operators like "*", "+", "{1,3}" etc. */ + +static bin_tree_t * +parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa, + re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err) +{ + bin_tree_t *tree = NULL, *old_tree = NULL; + int i, start, end, start_idx = re_string_cur_idx (regexp); + re_token_t start_token = *token; + + if (token->type == OP_OPEN_DUP_NUM) + { + end = 0; + start = fetch_number (regexp, token, syntax); + if (start == -1) + { + if (token->type == CHARACTER && token->opr.c == ',') + start = 0; /* We treat "{,m}" as "{0,m}". */ + else + { + *err = REG_BADBR; /* {} is invalid. */ + return NULL; + } + } + if (BE (start != -2, 1)) + { + /* We treat "{n}" as "{n,n}". */ + end = ((token->type == OP_CLOSE_DUP_NUM) ? start + : ((token->type == CHARACTER && token->opr.c == ',') + ? fetch_number (regexp, token, syntax) : -2)); + } + if (BE (start == -2 || end == -2, 0)) + { + /* Invalid sequence. */ + if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0)) + { + if (token->type == END_OF_RE) + *err = REG_EBRACE; + else + *err = REG_BADBR; + + return NULL; + } + + /* If the syntax bit is set, rollback. */ + re_string_set_index (regexp, start_idx); + *token = start_token; + token->type = CHARACTER; + /* mb_partial and word_char bits should be already initialized by + peek_token. */ + return elem; + } + + if (BE (end != -1 && start > end, 0)) + { + /* First number greater than second. */ + *err = REG_BADBR; + return NULL; + } + } + else + { + start = (token->type == OP_DUP_PLUS) ? 1 : 0; + end = (token->type == OP_DUP_QUESTION) ? 1 : -1; + } + + fetch_token (token, regexp, syntax); + + if (BE (elem == NULL, 0)) + return NULL; + if (BE (start == 0 && end == 0, 0)) + { + postorder (elem, free_tree, NULL); + return NULL; + } + + /* Extract "{n,m}" to "...{0,}". */ + if (BE (start > 0, 0)) + { + tree = elem; + for (i = 2; i <= start; ++i) + { + elem = duplicate_tree (elem, dfa); + tree = create_tree (dfa, tree, elem, CONCAT); + if (BE (elem == NULL || tree == NULL, 0)) + goto parse_dup_op_espace; + } + + if (start == end) + return tree; + + /* Duplicate ELEM before it is marked optional. */ + elem = duplicate_tree (elem, dfa); + old_tree = tree; + } + else + old_tree = NULL; + + if (elem->token.type == SUBEXP) + postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx); + + tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT)); + if (BE (tree == NULL, 0)) + goto parse_dup_op_espace; + + /* This loop is actually executed only when end != -1, + to rewrite {0,n} as ((...?)?)?... We have + already created the start+1-th copy. */ + for (i = start + 2; i <= end; ++i) + { + elem = duplicate_tree (elem, dfa); + tree = create_tree (dfa, tree, elem, CONCAT); + if (BE (elem == NULL || tree == NULL, 0)) + goto parse_dup_op_espace; + + tree = create_tree (dfa, tree, NULL, OP_ALT); + if (BE (tree == NULL, 0)) + goto parse_dup_op_espace; + } + + if (old_tree) + tree = create_tree (dfa, old_tree, tree, CONCAT); + + return tree; + + parse_dup_op_espace: + *err = REG_ESPACE; + return NULL; +} + +/* Size of the names for collating symbol/equivalence_class/character_class. + I'm not sure, but maybe enough. */ +#define BRACKET_NAME_BUF_SIZE 32 + +#ifndef _LIBC + /* Local function for parse_bracket_exp only used in case of NOT _LIBC. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + +static reg_errcode_t +internal_function +# ifdef RE_ENABLE_I18N +build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc, + bracket_elem_t *start_elem, bracket_elem_t *end_elem) +# else /* not RE_ENABLE_I18N */ +build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem, + bracket_elem_t *end_elem) +# endif /* not RE_ENABLE_I18N */ +{ + unsigned int start_ch, end_ch; + /* Equivalence Classes and Character Classes can't be a range start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + /* We can handle no multi character collating elements without libc + support. */ + if (BE ((start_elem->type == COLL_SYM + && strlen ((char *) start_elem->opr.name) > 1) + || (end_elem->type == COLL_SYM + && strlen ((char *) end_elem->opr.name) > 1), 0)) + return REG_ECOLLATE; + +# ifdef RE_ENABLE_I18N + { + wchar_t wc; + wint_t start_wc; + wint_t end_wc; + wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + + start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM) + ? __btowc (start_ch) : start_elem->opr.wch); + end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) + ? __btowc (end_ch) : end_elem->opr.wch); + if (start_wc == WEOF || end_wc == WEOF) + return REG_ECOLLATE; + cmp_buf[0] = start_wc; + cmp_buf[4] = end_wc; + if (wcscoll (cmp_buf, cmp_buf + 4) > 0) + return REG_ERANGE; + + /* Got valid collation sequence values, add them as a new entry. + However, for !_LIBC we have no collation elements: if the + character set is single byte, the single byte character set + that we build below suffices. parse_bracket_exp passes + no MBCSET if dfa->mb_cur_max == 1. */ + if (mbcset) + { + /* Check the space of the arrays. */ + if (BE (*range_alloc == mbcset->nranges, 0)) + { + /* There is not enough space, need realloc. */ + wchar_t *new_array_start, *new_array_end; + int new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + /* Use realloc since mbcset->range_starts and mbcset->range_ends + are NULL if *range_alloc == 0. */ + new_array_start = re_realloc (mbcset->range_starts, wchar_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, wchar_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } + + mbcset->range_starts[mbcset->nranges] = start_wc; + mbcset->range_ends[mbcset->nranges++] = end_wc; + } + + /* Build the table for single byte characters. */ + for (wc = 0; wc < SBC_MAX; ++wc) + { + cmp_buf[2] = wc; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + bitset_set (sbcset, wc); + } + } +# else /* not RE_ENABLE_I18N */ + { + unsigned int ch; + start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + if (start_ch > end_ch) + return REG_ERANGE; + /* Build the table for single byte characters. */ + for (ch = 0; ch < SBC_MAX; ++ch) + if (start_ch <= ch && ch <= end_ch) + bitset_set (sbcset, ch); + } +# endif /* not RE_ENABLE_I18N */ + return REG_NOERROR; +} +#endif /* not _LIBC */ + +#ifndef _LIBC +/* Helper function for parse_bracket_exp only used in case of NOT _LIBC.. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument since we may update it. */ + +static reg_errcode_t +internal_function +# ifdef RE_ENABLE_I18N +build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset, + int *coll_sym_alloc, const unsigned char *name) +# else /* not RE_ENABLE_I18N */ +build_collating_symbol (bitset_t sbcset, const unsigned char *name) +# endif /* not RE_ENABLE_I18N */ +{ + size_t name_len = strlen ((const char *) name); + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } +} +#endif /* not _LIBC */ + +/* This function parse bracket expression like "[abc]", "[a-c]", + "[[.a-a.]]" etc. */ + +static bin_tree_t * +parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, + reg_syntax_t syntax, reg_errcode_t *err) +{ +#ifdef _LIBC + const unsigned char *collseqmb; + const char *collseqwc; + uint32_t nrules; + int32_t table_size; + const int32_t *symb_table; + const unsigned char *extra; + + /* Local function for parse_bracket_exp used in _LIBC environement. + Seek the collating symbol entry correspondings to NAME. + Return the index of the symbol in the SYMB_TABLE. */ + + auto inline int32_t + __attribute ((always_inline)) + seek_collating_symbol_entry (name, name_len) + const unsigned char *name; + size_t name_len; + { + int32_t hash = elem_hash ((const char *) name, name_len); + int32_t elem = hash % table_size; + if (symb_table[2 * elem] != 0) + { + int32_t second = hash % (table_size - 2) + 1; + + do + { + /* First compare the hashing value. */ + if (symb_table[2 * elem] == hash + /* Compare the length of the name. */ + && name_len == extra[symb_table[2 * elem + 1]] + /* Compare the name. */ + && memcmp (name, &extra[symb_table[2 * elem + 1] + 1], + name_len) == 0) + { + /* Yep, this is the entry. */ + break; + } + + /* Next entry. */ + elem += second; + } + while (symb_table[2 * elem] != 0); + } + return elem; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Look up the collation sequence value of BR_ELEM. + Return the value if succeeded, UINT_MAX otherwise. */ + + auto inline unsigned int + __attribute ((always_inline)) + lookup_collation_sequence_value (br_elem) + bracket_elem_t *br_elem; + { + if (br_elem->type == SB_CHAR) + { + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + return collseqmb[br_elem->opr.ch]; + else + { + wint_t wc = __btowc (br_elem->opr.ch); + return __collseq_table_lookup (collseqwc, wc); + } + } + else if (br_elem->type == MB_CHAR) + { + return __collseq_table_lookup (collseqwc, br_elem->opr.wch); + } + else if (br_elem->type == COLL_SYM) + { + size_t sym_name_len = strlen ((char *) br_elem->opr.name); + if (nrules != 0) + { + int32_t elem, idx; + elem = seek_collating_symbol_entry (br_elem->opr.name, + sym_name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + /* Skip the byte sequence of the collating element. */ + idx += 1 + extra[idx]; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the multibyte collation sequence value. */ + idx += sizeof (unsigned int); + /* Skip the wide char sequence of the collating element. */ + idx += sizeof (unsigned int) * + (1 + *(unsigned int *) (extra + idx)); + /* Return the collation sequence value. */ + return *(unsigned int *) (extra + idx); + } + else if (symb_table[2 * elem] == 0 && sym_name_len == 1) + { + /* No valid character. Match it as a single byte + character. */ + return collseqmb[br_elem->opr.name[0]]; + } + } + else if (sym_name_len == 1) + return collseqmb[br_elem->opr.name[0]]; + } + return UINT_MAX; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + + auto inline reg_errcode_t + __attribute ((always_inline)) + build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem) + re_charset_t *mbcset; + int *range_alloc; + bitset_t sbcset; + bracket_elem_t *start_elem, *end_elem; + { + unsigned int ch; + uint32_t start_collseq; + uint32_t end_collseq; + + /* Equivalence Classes and Character Classes can't be a range + start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + start_collseq = lookup_collation_sequence_value (start_elem); + end_collseq = lookup_collation_sequence_value (end_elem); + /* Check start/end collation sequence values. */ + if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0)) + return REG_ECOLLATE; + if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0)) + return REG_ERANGE; + + /* Got valid collation sequence values, add them as a new entry. + However, if we have no collation elements, and the character set + is single byte, the single byte character set that we + build below suffices. */ + if (nrules > 0 || dfa->mb_cur_max > 1) + { + /* Check the space of the arrays. */ + if (BE (*range_alloc == mbcset->nranges, 0)) + { + /* There is not enough space, need realloc. */ + uint32_t *new_array_start; + uint32_t *new_array_end; + int new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + new_array_start = re_realloc (mbcset->range_starts, uint32_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, uint32_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } + + mbcset->range_starts[mbcset->nranges] = start_collseq; + mbcset->range_ends[mbcset->nranges++] = end_collseq; + } + + /* Build the table for single byte characters. */ + for (ch = 0; ch < SBC_MAX; ch++) + { + uint32_t ch_collseq; + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + ch_collseq = collseqmb[ch]; + else + ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch)); + if (start_collseq <= ch_collseq && ch_collseq <= end_collseq) + bitset_set (sbcset, ch); + } + return REG_NOERROR; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument sinse we may update it. */ + + auto inline reg_errcode_t + __attribute ((always_inline)) + build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name) + re_charset_t *mbcset; + int *coll_sym_alloc; + bitset_t sbcset; + const unsigned char *name; + { + int32_t elem, idx; + size_t name_len = strlen ((const char *) name); + if (nrules != 0) + { + elem = seek_collating_symbol_entry (name, name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + } + else if (symb_table[2 * elem] == 0 && name_len == 1) + { + /* No valid character, treat it as a normal + character. */ + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + else + return REG_ECOLLATE; + + /* Got valid collation sequence, add it as a new entry. */ + /* Check the space of the arrays. */ + if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->ncoll_syms is 0. */ + int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1; + /* Use realloc since mbcset->coll_syms is NULL + if *alloc == 0. */ + int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t, + new_coll_sym_alloc); + if (BE (new_coll_syms == NULL, 0)) + return REG_ESPACE; + mbcset->coll_syms = new_coll_syms; + *coll_sym_alloc = new_coll_sym_alloc; + } + mbcset->coll_syms[mbcset->ncoll_syms++] = idx; + return REG_NOERROR; + } + else + { + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + } + } +#endif + + re_token_t br_token; + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0; + int equiv_class_alloc = 0, char_class_alloc = 0; +#endif /* not RE_ENABLE_I18N */ + int non_match = 0; + bin_tree_t *work_tree; + int token_len; + int first_round = 1; +#ifdef _LIBC + collseqmb = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules) + { + /* + if (MB_CUR_MAX > 1) + */ + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB); + symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_TABLEMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_EXTRAMB); + } +#endif + sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else + if (BE (sbcset == NULL, 0)) +#endif /* RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_NON_MATCH_LIST) + { +#ifdef RE_ENABLE_I18N + mbcset->non_match = 1; +#endif /* not RE_ENABLE_I18N */ + non_match = 1; + if (syntax & RE_HAT_LISTS_NOT_NEWLINE) + bitset_set (sbcset, '\0'); + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + } + + /* We treat the first ']' as a normal character. */ + if (token->type == OP_CLOSE_BRACKET) + token->type = CHARACTER; + + while (1) + { + bracket_elem_t start_elem, end_elem; + unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE]; + unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE]; + reg_errcode_t ret; + int token_len2 = 0, is_range_exp = 0; + re_token_t token2; + + start_elem.opr.name = start_name_buf; + ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa, + syntax, first_round); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + first_round = 0; + + /* Get information about the next token. We need it in any case. */ + token_len = peek_token_bracket (token, regexp, syntax); + + /* Do not check for ranges if we know they are not allowed. */ + if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS) + { + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_CHARSET_RANGE) + { + re_string_skip_bytes (regexp, token_len); /* Skip '-'. */ + token_len2 = peek_token_bracket (&token2, regexp, syntax); + if (BE (token2.type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token2.type == OP_CLOSE_BRACKET) + { + /* We treat the last '-' as a normal character. */ + re_string_skip_bytes (regexp, -token_len); + token->type = CHARACTER; + } + else + is_range_exp = 1; + } + } + + if (is_range_exp == 1) + { + end_elem.opr.name = end_name_buf; + ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2, + dfa, syntax, 1); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + + token_len = peek_token_bracket (token, regexp, syntax); + +#ifdef _LIBC + *err = build_range_exp (sbcset, mbcset, &range_alloc, + &start_elem, &end_elem); +#else +# ifdef RE_ENABLE_I18N + *err = build_range_exp (sbcset, + dfa->mb_cur_max > 1 ? mbcset : NULL, + &range_alloc, &start_elem, &end_elem); +# else + *err = build_range_exp (sbcset, &start_elem, &end_elem); +# endif +#endif /* RE_ENABLE_I18N */ + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + } + else + { + switch (start_elem.type) + { + case SB_CHAR: + bitset_set (sbcset, start_elem.opr.ch); + break; +#ifdef RE_ENABLE_I18N + case MB_CHAR: + /* Check whether the array has enough space. */ + if (BE (mbchar_alloc == mbcset->nmbchars, 0)) + { + wchar_t *new_mbchars; + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nmbchars is 0. */ + mbchar_alloc = 2 * mbcset->nmbchars + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + new_mbchars = re_realloc (mbcset->mbchars, wchar_t, + mbchar_alloc); + if (BE (new_mbchars == NULL, 0)) + goto parse_bracket_exp_espace; + mbcset->mbchars = new_mbchars; + } + mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch; + break; +#endif /* RE_ENABLE_I18N */ + case EQUIV_CLASS: + *err = build_equiv_class (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &equiv_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case COLL_SYM: + *err = build_collating_symbol (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &coll_sym_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case CHAR_CLASS: + *err = build_charclass (regexp->trans, sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &char_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name, syntax); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + default: + assert (0); + break; + } + } + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_CLOSE_BRACKET) + break; + } + + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + + /* If it is non-matching list. */ + if (non_match) + bitset_not (sbcset); + +#ifdef RE_ENABLE_I18N + /* Ensure only single byte characters are set. */ + if (dfa->mb_cur_max > 1) + bitset_mask (sbcset, dfa->sb_char); + + if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes + || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes + || mbcset->non_match))) + { + bin_tree_t *mbc_tree; + int sbc_idx; + /* Build a tree for complex bracket. */ + dfa->has_mb_node = 1; + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (mbc_tree == NULL, 0)) + goto parse_bracket_exp_espace; + for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx) + if (sbcset[sbc_idx]) + break; + /* If there are no bits set in sbcset, there is no point + of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */ + if (sbc_idx < BITSET_WORDS) + { + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + work_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + + /* Then join them by ALT node. */ + work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + } + else + { + re_free (sbcset); + work_tree = mbc_tree; + } + } + else +#endif /* not RE_ENABLE_I18N */ + { +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + work_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + } + return work_tree; + + parse_bracket_exp_espace: + *err = REG_ESPACE; + parse_bracket_exp_free_return: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + return NULL; +} + +/* Parse an element in the bracket expression. */ + +static reg_errcode_t +parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp, + re_token_t *token, int token_len, re_dfa_t *dfa, + reg_syntax_t syntax, int accept_hyphen) +{ +#ifdef RE_ENABLE_I18N + int cur_char_size; + cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp)); + if (cur_char_size > 1) + { + elem->type = MB_CHAR; + elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp)); + re_string_skip_bytes (regexp, cur_char_size); + return REG_NOERROR; + } +#endif /* RE_ENABLE_I18N */ + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS + || token->type == OP_OPEN_EQUIV_CLASS) + return parse_bracket_symbol (elem, regexp, token); + if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen) + { + /* A '-' must only appear as anything but a range indicator before + the closing bracket. Everything else is an error. */ + re_token_t token2; + (void) peek_token_bracket (&token2, regexp, syntax); + if (token2.type != OP_CLOSE_BRACKET) + /* The actual error value is not standardized since this whole + case is undefined. But ERANGE makes good sense. */ + return REG_ERANGE; + } + elem->type = SB_CHAR; + elem->opr.ch = token->opr.c; + return REG_NOERROR; +} + +/* Parse a bracket symbol in the bracket expression. Bracket symbols are + such as [::], [..], and + [==]. */ + +static reg_errcode_t +parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp, + re_token_t *token) +{ + unsigned char ch, delim = token->opr.c; + int i = 0; + if (re_string_eoi(regexp)) + return REG_EBRACK; + for (;; ++i) + { + if (i >= BRACKET_NAME_BUF_SIZE) + return REG_EBRACK; + if (token->type == OP_OPEN_CHAR_CLASS) + ch = re_string_fetch_byte_case (regexp); + else + ch = re_string_fetch_byte (regexp); + if (re_string_eoi(regexp)) + return REG_EBRACK; + if (ch == delim && re_string_peek_byte (regexp, 0) == ']') + break; + elem->opr.name[i] = ch; + } + re_string_skip_bytes (regexp, 1); + elem->opr.name[i] = '\0'; + switch (token->type) + { + case OP_OPEN_COLL_ELEM: + elem->type = COLL_SYM; + break; + case OP_OPEN_EQUIV_CLASS: + elem->type = EQUIV_CLASS; + break; + case OP_OPEN_CHAR_CLASS: + elem->type = CHAR_CLASS; + break; + default: + break; + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the equivalence class which is represented by NAME. + The result are written to MBCSET and SBCSET. + EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_equiv_class (bitset_t sbcset, re_charset_t *mbcset, + int *equiv_class_alloc, const unsigned char *name) +#else /* not RE_ENABLE_I18N */ +build_equiv_class (bitset_t sbcset, const unsigned char *name) +#endif /* not RE_ENABLE_I18N */ +{ +#ifdef _LIBC + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules != 0) + { + const int32_t *table, *indirect; + const unsigned char *weights, *extra, *cp; + unsigned char char_buf[2]; + int32_t idx1, idx2; + unsigned int ch; + size_t len; + /* This #include defines a local function! */ +# include + /* Calculate the index for equivalence class. */ + cp = name; + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + idx1 = findidx (&cp); + if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0)) + /* This isn't a valid character. */ + return REG_ECOLLATE; + + /* Build single byte matcing table for this equivalence class. */ + char_buf[1] = (unsigned char) '\0'; + len = weights[idx1]; + for (ch = 0; ch < SBC_MAX; ++ch) + { + char_buf[0] = ch; + cp = char_buf; + idx2 = findidx (&cp); +/* + idx2 = table[ch]; +*/ + if (idx2 == 0) + /* This isn't a valid character. */ + continue; + if (len == weights[idx2]) + { + int cnt = 0; + while (cnt <= len && + weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt]) + ++cnt; + + if (cnt > len) + bitset_set (sbcset, ch); + } + } + /* Check whether the array has enough space. */ + if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nequiv_classes is 0. */ + int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1; + /* Use realloc since the array is NULL if *alloc == 0. */ + int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes, + int32_t, + new_equiv_class_alloc); + if (BE (new_equiv_classes == NULL, 0)) + return REG_ESPACE; + mbcset->equiv_classes = new_equiv_classes; + *equiv_class_alloc = new_equiv_class_alloc; + } + mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1; + } + else +#endif /* _LIBC */ + { + if (BE (strlen ((const char *) name) != 1, 0)) + return REG_ECOLLATE; + bitset_set (sbcset, *name); + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the character class which is represented by NAME. + The result are written to MBCSET and SBCSET. + CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, + re_charset_t *mbcset, int *char_class_alloc, + const unsigned char *class_name, reg_syntax_t syntax) +#else /* not RE_ENABLE_I18N */ +build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, + const unsigned char *class_name, reg_syntax_t syntax) +#endif /* not RE_ENABLE_I18N */ +{ + int i; + const char *name = (const char *) class_name; + + /* In case of REG_ICASE "upper" and "lower" match the both of + upper and lower cases. */ + if ((syntax & RE_ICASE) + && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0)) + name = "alpha"; + +#ifdef RE_ENABLE_I18N + /* Check the space of the arrays. */ + if (BE (*char_class_alloc == mbcset->nchar_classes, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nchar_classes is 0. */ + int new_char_class_alloc = 2 * mbcset->nchar_classes + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t, + new_char_class_alloc); + if (BE (new_char_classes == NULL, 0)) + return REG_ESPACE; + mbcset->char_classes = new_char_classes; + *char_class_alloc = new_char_class_alloc; + } + mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name); +#endif /* RE_ENABLE_I18N */ + +#define BUILD_CHARCLASS_LOOP(ctype_func) \ + do { \ + if (BE (trans != NULL, 0)) \ + { \ + for (i = 0; i < SBC_MAX; ++i) \ + if (ctype_func (i)) \ + bitset_set (sbcset, trans[i]); \ + } \ + else \ + { \ + for (i = 0; i < SBC_MAX; ++i) \ + if (ctype_func (i)) \ + bitset_set (sbcset, i); \ + } \ + } while (0) + + if (strcmp (name, "alnum") == 0) + BUILD_CHARCLASS_LOOP (isalnum); + else if (strcmp (name, "cntrl") == 0) + BUILD_CHARCLASS_LOOP (iscntrl); + else if (strcmp (name, "lower") == 0) + BUILD_CHARCLASS_LOOP (islower); + else if (strcmp (name, "space") == 0) + BUILD_CHARCLASS_LOOP (isspace); + else if (strcmp (name, "alpha") == 0) + BUILD_CHARCLASS_LOOP (isalpha); + else if (strcmp (name, "digit") == 0) + BUILD_CHARCLASS_LOOP (isdigit); + else if (strcmp (name, "print") == 0) + BUILD_CHARCLASS_LOOP (isprint); + else if (strcmp (name, "upper") == 0) + BUILD_CHARCLASS_LOOP (isupper); + else if (strcmp (name, "blank") == 0) + BUILD_CHARCLASS_LOOP (isblank); + else if (strcmp (name, "graph") == 0) + BUILD_CHARCLASS_LOOP (isgraph); + else if (strcmp (name, "punct") == 0) + BUILD_CHARCLASS_LOOP (ispunct); + else if (strcmp (name, "xdigit") == 0) + BUILD_CHARCLASS_LOOP (isxdigit); + else + return REG_ECTYPE; + + return REG_NOERROR; +} + +static bin_tree_t * +build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans, + const unsigned char *class_name, + const unsigned char *extra, int non_match, + reg_errcode_t *err) +{ + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + int alloc = 0; +#endif /* not RE_ENABLE_I18N */ + reg_errcode_t ret; + re_token_t br_token; + bin_tree_t *tree; + + sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ + +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else /* not RE_ENABLE_I18N */ + if (BE (sbcset == NULL, 0)) +#endif /* not RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + if (non_match) + { +#ifdef RE_ENABLE_I18N + /* + if (syntax & RE_HAT_LISTS_NOT_NEWLINE) + bitset_set(cset->sbcset, '\0'); + */ + mbcset->non_match = 1; +#endif /* not RE_ENABLE_I18N */ + } + + /* We don't care the syntax in this case. */ + ret = build_charclass (trans, sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &alloc, +#endif /* RE_ENABLE_I18N */ + class_name, 0); + + if (BE (ret != REG_NOERROR, 0)) + { + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = ret; + return NULL; + } + /* \w match '_' also. */ + for (; *extra; extra++) + bitset_set (sbcset, *extra); + + /* If it is non-matching list. */ + if (non_match) + bitset_not (sbcset); + +#ifdef RE_ENABLE_I18N + /* Ensure only single byte characters are set. */ + if (dfa->mb_cur_max > 1) + bitset_mask (sbcset, dfa->sb_char); +#endif + + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (tree == NULL, 0)) + goto build_word_op_espace; + +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + bin_tree_t *mbc_tree; + /* Build a tree for complex bracket. */ + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + dfa->has_mb_node = 1; + mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (mbc_tree == NULL, 0)) + goto build_word_op_espace; + /* Then join them by ALT node. */ + tree = create_tree (dfa, tree, mbc_tree, OP_ALT); + if (BE (mbc_tree != NULL, 1)) + return tree; + } + else + { + free_charset (mbcset); + return tree; + } +#else /* not RE_ENABLE_I18N */ + return tree; +#endif /* not RE_ENABLE_I18N */ + + build_word_op_espace: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = REG_ESPACE; + return NULL; +} + +/* This is intended for the expressions like "a{1,3}". + Fetch a number from `input', and return the number. + Return -1, if the number field is empty like "{,1}". + Return -2, If an error is occured. */ + +static int +fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax) +{ + int num = -1; + unsigned char c; + while (1) + { + fetch_token (token, input, syntax); + c = token->opr.c; + if (BE (token->type == END_OF_RE, 0)) + return -2; + if (token->type == OP_CLOSE_DUP_NUM || c == ',') + break; + num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2) + ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0')); + num = (num > RE_DUP_MAX) ? -2 : num; + } + return num; +} + +#ifdef RE_ENABLE_I18N +static void +free_charset (re_charset_t *cset) +{ + re_free (cset->mbchars); +# ifdef _LIBC + re_free (cset->coll_syms); + re_free (cset->equiv_classes); + re_free (cset->range_starts); + re_free (cset->range_ends); +# endif + re_free (cset->char_classes); + re_free (cset); +} +#endif /* RE_ENABLE_I18N */ + +/* Functions for binary tree operation. */ + +/* Create a tree node. */ + +static bin_tree_t * +create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, + re_token_type_t type) +{ + re_token_t t; + t.type = type; + return create_token_tree (dfa, left, right, &t); +} + +static bin_tree_t * +create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, + const re_token_t *token) +{ + bin_tree_t *tree; + if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0)) + { + bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1); + + if (storage == NULL) + return NULL; + storage->next = dfa->str_tree_storage; + dfa->str_tree_storage = storage; + dfa->str_tree_storage_idx = 0; + } + tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++]; + + tree->parent = NULL; + tree->left = left; + tree->right = right; + tree->token = *token; + tree->token.duplicated = 0; + tree->token.opt_subexp = 0; + tree->first = NULL; + tree->next = NULL; + tree->node_idx = -1; + + if (left != NULL) + left->parent = tree; + if (right != NULL) + right->parent = tree; + return tree; +} + +/* Mark the tree SRC as an optional subexpression. + To be called from preorder or postorder. */ + +static reg_errcode_t +mark_opt_subexp (void *extra, bin_tree_t *node) +{ + int idx = (int) (long) extra; + if (node->token.type == SUBEXP && node->token.opr.idx == idx) + node->token.opt_subexp = 1; + + return REG_NOERROR; +} + +/* Free the allocated memory inside NODE. */ + +static void +free_token (re_token_t *node) +{ +#ifdef RE_ENABLE_I18N + if (node->type == COMPLEX_BRACKET && node->duplicated == 0) + free_charset (node->opr.mbcset); + else +#endif /* RE_ENABLE_I18N */ + if (node->type == SIMPLE_BRACKET && node->duplicated == 0) + re_free (node->opr.sbcset); +} + +/* Worker function for tree walking. Free the allocated memory inside NODE + and its children. */ + +static reg_errcode_t +free_tree (void *extra, bin_tree_t *node) +{ + free_token (&node->token); + return REG_NOERROR; +} + + +/* Duplicate the node SRC, and return new node. This is a preorder + visit similar to the one implemented by the generic visitor, but + we need more infrastructure to maintain two parallel trees --- so, + it's easier to duplicate. */ + +static bin_tree_t * +duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa) +{ + const bin_tree_t *node; + bin_tree_t *dup_root; + bin_tree_t **p_new = &dup_root, *dup_node = root->parent; + + for (node = root; ; ) + { + /* Create a new tree and link it back to the current parent. */ + *p_new = create_token_tree (dfa, NULL, NULL, &node->token); + if (*p_new == NULL) + return NULL; + (*p_new)->parent = dup_node; + (*p_new)->token.duplicated = 1; + dup_node = *p_new; + + /* Go to the left node, or up and to the right. */ + if (node->left) + { + node = node->left; + p_new = &dup_node->left; + } + else + { + const bin_tree_t *prev = NULL; + while (node->right == prev || node->right == NULL) + { + prev = node; + node = node->parent; + dup_node = dup_node->parent; + if (!node) + return dup_root; + } + node = node->right; + p_new = &dup_node->right; + } + } +} + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/* GKINCLUDE #include "regexec.c" */ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, + int n) internal_function; +static void match_ctx_clean (re_match_context_t *mctx) internal_function; +static void match_ctx_free (re_match_context_t *cache) internal_function; +static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node, + int str_idx, int from, int to) + internal_function; +static int search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx) + internal_function; +static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node, + int str_idx) internal_function; +static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop, + int node, int str_idx) + internal_function; +static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, + re_dfastate_t **limited_sts, int last_node, + int last_str_idx) + internal_function; +static reg_errcode_t re_search_internal (const regex_t *preg, + const char *string, int length, + int start, int range, int stop, + size_t nmatch, regmatch_t pmatch[], + int eflags) internal_function; +static int re_search_2_stub (struct re_pattern_buffer *bufp, + const char *string1, int length1, + const char *string2, int length2, + int start, int range, struct re_registers *regs, + int stop, int ret_len) internal_function; +static int re_search_stub (struct re_pattern_buffer *bufp, + const char *string, int length, int start, + int range, int stop, struct re_registers *regs, + int ret_len) internal_function; +static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, + int nregs, int regs_allocated) internal_function; +static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx) + internal_function; +static int check_matching (re_match_context_t *mctx, int fl_longest_match, + int *p_match_first) internal_function; +static int check_halt_state_context (const re_match_context_t *mctx, + const re_dfastate_t *state, int idx) + internal_function; +static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, + regmatch_t *prev_idx_match, int cur_node, + int cur_idx, int nmatch) internal_function; +static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs, + int str_idx, int dest_node, int nregs, + regmatch_t *regs, + re_node_set *eps_via_nodes) + internal_function; +static reg_errcode_t set_regs (const regex_t *preg, + const re_match_context_t *mctx, + size_t nmatch, regmatch_t *pmatch, + int fl_backtrack) internal_function; +static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs) + internal_function; + +#ifdef RE_ENABLE_I18N +static int sift_states_iter_mb (const re_match_context_t *mctx, + re_sift_context_t *sctx, + int node_idx, int str_idx, int max_str_idx) + internal_function; +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t sift_states_backward (const re_match_context_t *mctx, + re_sift_context_t *sctx) + internal_function; +static reg_errcode_t build_sifted_states (const re_match_context_t *mctx, + re_sift_context_t *sctx, int str_idx, + re_node_set *cur_dest) + internal_function; +static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx, + re_sift_context_t *sctx, + int str_idx, + re_node_set *dest_nodes) + internal_function; +static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates) + internal_function; +static int check_dst_limits (const re_match_context_t *mctx, + re_node_set *limits, + int dst_node, int dst_idx, int src_node, + int src_idx) internal_function; +static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, + int boundaries, int subexp_idx, + int from_node, int bkref_idx) + internal_function; +static int check_dst_limits_calc_pos (const re_match_context_t *mctx, + int limit, int subexp_idx, + int node, int str_idx, + int bkref_idx) internal_function; +static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates, + re_node_set *limits, + struct re_backref_cache_entry *bkref_ents, + int str_idx) internal_function; +static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx, + re_sift_context_t *sctx, + int str_idx, const re_node_set *candidates) + internal_function; +static reg_errcode_t merge_state_array (const re_dfa_t *dfa, + re_dfastate_t **dst, + re_dfastate_t **src, int num) + internal_function; +static re_dfastate_t *find_recover_state (reg_errcode_t *err, + re_match_context_t *mctx) internal_function; +static re_dfastate_t *transit_state (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *state) internal_function; +static re_dfastate_t *merge_state_with_log (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *next_state) + internal_function; +static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx, + re_node_set *cur_nodes, + int str_idx) internal_function; +#if 0 +static re_dfastate_t *transit_state_sb (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *pstate) + internal_function; +#endif +#ifdef RE_ENABLE_I18N +static reg_errcode_t transit_state_mb (re_match_context_t *mctx, + re_dfastate_t *pstate) + internal_function; +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t transit_state_bkref (re_match_context_t *mctx, + const re_node_set *nodes) + internal_function; +static reg_errcode_t get_subexp (re_match_context_t *mctx, + int bkref_node, int bkref_str_idx) + internal_function; +static reg_errcode_t get_subexp_sub (re_match_context_t *mctx, + const re_sub_match_top_t *sub_top, + re_sub_match_last_t *sub_last, + int bkref_node, int bkref_str) + internal_function; +static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, + int subexp_idx, int type) internal_function; +static reg_errcode_t check_arrival (re_match_context_t *mctx, + state_array_t *path, int top_node, + int top_str, int last_node, int last_str, + int type) internal_function; +static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx, + int str_idx, + re_node_set *cur_nodes, + re_node_set *next_nodes) + internal_function; +static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa, + re_node_set *cur_nodes, + int ex_subexp, int type) + internal_function; +static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa, + re_node_set *dst_nodes, + int target, int ex_subexp, + int type) internal_function; +static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx, + re_node_set *cur_nodes, int cur_str, + int subexp_num, int type) + internal_function; +static int build_trtable (const re_dfa_t *dfa, + re_dfastate_t *state) internal_function; +#ifdef RE_ENABLE_I18N +static int check_node_accept_bytes (const re_dfa_t *dfa, int node_idx, + const re_string_t *input, int idx) + internal_function; +# ifdef _LIBC +static unsigned int find_collation_sequence_value (const unsigned char *mbs, + size_t name_len) + internal_function; +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ +static int group_nodes_into_DFAstates (const re_dfa_t *dfa, + const re_dfastate_t *state, + re_node_set *states_node, + bitset_t *states_ch) internal_function; +static int check_node_accept (const re_match_context_t *mctx, + const re_token_t *node, int idx) + internal_function; +static reg_errcode_t extend_buffers (re_match_context_t *mctx) + internal_function; + +/* Entry point for POSIX code. */ + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *__restrict preg; + const char *__restrict string; + size_t nmatch; + regmatch_t pmatch[]; + int eflags; +{ + reg_errcode_t err; + int start, length; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + + if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND)) + return REG_BADPAT; + + if (eflags & REG_STARTEND) + { + start = pmatch[0].rm_so; + length = pmatch[0].rm_eo; + } + else + { + start = 0; + length = strlen (string); + } + + __libc_lock_lock (dfa->lock); + if (preg->no_sub) + err = re_search_internal (preg, string, length, start, length - start, + length, 0, NULL, eflags); + else + err = re_search_internal (preg, string, length, start, length - start, + length, nmatch, pmatch, eflags); + __libc_lock_unlock (dfa->lock); + return err != REG_NOERROR; +} + +#ifdef _LIBC +# include +versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4); + +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) +__typeof__ (__regexec) __compat_regexec; + +int +attribute_compat_text_section +__compat_regexec (const regex_t *__restrict preg, + const char *__restrict string, size_t nmatch, + regmatch_t pmatch[], int eflags) +{ + return regexec (preg, string, nmatch, pmatch, + eflags & (REG_NOTBOL | REG_NOTEOL)); +} +compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0); +# endif +#endif + +/* Entry points for GNU code. */ + +/* re_match, re_search, re_match_2, re_search_2 + + The former two functions operate on STRING with length LENGTH, + while the later two operate on concatenation of STRING1 and STRING2 + with lengths LENGTH1 and LENGTH2, respectively. + + re_match() matches the compiled pattern in BUFP against the string, + starting at index START. + + re_search() first tries matching at index START, then it tries to match + starting from index START + 1, and so on. The last start position tried + is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same + way as re_match().) + + The parameter STOP of re_{match,search}_2 specifies that no match exceeding + the first STOP characters of the concatenation of the strings should be + concerned. + + If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match + and all groups is stroed in REGS. (For the "_2" variants, the offsets are + computed relative to the concatenation, not relative to the individual + strings.) + + On success, re_match* functions return the length of the match, re_search* + return the position of the start of the match. Return value -1 means no + match was found and -2 indicates an internal error. */ + +int +re_match (bufp, string, length, start, regs) + struct re_pattern_buffer *bufp; + const char *string; + int length, start; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, 0, length, regs, 1); +} +#ifdef _LIBC +weak_alias (__re_match, re_match) +#endif + +int +re_search (bufp, string, length, start, range, regs) + struct re_pattern_buffer *bufp; + const char *string; + int length, start, range; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, range, length, regs, 0); +} +#ifdef _LIBC +weak_alias (__re_search, re_search) +#endif + +int +re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int length1, length2, start, stop; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, 0, regs, stop, 1); +} +#ifdef _LIBC +weak_alias (__re_match_2, re_match_2) +#endif + +int +re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int length1, length2, start, range, stop; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, range, regs, stop, 0); +} +#ifdef _LIBC +weak_alias (__re_search_2, re_search_2) +#endif + +static int +re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs, + stop, ret_len) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int length1, length2, start, range, stop, ret_len; + struct re_registers *regs; +{ + const char *str; + int rval; + int len = length1 + length2; + int free_str = 0; + + if (BE (length1 < 0 || length2 < 0 || stop < 0, 0)) + return -2; + + /* Concatenate the strings. */ + if (length2 > 0) + if (length1 > 0) + { + char *s = re_malloc (char, len); + + if (BE (s == NULL, 0)) + return -2; +#ifdef _LIBC + memcpy (__mempcpy (s, string1, length1), string2, length2); +#else + memcpy (s, string1, length1); + memcpy (s + length1, string2, length2); +#endif + str = s; + free_str = 1; + } + else + str = string2; + else + str = string1; + + rval = re_search_stub (bufp, str, len, start, range, stop, regs, + ret_len); + if (free_str) + re_free ((char *) str); + return rval; +} + +/* The parameters have the same meaning as those of re_search. + Additional parameters: + If RET_LEN is nonzero the length of the match is returned (re_match style); + otherwise the position of the match is returned. */ + +static int +re_search_stub (bufp, string, length, start, range, stop, regs, ret_len) + struct re_pattern_buffer *bufp; + const char *string; + int length, start, range, stop, ret_len; + struct re_registers *regs; +{ + reg_errcode_t result; + regmatch_t *pmatch; + int nregs, rval; + int eflags = 0; + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + + /* Check for out-of-range. */ + if (BE (start < 0 || start > length, 0)) + return -1; + if (BE (start + range > length, 0)) + range = length - start; + else if (BE (start + range < 0, 0)) + range = -start; + + __libc_lock_lock (dfa->lock); + + eflags |= (bufp->not_bol) ? REG_NOTBOL : 0; + eflags |= (bufp->not_eol) ? REG_NOTEOL : 0; + + /* Compile fastmap if we haven't yet. */ + if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate) + re_compile_fastmap (bufp); + + if (BE (bufp->no_sub, 0)) + regs = NULL; + + /* We need at least 1 register. */ + if (regs == NULL) + nregs = 1; + else if (BE (bufp->regs_allocated == REGS_FIXED && + regs->num_regs < bufp->re_nsub + 1, 0)) + { + nregs = regs->num_regs; + if (BE (nregs < 1, 0)) + { + /* Nothing can be copied to regs. */ + regs = NULL; + nregs = 1; + } + } + else + nregs = bufp->re_nsub + 1; + pmatch = re_malloc (regmatch_t, nregs); + if (BE (pmatch == NULL, 0)) + { + rval = -2; + goto out; + } + + result = re_search_internal (bufp, string, length, start, range, stop, + nregs, pmatch, eflags); + + rval = 0; + + /* I hope we needn't fill ther regs with -1's when no match was found. */ + if (result != REG_NOERROR) + rval = -1; + else if (regs != NULL) + { + /* If caller wants register contents data back, copy them. */ + bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs, + bufp->regs_allocated); + if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0)) + rval = -2; + } + + if (BE (rval == 0, 1)) + { + if (ret_len) + { + assert (pmatch[0].rm_so == start); + rval = pmatch[0].rm_eo - start; + } + else + rval = pmatch[0].rm_so; + } + re_free (pmatch); + out: + __libc_lock_unlock (dfa->lock); + return rval; +} + +static unsigned +re_copy_regs (regs, pmatch, nregs, regs_allocated) + struct re_registers *regs; + regmatch_t *pmatch; + int nregs, regs_allocated; +{ + int rval = REGS_REALLOCATE; + int i; + int need_regs = nregs + 1; + /* We need one extra element beyond `num_regs' for the `-1' marker GNU code + uses. */ + + /* Have the register data arrays been allocated? */ + if (regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. */ + regs->start = re_malloc (regoff_t, need_regs); + regs->end = re_malloc (regoff_t, need_regs); + if (BE (regs->start == NULL, 0) || BE (regs->end == NULL, 0)) + return REGS_UNALLOCATED; + regs->num_regs = need_regs; + } + else if (regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (BE (need_regs > regs->num_regs, 0)) + { + regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs); + regoff_t *new_end = re_realloc (regs->end, regoff_t, need_regs); + if (BE (new_start == NULL, 0) || BE (new_end == NULL, 0)) + return REGS_UNALLOCATED; + regs->start = new_start; + regs->end = new_end; + regs->num_regs = need_regs; + } + } + else + { + assert (regs_allocated == REGS_FIXED); + /* This function may not be called with REGS_FIXED and nregs too big. */ + assert (regs->num_regs >= nregs); + rval = REGS_FIXED; + } + + /* Copy the regs. */ + for (i = 0; i < nregs; ++i) + { + regs->start[i] = pmatch[i].rm_so; + regs->end[i] = pmatch[i].rm_eo; + } + for ( ; i < regs->num_regs; ++i) + regs->start[i] = regs->end[i] = -1; + + return rval; +} + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use + this memory for recording register information. STARTS and ENDS + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + unsigned num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = (regoff_t *) 0; + } +} +#ifdef _LIBC +weak_alias (__re_set_registers, re_set_registers) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC +int +# ifdef _LIBC +weak_function +# endif +re_exec (s) + const char *s; +{ + return 0 == regexec (&re_comp_buf, s, 0, NULL, 0); +} +#endif /* _REGEX_RE_COMP */ + +/* Internal entry point. */ + +/* Searches for a compiled pattern PREG in the string STRING, whose + length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same + mingings with regexec. START, and RANGE have the same meanings + with re_search. + Return REG_NOERROR if we find a match, and REG_NOMATCH if not, + otherwise return the error code. + Note: We assume front end functions already check ranges. + (START + RANGE >= 0 && START + RANGE <= LENGTH) */ + +static reg_errcode_t +re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch, + eflags) + const regex_t *preg; + const char *string; + int length, start, range, stop, eflags; + size_t nmatch; + regmatch_t pmatch[]; +{ + reg_errcode_t err; + const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; + int left_lim, right_lim, incr; + int fl_longest_match, match_first, match_kind, match_last = -1; + int extra_nmatch; + int sb, ch; +#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) + re_match_context_t mctx = { .dfa = dfa }; +#else + re_match_context_t mctx; +#endif + char *fastmap = (preg->fastmap != NULL && preg->fastmap_accurate + && range && !preg->can_be_null) ? preg->fastmap : NULL; + RE_TRANSLATE_TYPE t = preg->translate; + +#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)) + memset (&mctx, '\0', sizeof (re_match_context_t)); + mctx.dfa = dfa; +#endif + + extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0; + nmatch -= extra_nmatch; + + /* Check if the DFA haven't been compiled. */ + if (BE (preg->used == 0 || dfa->init_state == NULL + || dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return REG_NOMATCH; + +#ifdef DEBUG + /* We assume front-end functions already check them. */ + assert (start + range >= 0 && start + range <= length); +#endif + + /* If initial states with non-begbuf contexts have no elements, + the regex must be anchored. If preg->newline_anchor is set, + we'll never use init_state_nl, so do not check it. */ + if (dfa->init_state->nodes.nelem == 0 + && dfa->init_state_word->nodes.nelem == 0 + && (dfa->init_state_nl->nodes.nelem == 0 + || !preg->newline_anchor)) + { + if (start != 0 && start + range != 0) + return REG_NOMATCH; + start = range = 0; + } + + /* We must check the longest matching, if nmatch > 0. */ + fl_longest_match = (nmatch != 0 || dfa->nbackref); + + err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1, + preg->translate, preg->syntax & RE_ICASE, dfa); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + mctx.input.stop = stop; + mctx.input.raw_stop = stop; + mctx.input.newline_anchor = preg->newline_anchor; + + err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* We will log all the DFA states through which the dfa pass, + if nmatch > 1, or this dfa has "multibyte node", which is a + back-reference or a node which can accept multibyte character or + multi character collating element. */ + if (nmatch > 1 || dfa->has_mb_node) + { + mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1); + if (BE (mctx.state_log == NULL, 0)) + { + err = REG_ESPACE; + goto free_return; + } + } + else + mctx.state_log = NULL; + + match_first = start; + mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF; + + /* Check incrementally whether of not the input string match. */ + incr = (range < 0) ? -1 : 1; + left_lim = (range < 0) ? start + range : start; + right_lim = (range < 0) ? start : start + range; + sb = dfa->mb_cur_max == 1; + match_kind = + (fastmap + ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0) + | (range >= 0 ? 2 : 0) + | (t != NULL ? 1 : 0)) + : 8); + + for (;; match_first += incr) + { + err = REG_NOMATCH; + if (match_first < left_lim || right_lim < match_first) + goto free_return; + + /* Advance as rapidly as possible through the string, until we + find a plausible place to start matching. This may be done + with varying efficiency, so there are various possibilities: + only the most common of them are specialized, in order to + save on code size. We use a switch statement for speed. */ + switch (match_kind) + { + case 8: + /* No fastmap. */ + break; + + case 7: + /* Fastmap with single-byte translation, match forward. */ + while (BE (match_first < right_lim, 1) + && !fastmap[t[(unsigned char) string[match_first]]]) + ++match_first; + goto forward_match_found_start_or_reached_end; + + case 6: + /* Fastmap without translation, match forward. */ + while (BE (match_first < right_lim, 1) + && !fastmap[(unsigned char) string[match_first]]) + ++match_first; + + forward_match_found_start_or_reached_end: + if (BE (match_first == right_lim, 0)) + { + ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (!fastmap[t ? t[ch] : ch]) + goto free_return; + } + break; + + case 4: + case 5: + /* Fastmap without multi-byte translation, match backwards. */ + while (match_first >= left_lim) + { + ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (fastmap[t ? t[ch] : ch]) + break; + --match_first; + } + if (match_first < left_lim) + goto free_return; + break; + + default: + /* In this case, we can't determine easily the current byte, + since it might be a component byte of a multibyte + character. Then we use the constructed buffer instead. */ + for (;;) + { + /* If MATCH_FIRST is out of the valid range, reconstruct the + buffers. */ + unsigned int offset = match_first - mctx.input.raw_mbs_idx; + if (BE (offset >= (unsigned int) mctx.input.valid_raw_len, 0)) + { + err = re_string_reconstruct (&mctx.input, match_first, + eflags); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + offset = match_first - mctx.input.raw_mbs_idx; + } + /* If MATCH_FIRST is out of the buffer, leave it as '\0'. + Note that MATCH_FIRST must not be smaller than 0. */ + ch = (match_first >= length + ? 0 : re_string_byte_at (&mctx.input, offset)); + if (fastmap[ch]) + break; + match_first += incr; + if (match_first < left_lim || match_first > right_lim) + { + err = REG_NOMATCH; + goto free_return; + } + } + break; + } + + /* Reconstruct the buffers so that the matcher can assume that + the matching starts from the beginning of the buffer. */ + err = re_string_reconstruct (&mctx.input, match_first, eflags); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + +#ifdef RE_ENABLE_I18N + /* Don't consider this char as a possible match start if it part, + yet isn't the head, of a multibyte character. */ + if (!sb && !re_string_first_byte (&mctx.input, 0)) + continue; +#endif + + /* It seems to be appropriate one, then use the matcher. */ + /* We assume that the matching starts from 0. */ + mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0; + match_last = check_matching (&mctx, fl_longest_match, + range >= 0 ? &match_first : NULL); + if (match_last != -1) + { + if (BE (match_last == -2, 0)) + { + err = REG_ESPACE; + goto free_return; + } + else + { + mctx.match_last = match_last; + if ((!preg->no_sub && nmatch > 1) || dfa->nbackref) + { + re_dfastate_t *pstate = mctx.state_log[match_last]; + mctx.last_node = check_halt_state_context (&mctx, pstate, + match_last); + } + if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match) + || dfa->nbackref) + { + err = prune_impossible_nodes (&mctx); + if (err == REG_NOERROR) + break; + if (BE (err != REG_NOMATCH, 0)) + goto free_return; + match_last = -1; + } + else + break; /* We found a match. */ + } + } + + match_ctx_clean (&mctx); + } + +#ifdef DEBUG + assert (match_last != -1); + assert (err == REG_NOERROR); +#endif + + /* Set pmatch[] if we need. */ + if (nmatch > 0) + { + int reg_idx; + + /* Initialize registers. */ + for (reg_idx = 1; reg_idx < nmatch; ++reg_idx) + pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1; + + /* Set the points where matching start/end. */ + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = mctx.match_last; + + if (!preg->no_sub && nmatch > 1) + { + err = set_regs (preg, &mctx, nmatch, pmatch, + dfa->has_plural_match && dfa->nbackref > 0); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + + /* At last, add the offset to the each registers, since we slided + the buffers so that we could assume that the matching starts + from 0. */ + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so != -1) + { +#ifdef RE_ENABLE_I18N + if (BE (mctx.input.offsets_needed != 0, 0)) + { + pmatch[reg_idx].rm_so = + (pmatch[reg_idx].rm_so == mctx.input.valid_len + ? mctx.input.valid_raw_len + : mctx.input.offsets[pmatch[reg_idx].rm_so]); + pmatch[reg_idx].rm_eo = + (pmatch[reg_idx].rm_eo == mctx.input.valid_len + ? mctx.input.valid_raw_len + : mctx.input.offsets[pmatch[reg_idx].rm_eo]); + } +#else + assert (mctx.input.offsets_needed == 0); +#endif + pmatch[reg_idx].rm_so += match_first; + pmatch[reg_idx].rm_eo += match_first; + } + for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx) + { + pmatch[nmatch + reg_idx].rm_so = -1; + pmatch[nmatch + reg_idx].rm_eo = -1; + } + + if (dfa->subexp_map) + for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++) + if (dfa->subexp_map[reg_idx] != reg_idx) + { + pmatch[reg_idx + 1].rm_so + = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so; + pmatch[reg_idx + 1].rm_eo + = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo; + } + } + + free_return: + re_free (mctx.state_log); + if (dfa->nbackref) + match_ctx_free (&mctx); + re_string_destruct (&mctx.input); + return err; +} + +static reg_errcode_t +prune_impossible_nodes (mctx) + re_match_context_t *mctx; +{ + const re_dfa_t *const dfa = mctx->dfa; + int halt_node, match_last; + reg_errcode_t ret; + re_dfastate_t **sifted_states; + re_dfastate_t **lim_states = NULL; + re_sift_context_t sctx; +#ifdef DEBUG + assert (mctx->state_log != NULL); +#endif + match_last = mctx->match_last; + halt_node = mctx->last_node; + sifted_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (sifted_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + if (dfa->nbackref) + { + lim_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (lim_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + while (1) + { + memset (lim_states, '\0', + sizeof (re_dfastate_t *) * (match_last + 1)); + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, + match_last); + ret = sift_states_backward (mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + if (sifted_states[0] != NULL || lim_states[0] != NULL) + break; + do + { + --match_last; + if (match_last < 0) + { + ret = REG_NOMATCH; + goto free_return; + } + } while (mctx->state_log[match_last] == NULL + || !mctx->state_log[match_last]->halt); + halt_node = check_halt_state_context (mctx, + mctx->state_log[match_last], + match_last); + } + ret = merge_state_array (dfa, sifted_states, lim_states, + match_last + 1); + re_free (lim_states); + lim_states = NULL; + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + } + else + { + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last); + ret = sift_states_backward (mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + } + re_free (mctx->state_log); + mctx->state_log = sifted_states; + sifted_states = NULL; + mctx->last_node = halt_node; + mctx->match_last = match_last; + ret = REG_NOERROR; + free_return: + re_free (sifted_states); + re_free (lim_states); + return ret; +} + +/* Acquire an initial state and return it. + We must select appropriate initial state depending on the context, + since initial states may have constraints like "\<", "^", etc.. */ + +static inline re_dfastate_t * +__attribute ((always_inline)) internal_function +acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx, + int idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + if (dfa->init_state->has_constraint) + { + unsigned int context; + context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags); + if (IS_WORD_CONTEXT (context)) + return dfa->init_state_word; + else if (IS_ORDINARY_CONTEXT (context)) + return dfa->init_state; + else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_begbuf; + else if (IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_nl; + else if (IS_BEGBUF_CONTEXT (context)) + { + /* It is relatively rare case, then calculate on demand. */ + return re_acquire_state_context (err, dfa, + dfa->init_state->entrance_nodes, + context); + } + else + /* Must not happen? */ + return dfa->init_state; + } + else + return dfa->init_state; +} + +/* Check whether the regular expression match input string INPUT or not, + and return the index where the matching end, return -1 if not match, + or return -2 in case of an error. + FL_LONGEST_MATCH means we want the POSIX longest matching. + If P_MATCH_FIRST is not NULL, and the match fails, it is set to the + next place where we may want to try matching. + Note that the matcher assume that the maching starts from the current + index of the buffer. */ + +static int +internal_function +check_matching (re_match_context_t *mctx, int fl_longest_match, + int *p_match_first) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int match = 0; + int match_last = -1; + int cur_str_idx = re_string_cur_idx (&mctx->input); + re_dfastate_t *cur_state; + int at_init_state = p_match_first != NULL; + int next_start_idx = cur_str_idx; + + err = REG_NOERROR; + cur_state = acquire_init_state_context (&err, mctx, cur_str_idx); + /* An initial state must not be NULL (invalid). */ + if (BE (cur_state == NULL, 0)) + { + assert (err == REG_ESPACE); + return -2; + } + + if (mctx->state_log != NULL) + { + mctx->state_log[cur_str_idx] = cur_state; + + /* Check OP_OPEN_SUBEXP in the initial state in case that we use them + later. E.g. Processing back references. */ + if (BE (dfa->nbackref, 0)) + { + at_init_state = 0; + err = check_subexp_matching_top (mctx, &cur_state->nodes, 0); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (cur_state->has_backref) + { + err = transit_state_bkref (mctx, &cur_state->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + } + + /* If the RE accepts NULL string. */ + if (BE (cur_state->halt, 0)) + { + if (!cur_state->has_constraint + || check_halt_state_context (mctx, cur_state, cur_str_idx)) + { + if (!fl_longest_match) + return cur_str_idx; + else + { + match_last = cur_str_idx; + match = 1; + } + } + } + + while (!re_string_eoi (&mctx->input)) + { + re_dfastate_t *old_state = cur_state; + int next_char_idx = re_string_cur_idx (&mctx->input) + 1; + + if (BE (next_char_idx >= mctx->input.bufs_len, 0) + || (BE (next_char_idx >= mctx->input.valid_len, 0) + && mctx->input.valid_len < mctx->input.len)) + { + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + { + assert (err == REG_ESPACE); + return -2; + } + } + + cur_state = transit_state (&err, mctx, cur_state); + if (mctx->state_log != NULL) + cur_state = merge_state_with_log (&err, mctx, cur_state); + + if (cur_state == NULL) + { + /* Reached the invalid state or an error. Try to recover a valid + state using the state log, if available and if we have not + already found a valid (even if not the longest) match. */ + if (BE (err != REG_NOERROR, 0)) + return -2; + + if (mctx->state_log == NULL + || (match && !fl_longest_match) + || (cur_state = find_recover_state (&err, mctx)) == NULL) + break; + } + + if (BE (at_init_state, 0)) + { + if (old_state == cur_state) + next_start_idx = next_char_idx; + else + at_init_state = 0; + } + + if (cur_state->halt) + { + /* Reached a halt state. + Check the halt state can satisfy the current context. */ + if (!cur_state->has_constraint + || check_halt_state_context (mctx, cur_state, + re_string_cur_idx (&mctx->input))) + { + /* We found an appropriate halt state. */ + match_last = re_string_cur_idx (&mctx->input); + match = 1; + + /* We found a match, do not modify match_first below. */ + p_match_first = NULL; + if (!fl_longest_match) + break; + } + } + } + + if (p_match_first) + *p_match_first += next_start_idx; + + return match_last; +} + +/* Check NODE match the current context. */ + +static int +internal_function +check_halt_node_context (const re_dfa_t *dfa, int node, unsigned int context) +{ + re_token_type_t type = dfa->nodes[node].type; + unsigned int constraint = dfa->nodes[node].constraint; + if (type != END_OF_RE) + return 0; + if (!constraint) + return 1; + if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context)) + return 0; + return 1; +} + +/* Check the halt state STATE match the current context. + Return 0 if not match, if the node, STATE has, is a halt node and + match the context, return the node. */ + +static int +internal_function +check_halt_state_context (const re_match_context_t *mctx, + const re_dfastate_t *state, int idx) +{ + int i; + unsigned int context; +#ifdef DEBUG + assert (state->halt); +#endif + context = re_string_context_at (&mctx->input, idx, mctx->eflags); + for (i = 0; i < state->nodes.nelem; ++i) + if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context)) + return state->nodes.elems[i]; + return 0; +} + +/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA + corresponding to the DFA). + Return the destination node, and update EPS_VIA_NODES, return -1 in case + of errors. */ + +static int +internal_function +proceed_next_node (const re_match_context_t *mctx, int nregs, regmatch_t *regs, + int *pidx, int node, re_node_set *eps_via_nodes, + struct re_fail_stack_t *fs) +{ + const re_dfa_t *const dfa = mctx->dfa; + int i, err; + if (IS_EPSILON_NODE (dfa->nodes[node].type)) + { + re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes; + re_node_set *edests = &dfa->edests[node]; + int dest_node; + err = re_node_set_insert (eps_via_nodes, node); + if (BE (err < 0, 0)) + return -2; + /* Pick up a valid destination, or return -1 if none is found. */ + for (dest_node = -1, i = 0; i < edests->nelem; ++i) + { + int candidate = edests->elems[i]; + if (!re_node_set_contains (cur_nodes, candidate)) + continue; + if (dest_node == -1) + dest_node = candidate; + + else + { + /* In order to avoid infinite loop like "(a*)*", return the second + epsilon-transition if the first was already considered. */ + if (re_node_set_contains (eps_via_nodes, dest_node)) + return candidate; + + /* Otherwise, push the second epsilon-transition on the fail stack. */ + else if (fs != NULL + && push_fail_stack (fs, *pidx, candidate, nregs, regs, + eps_via_nodes)) + return -2; + + /* We know we are going to exit. */ + break; + } + } + return dest_node; + } + else + { + int naccepted = 0; + re_token_type_t type = dfa->nodes[node].type; + +#ifdef RE_ENABLE_I18N + if (dfa->nodes[node].accept_mb) + naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx); + else +#endif /* RE_ENABLE_I18N */ + if (type == OP_BACK_REF) + { + int subexp_idx = dfa->nodes[node].opr.idx + 1; + naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so; + if (fs != NULL) + { + if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1) + return -1; + else if (naccepted) + { + char *buf = (char *) re_string_get_buffer (&mctx->input); + if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx, + naccepted) != 0) + return -1; + } + } + + if (naccepted == 0) + { + int dest_node; + err = re_node_set_insert (eps_via_nodes, node); + if (BE (err < 0, 0)) + return -2; + dest_node = dfa->edests[node].elems[0]; + if (re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node)) + return dest_node; + } + } + + if (naccepted != 0 + || check_node_accept (mctx, dfa->nodes + node, *pidx)) + { + int dest_node = dfa->nexts[node]; + *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted; + if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL + || !re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node))) + return -1; + re_node_set_empty (eps_via_nodes); + return dest_node; + } + } + return -1; +} + +static reg_errcode_t +internal_function +push_fail_stack (struct re_fail_stack_t *fs, int str_idx, int dest_node, + int nregs, regmatch_t *regs, re_node_set *eps_via_nodes) +{ + reg_errcode_t err; + int num = fs->num++; + if (fs->num == fs->alloc) + { + struct re_fail_stack_ent_t *new_array; + new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t) + * fs->alloc * 2)); + if (new_array == NULL) + return REG_ESPACE; + fs->alloc *= 2; + fs->stack = new_array; + } + fs->stack[num].idx = str_idx; + fs->stack[num].node = dest_node; + fs->stack[num].regs = re_malloc (regmatch_t, nregs); + if (fs->stack[num].regs == NULL) + return REG_ESPACE; + memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs); + err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes); + return err; +} + +static int +internal_function +pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs, + regmatch_t *regs, re_node_set *eps_via_nodes) +{ + int num = --fs->num; + assert (num >= 0); + *pidx = fs->stack[num].idx; + memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs); + re_node_set_free (eps_via_nodes); + re_free (fs->stack[num].regs); + *eps_via_nodes = fs->stack[num].eps_via_nodes; + return fs->stack[num].node; +} + +/* Set the positions where the subexpressions are starts/ends to registers + PMATCH. + Note: We assume that pmatch[0] is already set, and + pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch. */ + +static reg_errcode_t +internal_function +set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch, + regmatch_t *pmatch, int fl_backtrack) +{ + const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; + int idx, cur_node; + re_node_set eps_via_nodes; + struct re_fail_stack_t *fs; + struct re_fail_stack_t fs_body = { 0, 2, NULL }; + regmatch_t *prev_idx_match; + int prev_idx_match_malloced = 0; + +#ifdef DEBUG + assert (nmatch > 1); + assert (mctx->state_log != NULL); +#endif + if (fl_backtrack) + { + fs = &fs_body; + fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc); + if (fs->stack == NULL) + return REG_ESPACE; + } + else + fs = NULL; + + cur_node = dfa->init_node; + re_node_set_init_empty (&eps_via_nodes); + + if (__libc_use_alloca (nmatch * sizeof (regmatch_t))) + prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t)); + else + { + prev_idx_match = re_malloc (regmatch_t, nmatch); + if (prev_idx_match == NULL) + { + free_fail_stack_return (fs); + return REG_ESPACE; + } + prev_idx_match_malloced = 1; + } + memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); + + for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;) + { + update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch); + + if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node) + { + int reg_idx; + if (fs) + { + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1) + break; + if (reg_idx == nmatch) + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return free_fail_stack_return (fs); + } + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + } + else + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return REG_NOERROR; + } + } + + /* Proceed to next node. */ + cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node, + &eps_via_nodes, fs); + + if (BE (cur_node < 0, 0)) + { + if (BE (cur_node == -2, 0)) + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + free_fail_stack_return (fs); + return REG_ESPACE; + } + if (fs) + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + else + { + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return REG_NOMATCH; + } + } + } + re_node_set_free (&eps_via_nodes); + if (prev_idx_match_malloced) + re_free (prev_idx_match); + return free_fail_stack_return (fs); +} + +static reg_errcode_t +internal_function +free_fail_stack_return (struct re_fail_stack_t *fs) +{ + if (fs) + { + int fs_idx; + for (fs_idx = 0; fs_idx < fs->num; ++fs_idx) + { + re_node_set_free (&fs->stack[fs_idx].eps_via_nodes); + re_free (fs->stack[fs_idx].regs); + } + re_free (fs->stack); + } + return REG_NOERROR; +} + +static void +internal_function +update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, + regmatch_t *prev_idx_match, int cur_node, int cur_idx, int nmatch) +{ + int type = dfa->nodes[cur_node].type; + if (type == OP_OPEN_SUBEXP) + { + int reg_num = dfa->nodes[cur_node].opr.idx + 1; + + /* We are at the first node of this sub expression. */ + if (reg_num < nmatch) + { + pmatch[reg_num].rm_so = cur_idx; + pmatch[reg_num].rm_eo = -1; + } + } + else if (type == OP_CLOSE_SUBEXP) + { + int reg_num = dfa->nodes[cur_node].opr.idx + 1; + if (reg_num < nmatch) + { + /* We are at the last node of this sub expression. */ + if (pmatch[reg_num].rm_so < cur_idx) + { + pmatch[reg_num].rm_eo = cur_idx; + /* This is a non-empty match or we are not inside an optional + subexpression. Accept this right away. */ + memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); + } + else + { + if (dfa->nodes[cur_node].opt_subexp + && prev_idx_match[reg_num].rm_so != -1) + /* We transited through an empty match for an optional + subexpression, like (a?)*, and this is not the subexp's + first match. Copy back the old content of the registers + so that matches of an inner subexpression are undone as + well, like in ((a?))*. */ + memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch); + else + /* We completed a subexpression, but it may be part of + an optional one, so do not update PREV_IDX_MATCH. */ + pmatch[reg_num].rm_eo = cur_idx; + } + } + } +} + +/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0 + and sift the nodes in each states according to the following rules. + Updated state_log will be wrote to STATE_LOG. + + Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if... + 1. When STR_IDX == MATCH_LAST(the last index in the state_log): + If `a' isn't the LAST_NODE and `a' can't epsilon transit to + the LAST_NODE, we throw away the node `a'. + 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts + string `s' and transit to `b': + i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw + away the node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is + thrown away, we throw away the node `a'. + 3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b': + i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the + node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away, + we throw away the node `a'. */ + +#define STATE_NODE_CONTAINS(state,node) \ + ((state) != NULL && re_node_set_contains (&(state)->nodes, node)) + +static reg_errcode_t +internal_function +sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx) +{ + reg_errcode_t err; + int null_cnt = 0; + int str_idx = sctx->last_str_idx; + re_node_set cur_dest; + +#ifdef DEBUG + assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL); +#endif + + /* Build sifted state_log[str_idx]. It has the nodes which can epsilon + transit to the last_node and the last_node itself. */ + err = re_node_set_init_1 (&cur_dest, sctx->last_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* Then check each states in the state_log. */ + while (str_idx > 0) + { + /* Update counters. */ + null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0; + if (null_cnt > mctx->max_mb_elem_len) + { + memset (sctx->sifted_states, '\0', + sizeof (re_dfastate_t *) * str_idx); + re_node_set_free (&cur_dest); + return REG_NOERROR; + } + re_node_set_empty (&cur_dest); + --str_idx; + + if (mctx->state_log[str_idx]) + { + err = build_sifted_states (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + + /* Add all the nodes which satisfy the following conditions: + - It can epsilon transit to a node in CUR_DEST. + - It is in CUR_SRC. + And update state_log. */ + err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + err = REG_NOERROR; + free_return: + re_node_set_free (&cur_dest); + return err; +} + +static reg_errcode_t +internal_function +build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx, + int str_idx, re_node_set *cur_dest) +{ + const re_dfa_t *const dfa = mctx->dfa; + const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes; + int i; + + /* Then build the next sifted state. + We build the next sifted state on `cur_dest', and update + `sifted_states[str_idx]' with `cur_dest'. + Note: + `cur_dest' is the sifted state from `state_log[str_idx + 1]'. + `cur_src' points the node_set of the old `state_log[str_idx]' + (with the epsilon nodes pre-filtered out). */ + for (i = 0; i < cur_src->nelem; i++) + { + int prev_node = cur_src->elems[i]; + int naccepted = 0; + int ret; + +#ifdef DEBUG + re_token_type_t type = dfa->nodes[prev_node].type; + assert (!IS_EPSILON_NODE (type)); +#endif +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (dfa->nodes[prev_node].accept_mb) + naccepted = sift_states_iter_mb (mctx, sctx, prev_node, + str_idx, sctx->last_str_idx); +#endif /* RE_ENABLE_I18N */ + + /* We don't check backreferences here. + See update_cur_sifted_state(). */ + if (!naccepted + && check_node_accept (mctx, dfa->nodes + prev_node, str_idx) + && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1], + dfa->nexts[prev_node])) + naccepted = 1; + + if (naccepted == 0) + continue; + + if (sctx->limits.nelem) + { + int to_idx = str_idx + naccepted; + if (check_dst_limits (mctx, &sctx->limits, + dfa->nexts[prev_node], to_idx, + prev_node, str_idx)) + continue; + } + ret = re_node_set_insert (cur_dest, prev_node); + if (BE (ret == -1, 0)) + return REG_ESPACE; + } + + return REG_NOERROR; +} + +/* Helper functions. */ + +static reg_errcode_t +internal_function +clean_state_log_if_needed (re_match_context_t *mctx, int next_state_log_idx) +{ + int top = mctx->state_log_top; + + if (next_state_log_idx >= mctx->input.bufs_len + || (next_state_log_idx >= mctx->input.valid_len + && mctx->input.valid_len < mctx->input.len)) + { + reg_errcode_t err; + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (top < next_state_log_idx) + { + memset (mctx->state_log + top + 1, '\0', + sizeof (re_dfastate_t *) * (next_state_log_idx - top)); + mctx->state_log_top = next_state_log_idx; + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst, + re_dfastate_t **src, int num) +{ + int st_idx; + reg_errcode_t err; + for (st_idx = 0; st_idx < num; ++st_idx) + { + if (dst[st_idx] == NULL) + dst[st_idx] = src[st_idx]; + else if (src[st_idx] != NULL) + { + re_node_set merged_set; + err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes, + &src[st_idx]->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + dst[st_idx] = re_acquire_state (&err, dfa, &merged_set); + re_node_set_free (&merged_set); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +update_cur_sifted_state (const re_match_context_t *mctx, + re_sift_context_t *sctx, int str_idx, + re_node_set *dest_nodes) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err = REG_NOERROR; + const re_node_set *candidates; + candidates = ((mctx->state_log[str_idx] == NULL) ? NULL + : &mctx->state_log[str_idx]->nodes); + + if (dest_nodes->nelem == 0) + sctx->sifted_states[str_idx] = NULL; + else + { + if (candidates) + { + /* At first, add the nodes which can epsilon transit to a node in + DEST_NODE. */ + err = add_epsilon_src_nodes (dfa, dest_nodes, candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* Then, check the limitations in the current sift_context. */ + if (sctx->limits.nelem) + { + err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits, + mctx->bkref_ents, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + + sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (candidates && mctx->state_log[str_idx]->has_backref) + { + err = sift_states_bkref (mctx, sctx, str_idx, candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes, + const re_node_set *candidates) +{ + reg_errcode_t err = REG_NOERROR; + int i; + + re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (!state->inveclosure.alloc) + { + err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem); + if (BE (err != REG_NOERROR, 0)) + return REG_ESPACE; + for (i = 0; i < dest_nodes->nelem; i++) + re_node_set_merge (&state->inveclosure, + dfa->inveclosures + dest_nodes->elems[i]); + } + return re_node_set_add_intersect (dest_nodes, candidates, + &state->inveclosure); +} + +static reg_errcode_t +internal_function +sub_epsilon_src_nodes (const re_dfa_t *dfa, int node, re_node_set *dest_nodes, + const re_node_set *candidates) +{ + int ecl_idx; + reg_errcode_t err; + re_node_set *inv_eclosure = dfa->inveclosures + node; + re_node_set except_nodes; + re_node_set_init_empty (&except_nodes); + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + int cur_node = inv_eclosure->elems[ecl_idx]; + if (cur_node == node) + continue; + if (IS_EPSILON_NODE (dfa->nodes[cur_node].type)) + { + int edst1 = dfa->edests[cur_node].elems[0]; + int edst2 = ((dfa->edests[cur_node].nelem > 1) + ? dfa->edests[cur_node].elems[1] : -1); + if ((!re_node_set_contains (inv_eclosure, edst1) + && re_node_set_contains (dest_nodes, edst1)) + || (edst2 > 0 + && !re_node_set_contains (inv_eclosure, edst2) + && re_node_set_contains (dest_nodes, edst2))) + { + err = re_node_set_add_intersect (&except_nodes, candidates, + dfa->inveclosures + cur_node); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&except_nodes); + return err; + } + } + } + } + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + int cur_node = inv_eclosure->elems[ecl_idx]; + if (!re_node_set_contains (&except_nodes, cur_node)) + { + int idx = re_node_set_contains (dest_nodes, cur_node) - 1; + re_node_set_remove_at (dest_nodes, idx); + } + } + re_node_set_free (&except_nodes); + return REG_NOERROR; +} + +static int +internal_function +check_dst_limits (const re_match_context_t *mctx, re_node_set *limits, + int dst_node, int dst_idx, int src_node, int src_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + int lim_idx, src_pos, dst_pos; + + int dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx); + int src_bkref_idx = search_cur_bkref_entry (mctx, src_idx); + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + int subexp_idx; + struct re_backref_cache_entry *ent; + ent = mctx->bkref_ents + limits->elems[lim_idx]; + subexp_idx = dfa->nodes[ent->node].opr.idx; + + dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, dst_node, dst_idx, + dst_bkref_idx); + src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, src_node, src_idx, + src_bkref_idx); + + /* In case of: + ( ) + ( ) + ( ) */ + if (src_pos == dst_pos) + continue; /* This is unrelated limitation. */ + else + return 1; + } + return 0; +} + +static int +internal_function +check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, + int subexp_idx, int from_node, int bkref_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + const re_node_set *eclosures = dfa->eclosures + from_node; + int node_idx; + + /* Else, we are on the boundary: examine the nodes on the epsilon + closure. */ + for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx) + { + int node = eclosures->elems[node_idx]; + switch (dfa->nodes[node].type) + { + case OP_BACK_REF: + if (bkref_idx != -1) + { + struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx; + do + { + int dst, cpos; + + if (ent->node != node) + continue; + + if (subexp_idx < BITSET_WORD_BITS + && !(ent->eps_reachable_subexps_map + & ((bitset_word_t) 1 << subexp_idx))) + continue; + + /* Recurse trying to reach the OP_OPEN_SUBEXP and + OP_CLOSE_SUBEXP cases below. But, if the + destination node is the same node as the source + node, don't recurse because it would cause an + infinite loop: a regex that exhibits this behavior + is ()\1*\1* */ + dst = dfa->edests[node].elems[0]; + if (dst == from_node) + { + if (boundaries & 1) + return -1; + else /* if (boundaries & 2) */ + return 0; + } + + cpos = + check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, + dst, bkref_idx); + if (cpos == -1 /* && (boundaries & 1) */) + return -1; + if (cpos == 0 && (boundaries & 2)) + return 0; + + if (subexp_idx < BITSET_WORD_BITS) + ent->eps_reachable_subexps_map + &= ~((bitset_word_t) 1 << subexp_idx); + } + while (ent++->more); + } + break; + + case OP_OPEN_SUBEXP: + if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx) + return -1; + break; + + case OP_CLOSE_SUBEXP: + if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx) + return 0; + break; + + default: + break; + } + } + + return (boundaries & 2) ? 1 : 0; +} + +static int +internal_function +check_dst_limits_calc_pos (const re_match_context_t *mctx, int limit, + int subexp_idx, int from_node, int str_idx, + int bkref_idx) +{ + struct re_backref_cache_entry *lim = mctx->bkref_ents + limit; + int boundaries; + + /* If we are outside the range of the subexpression, return -1 or 1. */ + if (str_idx < lim->subexp_from) + return -1; + + if (lim->subexp_to < str_idx) + return 1; + + /* If we are within the subexpression, return 0. */ + boundaries = (str_idx == lim->subexp_from); + boundaries |= (str_idx == lim->subexp_to) << 1; + if (boundaries == 0) + return 0; + + /* Else, examine epsilon closure. */ + return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, + from_node, bkref_idx); +} + +/* Check the limitations of sub expressions LIMITS, and remove the nodes + which are against limitations from DEST_NODES. */ + +static reg_errcode_t +internal_function +check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes, + const re_node_set *candidates, re_node_set *limits, + struct re_backref_cache_entry *bkref_ents, int str_idx) +{ + reg_errcode_t err; + int node_idx, lim_idx; + + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + int subexp_idx; + struct re_backref_cache_entry *ent; + ent = bkref_ents + limits->elems[lim_idx]; + + if (str_idx <= ent->subexp_from || ent->str_idx < str_idx) + continue; /* This is unrelated limitation. */ + + subexp_idx = dfa->nodes[ent->node].opr.idx; + if (ent->subexp_to == str_idx) + { + int ops_node = -1; + int cls_node = -1; + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + int node = dest_nodes->elems[node_idx]; + re_token_type_t type = dfa->nodes[node].type; + if (type == OP_OPEN_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + ops_node = node; + else if (type == OP_CLOSE_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + cls_node = node; + } + + /* Check the limitation of the open subexpression. */ + /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */ + if (ops_node >= 0) + { + err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Check the limitation of the close subexpression. */ + if (cls_node >= 0) + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + int node = dest_nodes->elems[node_idx]; + if (!re_node_set_contains (dfa->inveclosures + node, + cls_node) + && !re_node_set_contains (dfa->eclosures + node, + cls_node)) + { + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes (dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + --node_idx; + } + } + } + else /* (ent->subexp_to != str_idx) */ + { + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + int node = dest_nodes->elems[node_idx]; + re_token_type_t type = dfa->nodes[node].type; + if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP) + { + if (subexp_idx != dfa->nodes[node].opr.idx) + continue; + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes (dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + } + } + return REG_NOERROR; +} + +static reg_errcode_t +internal_function +sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, + int str_idx, const re_node_set *candidates) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int node_idx, node; + re_sift_context_t local_sctx; + int first_idx = search_cur_bkref_entry (mctx, str_idx); + + if (first_idx == -1) + return REG_NOERROR; + + local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */ + + for (node_idx = 0; node_idx < candidates->nelem; ++node_idx) + { + int enabled_idx; + re_token_type_t type; + struct re_backref_cache_entry *entry; + node = candidates->elems[node_idx]; + type = dfa->nodes[node].type; + /* Avoid infinite loop for the REs like "()\1+". */ + if (node == sctx->last_node && str_idx == sctx->last_str_idx) + continue; + if (type != OP_BACK_REF) + continue; + + entry = mctx->bkref_ents + first_idx; + enabled_idx = first_idx; + do + { + int subexp_len; + int to_idx; + int dst_node; + int ret; + re_dfastate_t *cur_state; + + if (entry->node != node) + continue; + subexp_len = entry->subexp_to - entry->subexp_from; + to_idx = str_idx + subexp_len; + dst_node = (subexp_len ? dfa->nexts[node] + : dfa->edests[node].elems[0]); + + if (to_idx > sctx->last_str_idx + || sctx->sifted_states[to_idx] == NULL + || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node) + || check_dst_limits (mctx, &sctx->limits, node, + str_idx, dst_node, to_idx)) + continue; + + if (local_sctx.sifted_states == NULL) + { + local_sctx = *sctx; + err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.last_node = node; + local_sctx.last_str_idx = str_idx; + ret = re_node_set_insert (&local_sctx.limits, enabled_idx); + if (BE (ret < 0, 0)) + { + err = REG_ESPACE; + goto free_return; + } + cur_state = local_sctx.sifted_states[str_idx]; + err = sift_states_backward (mctx, &local_sctx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + if (sctx->limited_states != NULL) + { + err = merge_state_array (dfa, sctx->limited_states, + local_sctx.sifted_states, + str_idx + 1); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.sifted_states[str_idx] = cur_state; + re_node_set_remove (&local_sctx.limits, enabled_idx); + + /* mctx->bkref_ents may have changed, reload the pointer. */ + entry = mctx->bkref_ents + enabled_idx; + } + while (enabled_idx++, entry++->more); + } + err = REG_NOERROR; + free_return: + if (local_sctx.sifted_states != NULL) + { + re_node_set_free (&local_sctx.limits); + } + + return err; +} + + +#ifdef RE_ENABLE_I18N +static int +internal_function +sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx, + int node_idx, int str_idx, int max_str_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + int naccepted; + /* Check the node can accept `multi byte'. */ + naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx); + if (naccepted > 0 && str_idx + naccepted <= max_str_idx && + !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted], + dfa->nexts[node_idx])) + /* The node can't accept the `multi byte', or the + destination was already thrown away, then the node + could't accept the current input `multi byte'. */ + naccepted = 0; + /* Otherwise, it is sure that the node could accept + `naccepted' bytes input. */ + return naccepted; +} +#endif /* RE_ENABLE_I18N */ + + +/* Functions for state transition. */ + +/* Return the next state to which the current state STATE will transit by + accepting the current input byte, and update STATE_LOG if necessary. + If STATE can accept a multibyte char/collating element/back reference + update the destination of STATE_LOG. */ + +static re_dfastate_t * +internal_function +transit_state (reg_errcode_t *err, re_match_context_t *mctx, + re_dfastate_t *state) +{ + re_dfastate_t **trtable; + unsigned char ch; + +#ifdef RE_ENABLE_I18N + /* If the current state can accept multibyte. */ + if (BE (state->accept_mb, 0)) + { + *err = transit_state_mb (mctx, state); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } +#endif /* RE_ENABLE_I18N */ + + /* Then decide the next state with the single byte. */ +#if 0 + if (0) + /* don't use transition table */ + return transit_state_sb (err, mctx, state); +#endif + + /* Use transition table */ + ch = re_string_fetch_byte (&mctx->input); + for (;;) + { + trtable = state->trtable; + if (BE (trtable != NULL, 1)) + return trtable[ch]; + + trtable = state->word_trtable; + if (BE (trtable != NULL, 1)) + { + unsigned int context; + context + = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input) - 1, + mctx->eflags); + if (IS_WORD_CONTEXT (context)) + return trtable[ch + SBC_MAX]; + else + return trtable[ch]; + } + + if (!build_trtable (mctx->dfa, state)) + { + *err = REG_ESPACE; + return NULL; + } + + /* Retry, we now have a transition table. */ + } +} + +/* Update the state_log if we need */ +re_dfastate_t * +internal_function +merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx, + re_dfastate_t *next_state) +{ + const re_dfa_t *const dfa = mctx->dfa; + int cur_idx = re_string_cur_idx (&mctx->input); + + if (cur_idx > mctx->state_log_top) + { + mctx->state_log[cur_idx] = next_state; + mctx->state_log_top = cur_idx; + } + else if (mctx->state_log[cur_idx] == 0) + { + mctx->state_log[cur_idx] = next_state; + } + else + { + re_dfastate_t *pstate; + unsigned int context; + re_node_set next_nodes, *log_nodes, *table_nodes = NULL; + /* If (state_log[cur_idx] != 0), it implies that cur_idx is + the destination of a multibyte char/collating element/ + back reference. Then the next state is the union set of + these destinations and the results of the transition table. */ + pstate = mctx->state_log[cur_idx]; + log_nodes = pstate->entrance_nodes; + if (next_state != NULL) + { + table_nodes = next_state->entrance_nodes; + *err = re_node_set_init_union (&next_nodes, table_nodes, + log_nodes); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + else + next_nodes = *log_nodes; + /* Note: We already add the nodes of the initial state, + then we don't need to add them here. */ + + context = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input) - 1, + mctx->eflags); + next_state = mctx->state_log[cur_idx] + = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + if (table_nodes != NULL) + re_node_set_free (&next_nodes); + } + + if (BE (dfa->nbackref, 0) && next_state != NULL) + { + /* Check OP_OPEN_SUBEXP in the current state in case that we use them + later. We must check them here, since the back references in the + next state might use them. */ + *err = check_subexp_matching_top (mctx, &next_state->nodes, + cur_idx); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + + /* If the next state has back references. */ + if (next_state->has_backref) + { + *err = transit_state_bkref (mctx, &next_state->nodes); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + next_state = mctx->state_log[cur_idx]; + } + } + + return next_state; +} + +/* Skip bytes in the input that correspond to part of a + multi-byte match, then look in the log for a state + from which to restart matching. */ +re_dfastate_t * +internal_function +find_recover_state (reg_errcode_t *err, re_match_context_t *mctx) +{ + re_dfastate_t *cur_state; + do + { + int max = mctx->state_log_top; + int cur_str_idx = re_string_cur_idx (&mctx->input); + + do + { + if (++cur_str_idx > max) + return NULL; + re_string_skip_bytes (&mctx->input, 1); + } + while (mctx->state_log[cur_str_idx] == NULL); + + cur_state = merge_state_with_log (err, mctx, NULL); + } + while (*err == REG_NOERROR && cur_state == NULL); + return cur_state; +} + +/* Helper functions for transit_state. */ + +/* From the node set CUR_NODES, pick up the nodes whose types are + OP_OPEN_SUBEXP and which have corresponding back references in the regular + expression. And register them to use them later for evaluating the + correspoding back references. */ + +static reg_errcode_t +internal_function +check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes, + int str_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + int node_idx; + reg_errcode_t err; + + /* TODO: This isn't efficient. + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx) + { + int node = cur_nodes->elems[node_idx]; + if (dfa->nodes[node].type == OP_OPEN_SUBEXP + && dfa->nodes[node].opr.idx < BITSET_WORD_BITS + && (dfa->used_bkref_map + & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx))) + { + err = match_ctx_add_subtop (mctx, node, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +#if 0 +/* Return the next state to which the current state STATE will transit by + accepting the current input byte. */ + +static re_dfastate_t * +transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx, + re_dfastate_t *state) +{ + const re_dfa_t *const dfa = mctx->dfa; + re_node_set next_nodes; + re_dfastate_t *next_state; + int node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input); + unsigned int context; + + *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt) + { + int cur_node = state->nodes.elems[node_cnt]; + if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx)) + { + *err = re_node_set_merge (&next_nodes, + dfa->eclosures + dfa->nexts[cur_node]); + if (BE (*err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return NULL; + } + } + } + context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags); + next_state = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + re_node_set_free (&next_nodes); + re_string_skip_bytes (&mctx->input, 1); + return next_state; +} +#endif + +#ifdef RE_ENABLE_I18N +static reg_errcode_t +internal_function +transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int i; + + for (i = 0; i < pstate->nodes.nelem; ++i) + { + re_node_set dest_nodes, *new_nodes; + int cur_node_idx = pstate->nodes.elems[i]; + int naccepted, dest_idx; + unsigned int context; + re_dfastate_t *dest_state; + + if (!dfa->nodes[cur_node_idx].accept_mb) + continue; + + if (dfa->nodes[cur_node_idx].constraint) + { + context = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input), + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint, + context)) + continue; + } + + /* How many bytes the node can accept? */ + naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input, + re_string_cur_idx (&mctx->input)); + if (naccepted == 0) + continue; + + /* The node can accepts `naccepted' bytes. */ + dest_idx = re_string_cur_idx (&mctx->input) + naccepted; + mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted + : mctx->max_mb_elem_len); + err = clean_state_log_if_needed (mctx, dest_idx); + if (BE (err != REG_NOERROR, 0)) + return err; +#ifdef DEBUG + assert (dfa->nexts[cur_node_idx] != -1); +#endif + new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx]; + + dest_state = mctx->state_log[dest_idx]; + if (dest_state == NULL) + dest_nodes = *new_nodes; + else + { + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, new_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + context = re_string_context_at (&mctx->input, dest_idx - 1, + mctx->eflags); + mctx->state_log[dest_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + if (dest_state != NULL) + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} +#endif /* RE_ENABLE_I18N */ + +static reg_errcode_t +internal_function +transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int i; + int cur_str_idx = re_string_cur_idx (&mctx->input); + + for (i = 0; i < nodes->nelem; ++i) + { + int dest_str_idx, prev_nelem, bkc_idx; + int node_idx = nodes->elems[i]; + unsigned int context; + const re_token_t *node = dfa->nodes + node_idx; + re_node_set *new_dest_nodes; + + /* Check whether `node' is a backreference or not. */ + if (node->type != OP_BACK_REF) + continue; + + if (node->constraint) + { + context = re_string_context_at (&mctx->input, cur_str_idx, + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + continue; + } + + /* `node' is a backreference. + Check the substring which the substring matched. */ + bkc_idx = mctx->nbkref_ents; + err = get_subexp (mctx, node_idx, cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* And add the epsilon closures (which is `new_dest_nodes') of + the backreference to appropriate state_log. */ +#ifdef DEBUG + assert (dfa->nexts[node_idx] != -1); +#endif + for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx) + { + int subexp_len; + re_dfastate_t *dest_state; + struct re_backref_cache_entry *bkref_ent; + bkref_ent = mctx->bkref_ents + bkc_idx; + if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx) + continue; + subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from; + new_dest_nodes = (subexp_len == 0 + ? dfa->eclosures + dfa->edests[node_idx].elems[0] + : dfa->eclosures + dfa->nexts[node_idx]); + dest_str_idx = (cur_str_idx + bkref_ent->subexp_to + - bkref_ent->subexp_from); + context = re_string_context_at (&mctx->input, dest_str_idx - 1, + mctx->eflags); + dest_state = mctx->state_log[dest_str_idx]; + prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0 + : mctx->state_log[cur_str_idx]->nodes.nelem); + /* Add `new_dest_node' to state_log. */ + if (dest_state == NULL) + { + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, new_dest_nodes, + context); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + else + { + re_node_set dest_nodes; + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, + new_dest_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&dest_nodes); + goto free_return; + } + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + /* We need to check recursively if the backreference can epsilon + transit. */ + if (subexp_len == 0 + && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem) + { + err = check_subexp_matching_top (mctx, new_dest_nodes, + cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + err = transit_state_bkref (mctx, new_dest_nodes); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + } + } + err = REG_NOERROR; + free_return: + return err; +} + +/* Enumerate all the candidates which the backreference BKREF_NODE can match + at BKREF_STR_IDX, and register them by match_ctx_add_entry(). + Note that we might collect inappropriate candidates here. + However, the cost of checking them strictly here is too high, then we + delay these checking for prune_impossible_nodes(). */ + +static reg_errcode_t +internal_function +get_subexp (re_match_context_t *mctx, int bkref_node, int bkref_str_idx) +{ + const re_dfa_t *const dfa = mctx->dfa; + int subexp_num, sub_top_idx; + const char *buf = (const char *) re_string_get_buffer (&mctx->input); + /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */ + int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx); + if (cache_idx != -1) + { + const struct re_backref_cache_entry *entry + = mctx->bkref_ents + cache_idx; + do + if (entry->node == bkref_node) + return REG_NOERROR; /* We already checked it. */ + while (entry++->more); + } + + subexp_num = dfa->nodes[bkref_node].opr.idx; + + /* For each sub expression */ + for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx) + { + reg_errcode_t err; + re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx]; + re_sub_match_last_t *sub_last; + int sub_last_idx, sl_str, bkref_str_off; + + if (dfa->nodes[sub_top->node].opr.idx != subexp_num) + continue; /* It isn't related. */ + + sl_str = sub_top->str_idx; + bkref_str_off = bkref_str_idx; + /* At first, check the last node of sub expressions we already + evaluated. */ + for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx) + { + int sl_str_diff; + sub_last = sub_top->lasts[sub_last_idx]; + sl_str_diff = sub_last->str_idx - sl_str; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_diff > 0) + { + if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0)) + { + /* Not enough chars for a successful match. */ + if (bkref_str_off + sl_str_diff > mctx->input.len) + break; + + err = clean_state_log_if_needed (mctx, + bkref_str_off + + sl_str_diff); + if (BE (err != REG_NOERROR, 0)) + return err; + buf = (const char *) re_string_get_buffer (&mctx->input); + } + if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0) + /* We don't need to search this sub expression any more. */ + break; + } + bkref_str_off += sl_str_diff; + sl_str += sl_str_diff; + err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + + /* Reload buf, since the preceding call might have reallocated + the buffer. */ + buf = (const char *) re_string_get_buffer (&mctx->input); + + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (sub_last_idx < sub_top->nlasts) + continue; + if (sub_last_idx > 0) + ++sl_str; + /* Then, search for the other last nodes of the sub expression. */ + for (; sl_str <= bkref_str_idx; ++sl_str) + { + int cls_node, sl_str_off; + const re_node_set *nodes; + sl_str_off = sl_str - sub_top->str_idx; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_off > 0) + { + if (BE (bkref_str_off >= mctx->input.valid_len, 0)) + { + /* If we are at the end of the input, we cannot match. */ + if (bkref_str_off >= mctx->input.len) + break; + + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + + buf = (const char *) re_string_get_buffer (&mctx->input); + } + if (buf [bkref_str_off++] != buf[sl_str - 1]) + break; /* We don't need to search this sub expression + any more. */ + } + if (mctx->state_log[sl_str] == NULL) + continue; + /* Does this state have a ')' of the sub expression? */ + nodes = &mctx->state_log[sl_str]->nodes; + cls_node = find_subexp_node (dfa, nodes, subexp_num, + OP_CLOSE_SUBEXP); + if (cls_node == -1) + continue; /* No. */ + if (sub_top->path == NULL) + { + sub_top->path = calloc (sizeof (state_array_t), + sl_str - sub_top->str_idx + 1); + if (sub_top->path == NULL) + return REG_ESPACE; + } + /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node + in the current context? */ + err = check_arrival (mctx, sub_top->path, sub_top->node, + sub_top->str_idx, cls_node, sl_str, + OP_CLOSE_SUBEXP); + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str); + if (BE (sub_last == NULL, 0)) + return REG_ESPACE; + err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + if (err == REG_NOMATCH) + continue; + } + } + return REG_NOERROR; +} + +/* Helper functions for get_subexp(). */ + +/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR. + If it can arrive, register the sub expression expressed with SUB_TOP + and SUB_LAST. */ + +static reg_errcode_t +internal_function +get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top, + re_sub_match_last_t *sub_last, int bkref_node, int bkref_str) +{ + reg_errcode_t err; + int to_idx; + /* Can the subexpression arrive the back reference? */ + err = check_arrival (mctx, &sub_last->path, sub_last->node, + sub_last->str_idx, bkref_node, bkref_str, + OP_OPEN_SUBEXP); + if (err != REG_NOERROR) + return err; + err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx, + sub_last->str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx; + return clean_state_log_if_needed (mctx, to_idx); +} + +/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX. + Search '(' if FL_OPEN, or search ')' otherwise. + TODO: This function isn't efficient... + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + +static int +internal_function +find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, + int subexp_idx, int type) +{ + int cls_idx; + for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx) + { + int cls_node = nodes->elems[cls_idx]; + const re_token_t *node = dfa->nodes + cls_node; + if (node->type == type + && node->opr.idx == subexp_idx) + return cls_node; + } + return -1; +} + +/* Check whether the node TOP_NODE at TOP_STR can arrive to the node + LAST_NODE at LAST_STR. We record the path onto PATH since it will be + heavily reused. + Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */ + +static reg_errcode_t +internal_function +check_arrival (re_match_context_t *mctx, state_array_t *path, int top_node, + int top_str, int last_node, int last_str, int type) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err = REG_NOERROR; + int subexp_num, backup_cur_idx, str_idx, null_cnt; + re_dfastate_t *cur_state = NULL; + re_node_set *cur_nodes, next_nodes; + re_dfastate_t **backup_state_log; + unsigned int context; + + subexp_num = dfa->nodes[top_node].opr.idx; + /* Extend the buffer if we need. */ + if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0)) + { + re_dfastate_t **new_array; + int old_alloc = path->alloc; + path->alloc += last_str + mctx->max_mb_elem_len + 1; + new_array = re_realloc (path->array, re_dfastate_t *, path->alloc); + if (BE (new_array == NULL, 0)) + { + path->alloc = old_alloc; + return REG_ESPACE; + } + path->array = new_array; + memset (new_array + old_alloc, '\0', + sizeof (re_dfastate_t *) * (path->alloc - old_alloc)); + } + + str_idx = path->next_idx ? path->next_idx : top_str; + + /* Temporary modify MCTX. */ + backup_state_log = mctx->state_log; + backup_cur_idx = mctx->input.cur_idx; + mctx->state_log = path->array; + mctx->input.cur_idx = str_idx; + + /* Setup initial node set. */ + context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); + if (str_idx == top_str) + { + err = re_node_set_init_1 (&next_nodes, top_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + else + { + cur_state = mctx->state_log[str_idx]; + if (cur_state && cur_state->has_backref) + { + err = re_node_set_init_copy (&next_nodes, &cur_state->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + re_node_set_init_empty (&next_nodes); + } + if (str_idx == top_str || (cur_state && cur_state->has_backref)) + { + if (next_nodes.nelem) + { + err = expand_bkref_cache (mctx, &next_nodes, str_idx, + subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + } + + for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;) + { + re_node_set_empty (&next_nodes); + if (mctx->state_log[str_idx + 1]) + { + err = re_node_set_merge (&next_nodes, + &mctx->state_log[str_idx + 1]->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + if (cur_state) + { + err = check_arrival_add_next_nodes (mctx, str_idx, + &cur_state->non_eps_nodes, + &next_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + ++str_idx; + if (next_nodes.nelem) + { + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + err = expand_bkref_cache (mctx, &next_nodes, str_idx, + subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + null_cnt = cur_state == NULL ? null_cnt + 1 : 0; + } + re_node_set_free (&next_nodes); + cur_nodes = (mctx->state_log[last_str] == NULL ? NULL + : &mctx->state_log[last_str]->nodes); + path->next_idx = str_idx; + + /* Fix MCTX. */ + mctx->state_log = backup_state_log; + mctx->input.cur_idx = backup_cur_idx; + + /* Then check the current node set has the node LAST_NODE. */ + if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node)) + return REG_NOERROR; + + return REG_NOMATCH; +} + +/* Helper functions for check_arrival. */ + +/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them + to NEXT_NODES. + TODO: This function is similar to the functions transit_state*(), + however this function has many additional works. + Can't we unify them? */ + +static reg_errcode_t +internal_function +check_arrival_add_next_nodes (re_match_context_t *mctx, int str_idx, + re_node_set *cur_nodes, re_node_set *next_nodes) +{ + const re_dfa_t *const dfa = mctx->dfa; + int result; + int cur_idx; + reg_errcode_t err = REG_NOERROR; + re_node_set union_set; + re_node_set_init_empty (&union_set); + for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx) + { + int naccepted = 0; + int cur_node = cur_nodes->elems[cur_idx]; +#ifdef DEBUG + re_token_type_t type = dfa->nodes[cur_node].type; + assert (!IS_EPSILON_NODE (type)); +#endif +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (dfa->nodes[cur_node].accept_mb) + { + naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input, + str_idx); + if (naccepted > 1) + { + re_dfastate_t *dest_state; + int next_node = dfa->nexts[cur_node]; + int next_idx = str_idx + naccepted; + dest_state = mctx->state_log[next_idx]; + re_node_set_empty (&union_set); + if (dest_state) + { + err = re_node_set_merge (&union_set, &dest_state->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + } + result = re_node_set_insert (&union_set, next_node); + if (BE (result < 0, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + mctx->state_log[next_idx] = re_acquire_state (&err, dfa, + &union_set); + if (BE (mctx->state_log[next_idx] == NULL + && err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + } + } +#endif /* RE_ENABLE_I18N */ + if (naccepted + || check_node_accept (mctx, dfa->nodes + cur_node, str_idx)) + { + result = re_node_set_insert (next_nodes, dfa->nexts[cur_node]); + if (BE (result < 0, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + } + } + re_node_set_free (&union_set); + return REG_NOERROR; +} + +/* For all the nodes in CUR_NODES, add the epsilon closures of them to + CUR_NODES, however exclude the nodes which are: + - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN. + - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN. +*/ + +static reg_errcode_t +internal_function +check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes, + int ex_subexp, int type) +{ + reg_errcode_t err; + int idx, outside_node; + re_node_set new_nodes; +#ifdef DEBUG + assert (cur_nodes->nelem); +#endif + err = re_node_set_alloc (&new_nodes, cur_nodes->nelem); + if (BE (err != REG_NOERROR, 0)) + return err; + /* Create a new node set NEW_NODES with the nodes which are epsilon + closures of the node in CUR_NODES. */ + + for (idx = 0; idx < cur_nodes->nelem; ++idx) + { + int cur_node = cur_nodes->elems[idx]; + const re_node_set *eclosure = dfa->eclosures + cur_node; + outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type); + if (outside_node == -1) + { + /* There are no problematic nodes, just merge them. */ + err = re_node_set_merge (&new_nodes, eclosure); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + else + { + /* There are problematic nodes, re-calculate incrementally. */ + err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node, + ex_subexp, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + } + re_node_set_free (cur_nodes); + *cur_nodes = new_nodes; + return REG_NOERROR; +} + +/* Helper function for check_arrival_expand_ecl. + Check incrementally the epsilon closure of TARGET, and if it isn't + problematic append it to DST_NODES. */ + +static reg_errcode_t +internal_function +check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes, + int target, int ex_subexp, int type) +{ + int cur_node; + for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);) + { + int err; + + if (dfa->nodes[cur_node].type == type + && dfa->nodes[cur_node].opr.idx == ex_subexp) + { + if (type == OP_CLOSE_SUBEXP) + { + err = re_node_set_insert (dst_nodes, cur_node); + if (BE (err == -1, 0)) + return REG_ESPACE; + } + break; + } + err = re_node_set_insert (dst_nodes, cur_node); + if (BE (err == -1, 0)) + return REG_ESPACE; + if (dfa->edests[cur_node].nelem == 0) + break; + if (dfa->edests[cur_node].nelem == 2) + { + err = check_arrival_expand_ecl_sub (dfa, dst_nodes, + dfa->edests[cur_node].elems[1], + ex_subexp, type); + if (BE (err != REG_NOERROR, 0)) + return err; + } + cur_node = dfa->edests[cur_node].elems[0]; + } + return REG_NOERROR; +} + + +/* For all the back references in the current state, calculate the + destination of the back references by the appropriate entry + in MCTX->BKREF_ENTS. */ + +static reg_errcode_t +internal_function +expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes, + int cur_str, int subexp_num, int type) +{ + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int cache_idx_start = search_cur_bkref_entry (mctx, cur_str); + struct re_backref_cache_entry *ent; + + if (cache_idx_start == -1) + return REG_NOERROR; + + restart: + ent = mctx->bkref_ents + cache_idx_start; + do + { + int to_idx, next_node; + + /* Is this entry ENT is appropriate? */ + if (!re_node_set_contains (cur_nodes, ent->node)) + continue; /* No. */ + + to_idx = cur_str + ent->subexp_to - ent->subexp_from; + /* Calculate the destination of the back reference, and append it + to MCTX->STATE_LOG. */ + if (to_idx == cur_str) + { + /* The backreference did epsilon transit, we must re-check all the + node in the current state. */ + re_node_set new_dests; + reg_errcode_t err2, err3; + next_node = dfa->edests[ent->node].elems[0]; + if (re_node_set_contains (cur_nodes, next_node)) + continue; + err = re_node_set_init_1 (&new_dests, next_node); + err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type); + err3 = re_node_set_merge (cur_nodes, &new_dests); + re_node_set_free (&new_dests); + if (BE (err != REG_NOERROR || err2 != REG_NOERROR + || err3 != REG_NOERROR, 0)) + { + err = (err != REG_NOERROR ? err + : (err2 != REG_NOERROR ? err2 : err3)); + return err; + } + /* TODO: It is still inefficient... */ + goto restart; + } + else + { + re_node_set union_set; + next_node = dfa->nexts[ent->node]; + if (mctx->state_log[to_idx]) + { + int ret; + if (re_node_set_contains (&mctx->state_log[to_idx]->nodes, + next_node)) + continue; + err = re_node_set_init_copy (&union_set, + &mctx->state_log[to_idx]->nodes); + ret = re_node_set_insert (&union_set, next_node); + if (BE (err != REG_NOERROR || ret < 0, 0)) + { + re_node_set_free (&union_set); + err = err != REG_NOERROR ? err : REG_ESPACE; + return err; + } + } + else + { + err = re_node_set_init_1 (&union_set, next_node); + if (BE (err != REG_NOERROR, 0)) + return err; + } + mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set); + re_node_set_free (&union_set); + if (BE (mctx->state_log[to_idx] == NULL + && err != REG_NOERROR, 0)) + return err; + } + } + while (ent++->more); + return REG_NOERROR; +} + +/* Build transition table for the state. + Return 1 if succeeded, otherwise return NULL. */ + +static int +internal_function +build_trtable (const re_dfa_t *dfa, re_dfastate_t *state) +{ + reg_errcode_t err; + int i, j, ch, need_word_trtable = 0; + bitset_word_t elem, mask; + bool dests_node_malloced = false; + bool dest_states_malloced = false; + int ndests; /* Number of the destination states from `state'. */ + re_dfastate_t **trtable; + re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl; + re_node_set follows, *dests_node; + bitset_t *dests_ch; + bitset_t acceptable; + + struct dests_alloc + { + re_node_set dests_node[SBC_MAX]; + bitset_t dests_ch[SBC_MAX]; + } *dests_alloc; + + /* We build DFA states which corresponds to the destination nodes + from `state'. `dests_node[i]' represents the nodes which i-th + destination state contains, and `dests_ch[i]' represents the + characters which i-th destination state accepts. */ + if (__libc_use_alloca (sizeof (struct dests_alloc))) + dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc)); + else + { + dests_alloc = re_malloc (struct dests_alloc, 1); + if (BE (dests_alloc == NULL, 0)) + return 0; + dests_node_malloced = true; + } + dests_node = dests_alloc->dests_node; + dests_ch = dests_alloc->dests_ch; + + /* Initialize transiton table. */ + state->word_trtable = state->trtable = NULL; + + /* At first, group all nodes belonging to `state' into several + destinations. */ + ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch); + if (BE (ndests <= 0, 0)) + { + if (dests_node_malloced) + free (dests_alloc); + /* Return 0 in case of an error, 1 otherwise. */ + if (ndests == 0) + { + state->trtable = (re_dfastate_t **) + calloc (sizeof (re_dfastate_t *), SBC_MAX); + return 1; + } + return 0; + } + + err = re_node_set_alloc (&follows, ndests + 1); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + + if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX + + ndests * 3 * sizeof (re_dfastate_t *))) + dest_states = (re_dfastate_t **) + alloca (ndests * 3 * sizeof (re_dfastate_t *)); + else + { + dest_states = (re_dfastate_t **) + malloc (ndests * 3 * sizeof (re_dfastate_t *)); + if (BE (dest_states == NULL, 0)) + { +out_free: + if (dest_states_malloced) + free (dest_states); + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + if (dests_node_malloced) + free (dests_alloc); + return 0; + } + dest_states_malloced = true; + } + dest_states_word = dest_states + ndests; + dest_states_nl = dest_states_word + ndests; + bitset_empty (acceptable); + + /* Then build the states for all destinations. */ + for (i = 0; i < ndests; ++i) + { + int next_node; + re_node_set_empty (&follows); + /* Merge the follows of this destination states. */ + for (j = 0; j < dests_node[i].nelem; ++j) + { + next_node = dfa->nexts[dests_node[i].elems[j]]; + if (next_node != -1) + { + err = re_node_set_merge (&follows, dfa->eclosures + next_node); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + } + } + dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0); + if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + /* If the new state has context constraint, + build appropriate states for these contexts. */ + if (dest_states[i]->has_constraint) + { + dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_WORD); + if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + + if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1) + need_word_trtable = 1; + + dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_NEWLINE); + if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + } + else + { + dest_states_word[i] = dest_states[i]; + dest_states_nl[i] = dest_states[i]; + } + bitset_merge (acceptable, dests_ch[i]); + } + + if (!BE (need_word_trtable, 0)) + { + /* We don't care about whether the following character is a word + character, or we are in a single-byte character set so we can + discern by looking at the character code: allocate a + 256-entry transition table. */ + trtable = state->trtable = + (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); + if (BE (trtable == NULL, 0)) + goto out_free; + + /* For all characters ch...: */ + for (i = 0; i < BITSET_WORDS; ++i) + for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; + elem; + mask <<= 1, elem >>= 1, ++ch) + if (BE (elem & 1, 0)) + { + /* There must be exactly one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) + ; + + /* j-th destination accepts the word character ch. */ + if (dfa->word_char[i] & mask) + trtable[ch] = dest_states_word[j]; + else + trtable[ch] = dest_states[j]; + } + } + else + { + /* We care about whether the following character is a word + character, and we are in a multi-byte character set: discern + by looking at the character code: build two 256-entry + transition tables, one starting at trtable[0] and one + starting at trtable[SBC_MAX]. */ + trtable = state->word_trtable = + (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX); + if (BE (trtable == NULL, 0)) + goto out_free; + + /* For all characters ch...: */ + for (i = 0; i < BITSET_WORDS; ++i) + for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; + elem; + mask <<= 1, elem >>= 1, ++ch) + if (BE (elem & 1, 0)) + { + /* There must be exactly one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) + ; + + /* j-th destination accepts the word character ch. */ + trtable[ch] = dest_states[j]; + trtable[ch + SBC_MAX] = dest_states_word[j]; + } + } + + /* new line */ + if (bitset_contain (acceptable, NEWLINE_CHAR)) + { + /* The current state accepts newline character. */ + for (j = 0; j < ndests; ++j) + if (bitset_contain (dests_ch[j], NEWLINE_CHAR)) + { + /* k-th destination accepts newline character. */ + trtable[NEWLINE_CHAR] = dest_states_nl[j]; + if (need_word_trtable) + trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j]; + /* There must be only one destination which accepts + newline. See group_nodes_into_DFAstates. */ + break; + } + } + + if (dest_states_malloced) + free (dest_states); + + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + + if (dests_node_malloced) + free (dests_alloc); + + return 1; +} + +/* Group all nodes belonging to STATE into several destinations. + Then for all destinations, set the nodes belonging to the destination + to DESTS_NODE[i] and set the characters accepted by the destination + to DEST_CH[i]. This function return the number of destinations. */ + +static int +internal_function +group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state, + re_node_set *dests_node, bitset_t *dests_ch) +{ + reg_errcode_t err; + int result; + int i, j, k; + int ndests; /* Number of the destinations from `state'. */ + bitset_t accepts; /* Characters a node can accept. */ + const re_node_set *cur_nodes = &state->nodes; + bitset_empty (accepts); + ndests = 0; + + /* For all the nodes belonging to `state', */ + for (i = 0; i < cur_nodes->nelem; ++i) + { + re_token_t *node = &dfa->nodes[cur_nodes->elems[i]]; + re_token_type_t type = node->type; + unsigned int constraint = node->constraint; + + /* Enumerate all single byte character this node can accept. */ + if (type == CHARACTER) + bitset_set (accepts, node->opr.c); + else if (type == SIMPLE_BRACKET) + { + bitset_merge (accepts, node->opr.sbcset); + } + else if (type == OP_PERIOD) + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + bitset_merge (accepts, dfa->sb_char); + else +#endif + bitset_set_all (accepts); + if (!(dfa->syntax & RE_DOT_NEWLINE)) + bitset_clear (accepts, '\n'); + if (dfa->syntax & RE_DOT_NOT_NULL) + bitset_clear (accepts, '\0'); + } +#ifdef RE_ENABLE_I18N + else if (type == OP_UTF8_PERIOD) + { + memset (accepts, '\xff', sizeof (bitset_t) / 2); + if (!(dfa->syntax & RE_DOT_NEWLINE)) + bitset_clear (accepts, '\n'); + if (dfa->syntax & RE_DOT_NOT_NULL) + bitset_clear (accepts, '\0'); + } +#endif + else + continue; + + /* Check the `accepts' and sift the characters which are not + match it the context. */ + if (constraint) + { + if (constraint & NEXT_NEWLINE_CONSTRAINT) + { + bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR); + bitset_empty (accepts); + if (accepts_newline) + bitset_set (accepts, NEWLINE_CHAR); + else + continue; + } + if (constraint & NEXT_ENDBUF_CONSTRAINT) + { + bitset_empty (accepts); + continue; + } + + if (constraint & NEXT_WORD_CONSTRAINT) + { + bitset_word_t any_set = 0; + if (type == CHARACTER && !node->word_char) + { + bitset_empty (accepts); + continue; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j])); + else +#endif + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= dfa->word_char[j]); + if (!any_set) + continue; + } + if (constraint & NEXT_NOTWORD_CONSTRAINT) + { + bitset_word_t any_set = 0; + if (type == CHARACTER && node->word_char) + { + bitset_empty (accepts); + continue; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j])); + else +#endif + for (j = 0; j < BITSET_WORDS; ++j) + any_set |= (accepts[j] &= ~dfa->word_char[j]); + if (!any_set) + continue; + } + } + + /* Then divide `accepts' into DFA states, or create a new + state. Above, we make sure that accepts is not empty. */ + for (j = 0; j < ndests; ++j) + { + bitset_t intersec; /* Intersection sets, see below. */ + bitset_t remains; + /* Flags, see below. */ + bitset_word_t has_intersec, not_subset, not_consumed; + + /* Optimization, skip if this state doesn't accept the character. */ + if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c)) + continue; + + /* Enumerate the intersection set of this state and `accepts'. */ + has_intersec = 0; + for (k = 0; k < BITSET_WORDS; ++k) + has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k]; + /* And skip if the intersection set is empty. */ + if (!has_intersec) + continue; + + /* Then check if this state is a subset of `accepts'. */ + not_subset = not_consumed = 0; + for (k = 0; k < BITSET_WORDS; ++k) + { + not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k]; + not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k]; + } + + /* If this state isn't a subset of `accepts', create a + new group state, which has the `remains'. */ + if (not_subset) + { + bitset_copy (dests_ch[ndests], remains); + bitset_copy (dests_ch[j], intersec); + err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + } + + /* Put the position in the current group. */ + result = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]); + if (BE (result < 0, 0)) + goto error_return; + + /* If all characters are consumed, go to next node. */ + if (!not_consumed) + break; + } + /* Some characters remain, create a new group. */ + if (j == ndests) + { + bitset_copy (dests_ch[ndests], accepts); + err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + bitset_empty (accepts); + } + } + return ndests; + error_return: + for (j = 0; j < ndests; ++j) + re_node_set_free (dests_node + j); + return -1; +} + +#ifdef RE_ENABLE_I18N +/* Check how many bytes the node `dfa->nodes[node_idx]' accepts. + Return the number of the bytes the node accepts. + STR_IDX is the current index of the input string. + + This function handles the nodes which can accept one character, or + one collating element like '.', '[a-z]', opposite to the other nodes + can only accept one byte. */ + +static int +internal_function +check_node_accept_bytes (const re_dfa_t *dfa, int node_idx, + const re_string_t *input, int str_idx) +{ + const re_token_t *node = dfa->nodes + node_idx; + int char_len, elem_len; + int i; + + if (BE (node->type == OP_UTF8_PERIOD, 0)) + { + unsigned char c = re_string_byte_at (input, str_idx), d; + if (BE (c < 0xc2, 1)) + return 0; + + if (str_idx + 2 > input->len) + return 0; + + d = re_string_byte_at (input, str_idx + 1); + if (c < 0xe0) + return (d < 0x80 || d > 0xbf) ? 0 : 2; + else if (c < 0xf0) + { + char_len = 3; + if (c == 0xe0 && d < 0xa0) + return 0; + } + else if (c < 0xf8) + { + char_len = 4; + if (c == 0xf0 && d < 0x90) + return 0; + } + else if (c < 0xfc) + { + char_len = 5; + if (c == 0xf8 && d < 0x88) + return 0; + } + else if (c < 0xfe) + { + char_len = 6; + if (c == 0xfc && d < 0x84) + return 0; + } + else + return 0; + + if (str_idx + char_len > input->len) + return 0; + + for (i = 1; i < char_len; ++i) + { + d = re_string_byte_at (input, str_idx + i); + if (d < 0x80 || d > 0xbf) + return 0; + } + return char_len; + } + + char_len = re_string_char_size_at (input, str_idx); + if (node->type == OP_PERIOD) + { + if (char_len <= 1) + return 0; + /* FIXME: I don't think this if is needed, as both '\n' + and '\0' are char_len == 1. */ + /* '.' accepts any one character except the following two cases. */ + if ((!(dfa->syntax & RE_DOT_NEWLINE) && + re_string_byte_at (input, str_idx) == '\n') || + ((dfa->syntax & RE_DOT_NOT_NULL) && + re_string_byte_at (input, str_idx) == '\0')) + return 0; + return char_len; + } + + elem_len = re_string_elem_size_at (input, str_idx); + if ((elem_len <= 1 && char_len <= 1) || char_len == 0) + return 0; + + if (node->type == COMPLEX_BRACKET) + { + const re_charset_t *cset = node->opr.mbcset; +# ifdef _LIBC + const unsigned char *pin + = ((const unsigned char *) re_string_get_buffer (input) + str_idx); + int j; + uint32_t nrules; +# endif /* _LIBC */ + int match_len = 0; + wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars) + ? re_string_wchar_at (input, str_idx) : 0); + + /* match with multibyte character? */ + for (i = 0; i < cset->nmbchars; ++i) + if (wc == cset->mbchars[i]) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + /* match with character_class? */ + for (i = 0; i < cset->nchar_classes; ++i) + { + wctype_t wt = cset->char_classes[i]; + if (__iswctype (wc, wt)) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + +# ifdef _LIBC + nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules != 0) + { + unsigned int in_collseq = 0; + const int32_t *table, *indirect; + const unsigned char *weights, *extra; + const char *collseqwc; + int32_t idx; + /* This #include defines a local function! */ +# include + + /* match with collating_symbol? */ + if (cset->ncoll_syms) + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + for (i = 0; i < cset->ncoll_syms; ++i) + { + const unsigned char *coll_sym = extra + cset->coll_syms[i]; + /* Compare the length of input collating element and + the length of current collating element. */ + if (*coll_sym != elem_len) + continue; + /* Compare each bytes. */ + for (j = 0; j < *coll_sym; j++) + if (pin[j] != coll_sym[1 + j]) + break; + if (j == *coll_sym) + { + /* Match if every bytes is equal. */ + match_len = j; + goto check_node_accept_bytes_match; + } + } + + if (cset->nranges) + { + if (elem_len <= char_len) + { + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + in_collseq = __collseq_table_lookup (collseqwc, wc); + } + else + in_collseq = find_collation_sequence_value (pin, elem_len); + } + /* match with range expression? */ + for (i = 0; i < cset->nranges; ++i) + if (cset->range_starts[i] <= in_collseq + && in_collseq <= cset->range_ends[i]) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + + /* match with equivalence_class? */ + if (cset->nequiv_classes) + { + const unsigned char *cp = pin; + table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); + idx = findidx (&cp); + if (idx > 0) + for (i = 0; i < cset->nequiv_classes; ++i) + { + int32_t equiv_class_idx = cset->equiv_classes[i]; + size_t weight_len = weights[idx]; + if (weight_len == weights[equiv_class_idx]) + { + int cnt = 0; + while (cnt <= weight_len + && (weights[equiv_class_idx + 1 + cnt] + == weights[idx + 1 + cnt])) + ++cnt; + if (cnt > weight_len) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + } + } + } + } + else +# endif /* _LIBC */ + { + /* match with range expression? */ +#if __GNUC__ >= 2 + wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'}; +#else + wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + cmp_buf[2] = wc; +#endif + for (i = 0; i < cset->nranges; ++i) + { + cmp_buf[0] = cset->range_starts[i]; + cmp_buf[4] = cset->range_ends[i]; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + } + check_node_accept_bytes_match: + if (!cset->non_match) + return match_len; + else + { + if (match_len > 0) + return 0; + else + return (elem_len > char_len) ? elem_len : char_len; + } + } + return 0; +} + +# ifdef _LIBC +static unsigned int +internal_function +find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len) +{ + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules == 0) + { + if (mbs_len == 1) + { + /* No valid character. Match it as a single byte character. */ + const unsigned char *collseq = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + return collseq[mbs[0]]; + } + return UINT_MAX; + } + else + { + int32_t idx; + const unsigned char *extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + int32_t extrasize = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra; + + for (idx = 0; idx < extrasize;) + { + int mbs_cnt, found = 0; + int32_t elem_mbs_len; + /* Skip the name of collating element name. */ + idx = idx + extra[idx] + 1; + elem_mbs_len = extra[idx++]; + if (mbs_len == elem_mbs_len) + { + for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt) + if (extra[idx + mbs_cnt] != mbs[mbs_cnt]) + break; + if (mbs_cnt == elem_mbs_len) + /* Found the entry. */ + found = 1; + } + /* Skip the byte sequence of the collating element. */ + idx += elem_mbs_len; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + /* Skip the wide char sequence of the collating element. */ + idx = idx + sizeof (uint32_t) * (extra[idx] + 1); + /* If we found the entry, return the sequence value. */ + if (found) + return *(uint32_t *) (extra + idx); + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + } + return UINT_MAX; + } +} +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ + +/* Check whether the node accepts the byte which is IDX-th + byte of the INPUT. */ + +static int +internal_function +check_node_accept (const re_match_context_t *mctx, const re_token_t *node, + int idx) +{ + unsigned char ch; + ch = re_string_byte_at (&mctx->input, idx); + switch (node->type) + { + case CHARACTER: + if (node->opr.c != ch) + return 0; + break; + + case SIMPLE_BRACKET: + if (!bitset_contain (node->opr.sbcset, ch)) + return 0; + break; + +#ifdef RE_ENABLE_I18N + case OP_UTF8_PERIOD: + if (ch >= 0x80) + return 0; + /* FALLTHROUGH */ +#endif + case OP_PERIOD: + if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE)) + || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL))) + return 0; + break; + + default: + return 0; + } + + if (node->constraint) + { + /* The node has constraints. Check whether the current context + satisfies the constraints. */ + unsigned int context = re_string_context_at (&mctx->input, idx, + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + return 0; + } + + return 1; +} + +/* Extend the buffers, if the buffers have run out. */ + +static reg_errcode_t +internal_function +extend_buffers (re_match_context_t *mctx) +{ + reg_errcode_t ret; + re_string_t *pstr = &mctx->input; + + /* Double the lengthes of the buffers. */ + ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + if (mctx->state_log != NULL) + { + /* And double the length of state_log. */ + /* XXX We have no indication of the size of this buffer. If this + allocation fail we have no indication that the state_log array + does not have the right size. */ + re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *, + pstr->bufs_len + 1); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->state_log = new_array; + } + + /* Then reconstruct the buffers. */ + if (pstr->icase) + { +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + } + } + return REG_NOERROR; +} + + +/* Functions for matching context. */ + +/* Initialize MCTX. */ + +static reg_errcode_t +internal_function +match_ctx_init (re_match_context_t *mctx, int eflags, int n) +{ + mctx->eflags = eflags; + mctx->match_last = -1; + if (n > 0) + { + mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n); + mctx->sub_tops = re_malloc (re_sub_match_top_t *, n); + if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0)) + return REG_ESPACE; + } + /* Already zero-ed by the caller. + else + mctx->bkref_ents = NULL; + mctx->nbkref_ents = 0; + mctx->nsub_tops = 0; */ + mctx->abkref_ents = n; + mctx->max_mb_elem_len = 1; + mctx->asub_tops = n; + return REG_NOERROR; +} + +/* Clean the entries which depend on the current input in MCTX. + This function must be invoked when the matcher changes the start index + of the input, or changes the input string. */ + +static void +internal_function +match_ctx_clean (re_match_context_t *mctx) +{ + int st_idx; + for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx) + { + int sl_idx; + re_sub_match_top_t *top = mctx->sub_tops[st_idx]; + for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx) + { + re_sub_match_last_t *last = top->lasts[sl_idx]; + re_free (last->path.array); + re_free (last); + } + re_free (top->lasts); + if (top->path) + { + re_free (top->path->array); + re_free (top->path); + } + free (top); + } + + mctx->nsub_tops = 0; + mctx->nbkref_ents = 0; +} + +/* Free all the memory associated with MCTX. */ + +static void +internal_function +match_ctx_free (re_match_context_t *mctx) +{ + /* First, free all the memory associated with MCTX->SUB_TOPS. */ + match_ctx_clean (mctx); + re_free (mctx->sub_tops); + re_free (mctx->bkref_ents); +} + +/* Add a new backreference entry to MCTX. + Note that we assume that caller never call this function with duplicate + entry, and call with STR_IDX which isn't smaller than any existing entry. +*/ + +static reg_errcode_t +internal_function +match_ctx_add_entry (re_match_context_t *mctx, int node, int str_idx, int from, + int to) +{ + if (mctx->nbkref_ents >= mctx->abkref_ents) + { + struct re_backref_cache_entry* new_entry; + new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry, + mctx->abkref_ents * 2); + if (BE (new_entry == NULL, 0)) + { + re_free (mctx->bkref_ents); + return REG_ESPACE; + } + mctx->bkref_ents = new_entry; + memset (mctx->bkref_ents + mctx->nbkref_ents, '\0', + sizeof (struct re_backref_cache_entry) * mctx->abkref_ents); + mctx->abkref_ents *= 2; + } + if (mctx->nbkref_ents > 0 + && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx) + mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1; + + mctx->bkref_ents[mctx->nbkref_ents].node = node; + mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx; + mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from; + mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to; + + /* This is a cache that saves negative results of check_dst_limits_calc_pos. + If bit N is clear, means that this entry won't epsilon-transition to + an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If + it is set, check_dst_limits_calc_pos_1 will recurse and try to find one + such node. + + A backreference does not epsilon-transition unless it is empty, so set + to all zeros if FROM != TO. */ + mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map + = (from == to ? ~0 : 0); + + mctx->bkref_ents[mctx->nbkref_ents++].more = 0; + if (mctx->max_mb_elem_len < to - from) + mctx->max_mb_elem_len = to - from; + return REG_NOERROR; +} + +/* Search for the first entry which has the same str_idx, or -1 if none is + found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */ + +static int +internal_function +search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx) +{ + int left, right, mid, last; + last = right = mctx->nbkref_ents; + for (left = 0; left < right;) + { + mid = (left + right) / 2; + if (mctx->bkref_ents[mid].str_idx < str_idx) + left = mid + 1; + else + right = mid; + } + if (left < last && mctx->bkref_ents[left].str_idx == str_idx) + return left; + else + return -1; +} + +/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches + at STR_IDX. */ + +static reg_errcode_t +internal_function +match_ctx_add_subtop (re_match_context_t *mctx, int node, int str_idx) +{ +#ifdef DEBUG + assert (mctx->sub_tops != NULL); + assert (mctx->asub_tops > 0); +#endif + if (BE (mctx->nsub_tops == mctx->asub_tops, 0)) + { + int new_asub_tops = mctx->asub_tops * 2; + re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops, + re_sub_match_top_t *, + new_asub_tops); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->sub_tops = new_array; + mctx->asub_tops = new_asub_tops; + } + mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t)); + if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0)) + return REG_ESPACE; + mctx->sub_tops[mctx->nsub_tops]->node = node; + mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx; + return REG_NOERROR; +} + +/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches + at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */ + +static re_sub_match_last_t * +internal_function +match_ctx_add_sublast (re_sub_match_top_t *subtop, int node, int str_idx) +{ + re_sub_match_last_t *new_entry; + if (BE (subtop->nlasts == subtop->alasts, 0)) + { + int new_alasts = 2 * subtop->alasts + 1; + re_sub_match_last_t **new_array = re_realloc (subtop->lasts, + re_sub_match_last_t *, + new_alasts); + if (BE (new_array == NULL, 0)) + return NULL; + subtop->lasts = new_array; + subtop->alasts = new_alasts; + } + new_entry = calloc (1, sizeof (re_sub_match_last_t)); + if (BE (new_entry != NULL, 1)) + { + subtop->lasts[subtop->nlasts] = new_entry; + new_entry->node = node; + new_entry->str_idx = str_idx; + ++subtop->nlasts; + } + return new_entry; +} + +static void +internal_function +sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, + re_dfastate_t **limited_sts, int last_node, int last_str_idx) +{ + sctx->sifted_states = sifted_sts; + sctx->limited_states = limited_sts; + sctx->last_node = last_node; + sctx->last_str_idx = last_str_idx; + re_node_set_init_empty (&sctx->limits); +} + + +/* Binary backward compatibility. */ +#if _LIBC +# include +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3) +link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.") +int re_max_failures = 2000; +# endif +#endif +#endif diff --git a/src/metis/GKlib/gkregex.h b/src/metis/GKlib/gkregex.h new file mode 100644 index 0000000..807c404 --- /dev/null +++ b/src/metis/GKlib/gkregex.h @@ -0,0 +1,556 @@ +/* Definitions for data structures and routines for the regular + expression library. + Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _REGEX_H +#define _REGEX_H 1 + +#include + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* The following two types have to be signed and unsigned integer type + wide enough to hold a value of a pointer. For most ANSI compilers + ptrdiff_t and size_t should be likely OK. Still size of these two + types is 2 for Microsoft C. Ugh... */ +typedef long int s_reg_t; +typedef unsigned long int active_reg_t; + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned long int reg_syntax_t; + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +#define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \ matches . + If not set, then \ is a back-reference. */ +#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* If this bit is set, succeed as soon as we match the whole pattern, + without further backtracking. */ +#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) + +/* If this bit is set, do not process the GNU regex operators. + If not set, then the GNU regex operators are recognized. */ +#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) + +/* If this bit is set, turn on internal regex debugging. + If not set, and debugging was on, turn it off. + This only works if regex.c is compiled -DDEBUG. + We define this bit always, so that all that's needed to turn on + debugging is to recompile regex.c; the calling code can always have + this bit set, and it won't affect anything in the normal case. */ +#define RE_DEBUG (RE_NO_GNU_OPS << 1) + +/* If this bit is set, a syntactically invalid interval is treated as + a string of ordinary characters. For example, the ERE 'a{1' is + treated as 'a\{1'. */ +#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1) + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1) + +/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only + for ^, because it is difficult to scan the regex backwards to find + whether ^ should be special. */ +#define RE_CARET_ANCHORS_HERE (RE_ICASE << 1) + +/* If this bit is set, then \{ cannot be first in an bre or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1) + +/* If this bit is set, then no_sub will be set to 1 during + re_compile_pattern. */ +#define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1) + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ +#define RE_SYNTAX_EMACS 0 + +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ + | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GNU_AWK \ + ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \ + & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS \ + | RE_CONTEXT_INVALID_OPS )) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ + | RE_INTERVALS | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \ + | RE_INVALID_INTERVAL_ORD) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is + removed and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +/* Maximum number of duplicates an interval can allow. Some systems + (erroneously) define this in other header files, but we want our + value, so remove any previous define. */ +#ifdef RE_DUP_MAX +# undef RE_DUP_MAX +#endif +/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */ +#define RE_DUP_MAX (0x7fff) + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (REG_EXTENDED << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (REG_ICASE << 1) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (REG_NEWLINE << 1) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + +/* Use PMATCH[0] to delimit the start and end of the search in the + buffer. */ +#define REG_STARTEND (1 << 2) + + +/* If any error codes are removed, changed, or added, update the + `re_error_msg' table in regex.c. */ +typedef enum +{ +#ifdef _XOPEN_SOURCE + REG_ENOSYS = -1, /* This will never happen for this implementation. */ +#endif + + REG_NOERROR = 0, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + REG_BADPAT, /* Invalid pattern. */ + REG_ECOLLATE, /* Inalid collating element. */ + REG_ECTYPE, /* Invalid character class name. */ + REG_EESCAPE, /* Trailing backslash. */ + REG_ESUBREG, /* Invalid back reference. */ + REG_EBRACK, /* Unmatched left bracket. */ + REG_EPAREN, /* Parenthesis imbalance. */ + REG_EBRACE, /* Unmatched \{. */ + REG_BADBR, /* Invalid contents of \{\}. */ + REG_ERANGE, /* Invalid range end. */ + REG_ESPACE, /* Ran out of memory. */ + REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + REG_EEND, /* Premature end. */ + REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +#ifndef RE_TRANSLATE_TYPE +# define RE_TRANSLATE_TYPE unsigned char * +#endif + +struct re_pattern_buffer +{ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are sometimes used as + array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long int allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long int used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses the + fastmap, if there is one, to skip over impossible starting points + for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation is + applied to a pattern when it is compiled and to a string when it + is matched. */ + RE_TRANSLATE_TYPE translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see whether or + not we should use the fastmap, so we don't set this absolutely + perfectly; see `re_compile_fastmap' (the `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the beginning + of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; +}; + +typedef struct re_pattern_buffer regex_t; + +/* Type for byte offsets within the string. POSIX mandates this. */ +typedef int regoff_t; + + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#ifndef RE_NREGS +# define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern (const char *__pattern, size_t __length, + struct re_pattern_buffer *__buffer); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap (struct re_pattern_buffer *__buffer); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern int re_search (struct re_pattern_buffer *__buffer, const char *__string, + int __length, int __start, int __range, + struct re_registers *__regs); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern int re_search_2 (struct re_pattern_buffer *__buffer, + const char *__string1, int __length1, + const char *__string2, int __length2, int __start, + int __range, struct re_registers *__regs, int __stop); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern int re_match (struct re_pattern_buffer *__buffer, const char *__string, + int __length, int __start, struct re_registers *__regs); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern int re_match_2 (struct re_pattern_buffer *__buffer, + const char *__string1, int __length1, + const char *__string2, int __length2, int __start, + struct re_registers *__regs, int __stop); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers (struct re_pattern_buffer *__buffer, + struct re_registers *__regs, + unsigned int __num_regs, + regoff_t *__starts, regoff_t *__ends); + +#if defined _REGEX_RE_COMP || defined _LIBC +# ifndef _CRAY +/* 4.2 bsd compatibility. */ +extern char *re_comp (const char *); +extern int re_exec (const char *); +# endif +#endif + +/* GCC 2.95 and later have "__restrict"; C99 compilers have + "restrict", and "configure" may have defined "restrict". */ +#ifndef __restrict +# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)) +# if defined restrict || 199901L <= __STDC_VERSION__ +# define __restrict restrict +# else +# define __restrict +# endif +# endif +#endif +/* gcc 3.1 and up support the [restrict] syntax. */ +#ifndef __restrict_arr +# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \ + && !defined __GNUG__ +# define __restrict_arr __restrict +# else +# define __restrict_arr +# endif +#endif + +/* POSIX compatibility. */ +extern int regcomp (regex_t *__restrict __preg, + const char *__restrict __pattern, + int __cflags); + +extern int regexec (const regex_t *__restrict __preg, + const char *__restrict __string, size_t __nmatch, + regmatch_t __pmatch[__restrict_arr], + int __eflags); + +extern size_t regerror (int __errcode, const regex_t *__restrict __preg, + char *__restrict __errbuf, size_t __errbuf_size); + +extern void regfree (regex_t *__preg); + + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* regex.h */ diff --git a/src/metis/GKlib/graph.c b/src/metis/GKlib/graph.c new file mode 100644 index 0000000..2095818 --- /dev/null +++ b/src/metis/GKlib/graph.c @@ -0,0 +1,1574 @@ +/*! + * \file + * + * \brief Various routines with dealing with sparse graphs + * + * \author George Karypis + * \version\verbatim $Id: graph.c 13328 2012-12-31 14:57:40Z karypis $ \endverbatim + */ + +#include + +#define OMPMINOPS 50000 + +/*************************************************************************/ +/*! Allocate memory for a graph and initializes it + \returns the allocated graph. The various fields are set to NULL. +*/ +/**************************************************************************/ +gk_graph_t *gk_graph_Create() +{ + gk_graph_t *graph; + + graph = (gk_graph_t *)gk_malloc(sizeof(gk_graph_t), "gk_graph_Create: graph"); + + gk_graph_Init(graph); + + return graph; +} + + +/*************************************************************************/ +/*! Initializes the graph. + \param graph is the graph to be initialized. +*/ +/*************************************************************************/ +void gk_graph_Init(gk_graph_t *graph) +{ + memset(graph, 0, sizeof(gk_graph_t)); + graph->nvtxs = -1; +} + + +/*************************************************************************/ +/*! Frees all the memory allocated for a graph. + \param graph is the graph to be freed. +*/ +/*************************************************************************/ +void gk_graph_Free(gk_graph_t **graph) +{ + if (*graph == NULL) + return; + gk_graph_FreeContents(*graph); + gk_free((void **)graph, LTERM); +} + + +/*************************************************************************/ +/*! Frees only the memory allocated for the graph's different fields and + sets them to NULL. + \param graph is the graph whose contents will be freed. +*/ +/*************************************************************************/ +void gk_graph_FreeContents(gk_graph_t *graph) +{ + gk_free((void *)&graph->xadj, &graph->adjncy, + &graph->iadjwgt, &graph->fadjwgt, + &graph->ivwgts, &graph->fvwgts, + &graph->ivsizes, &graph->fvsizes, + &graph->vlabels, + LTERM); +} + + +/**************************************************************************/ +/*! Reads a sparse graph from the supplied file + \param filename is the file that stores the data. + \param format is the graph format. The supported values are: + GK_GRAPH_FMT_METIS. + \param isfewgts is 1 if the edge-weights should be read as floats + \param isfvwgts is 1 if the vertex-weights should be read as floats + \param isfvsizes is 1 if the vertex-sizes should be read as floats + \returns the graph that was read. +*/ +/**************************************************************************/ +gk_graph_t *gk_graph_Read(char *filename, int format, int isfewgts, + int isfvwgts, int isfvsizes) +{ + ssize_t i, k, l; + size_t nfields, nvtxs, nedges, fmt, ncon, lnlen; + int32_t ival; + float fval; + int readsizes=0, readwgts=0, readvals=0, numbering=0; + char *line=NULL, *head, *tail, fmtstr[256]; + FILE *fpin=NULL; + gk_graph_t *graph=NULL; + + + if (!gk_fexists(filename)) + gk_errexit(SIGERR, "File %s does not exist!\n", filename); + + if (format == GK_GRAPH_FMT_METIS) { + fpin = gk_fopen(filename, "r", "gk_graph_Read: fpin"); + do { + if (gk_getline(&line, &lnlen, fpin) <= 0) + gk_errexit(SIGERR, "Premature end of input file: file:%s\n", filename); + } while (line[0] == '%'); + + fmt = ncon = 0; + nfields = sscanf(line, "%zu %zu %zu %zu", &nvtxs, &nedges, &fmt, &ncon); + if (nfields < 2) + gk_errexit(SIGERR, "Header line must contain at least 2 integers (#vtxs and #edges).\n"); + + nedges *= 2; + + if (fmt > 111) + gk_errexit(SIGERR, "Cannot read this type of file format [fmt=%zu]!\n", fmt); + + sprintf(fmtstr, "%03zu", fmt%1000); + readsizes = (fmtstr[0] == '1'); + readwgts = (fmtstr[1] == '1'); + readvals = (fmtstr[2] == '1'); + numbering = 1; + ncon = (ncon == 0 ? 1 : ncon); + } + else { + gk_errexit(SIGERR, "Unrecognized format: %d\n", format); + } + + graph = gk_graph_Create(); + + graph->nvtxs = nvtxs; + + graph->xadj = gk_zmalloc(nvtxs+1, "gk_graph_Read: xadj"); + graph->adjncy = gk_i32malloc(nedges, "gk_graph_Read: adjncy"); + if (readvals) { + if (isfewgts) + graph->fadjwgt = gk_fmalloc(nedges, "gk_graph_Read: fadjwgt"); + else + graph->iadjwgt = gk_i32malloc(nedges, "gk_graph_Read: iadjwgt"); + } + + if (readsizes) { + if (isfvsizes) + graph->fvsizes = gk_fmalloc(nvtxs, "gk_graph_Read: fvsizes"); + else + graph->ivsizes = gk_i32malloc(nvtxs, "gk_graph_Read: ivsizes"); + } + + if (readwgts) { + if (isfvwgts) + graph->fvwgts = gk_fmalloc(nvtxs*ncon, "gk_graph_Read: fvwgts"); + else + graph->ivwgts = gk_i32malloc(nvtxs*ncon, "gk_graph_Read: ivwgts"); + } + + + /*---------------------------------------------------------------------- + * Read the sparse graph file + *---------------------------------------------------------------------*/ + numbering = (numbering ? - 1 : 0); + for (graph->xadj[0]=0, k=0, i=0; ifvsizes[i] = (float)strtod(head, &tail); +#else + graph->fvsizes[i] = strtof(head, &tail); +#endif + if (tail == head) + gk_errexit(SIGERR, "The line for vertex %zd does not have size information\n", i+1); + if (graph->fvsizes[i] < 0) + gk_errexit(SIGERR, "The size for vertex %zd must be >= 0\n", i+1); + } + else { + graph->ivsizes[i] = strtol(head, &tail, 0); + if (tail == head) + gk_errexit(SIGERR, "The line for vertex %zd does not have size information\n", i+1); + if (graph->ivsizes[i] < 0) + gk_errexit(SIGERR, "The size for vertex %zd must be >= 0\n", i+1); + } + head = tail; + } + + /* Read vertex weights */ + if (readwgts) { + for (l=0; lfvwgts[i*ncon+l] = (float)strtod(head, &tail); +#else + graph->fvwgts[i*ncon+l] = strtof(head, &tail); +#endif + if (tail == head) + gk_errexit(SIGERR, "The line for vertex %zd does not have enough weights " + "for the %d constraints.\n", i+1, ncon); + if (graph->fvwgts[i*ncon+l] < 0) + gk_errexit(SIGERR, "The weight vertex %zd and constraint %zd must be >= 0\n", i+1, l); + } + else { + graph->ivwgts[i*ncon+l] = strtol(head, &tail, 0); + if (tail == head) + gk_errexit(SIGERR, "The line for vertex %zd does not have enough weights " + "for the %d constraints.\n", i+1, ncon); + if (graph->ivwgts[i*ncon+l] < 0) + gk_errexit(SIGERR, "The weight vertex %zd and constraint %zd must be >= 0\n", i+1, l); + } + head = tail; + } + } + + + /* Read the rest of the row */ + while (1) { + ival = (int)strtol(head, &tail, 0); + if (tail == head) + break; + head = tail; + + if ((graph->adjncy[k] = ival + numbering) < 0) + gk_errexit(SIGERR, "Error: Invalid column number %d at row %zd.\n", ival, i); + + if (readvals) { + if (isfewgts) { +#ifdef __MSC__ + fval = (float)strtod(head, &tail); +#else + fval = strtof(head, &tail); +#endif + if (tail == head) + gk_errexit(SIGERR, "Value could not be found for edge! Vertex:%zd, NNZ:%zd\n", i, k); + + graph->fadjwgt[k] = fval; + } + else { + ival = strtol(head, &tail, 0); + if (tail == head) + gk_errexit(SIGERR, "Value could not be found for edge! Vertex:%zd, NNZ:%zd\n", i, k); + + graph->iadjwgt[k] = ival; + } + head = tail; + } + k++; + } + graph->xadj[i+1] = k; + } + + if (k != nedges) + gk_errexit(SIGERR, "gk_graph_Read: Something wrong with the number of edges in " + "the input file. nedges=%zd, Actualnedges=%zd.\n", nedges, k); + + gk_fclose(fpin); + + gk_free((void **)&line, LTERM); + + return graph; +} + + +/**************************************************************************/ +/*! Writes a graph into a file. + \param graph is the graph to be written, + \param filename is the name of the output file. + \param format is one of GK_GRAPH_FMT_METIS specifying + the format of the output file. +*/ +/**************************************************************************/ +void gk_graph_Write(gk_graph_t *graph, char *filename, int format) +{ + ssize_t i, j; + int hasvwgts, hasvsizes, hasewgts; + FILE *fpout; + + if (format != GK_GRAPH_FMT_METIS) + gk_errexit(SIGERR, "Unknown file format. %d\n", format); + + if (filename) + fpout = gk_fopen(filename, "w", "gk_graph_Write: fpout"); + else + fpout = stdout; + + + hasewgts = (graph->iadjwgt || graph->fadjwgt); + hasvwgts = (graph->ivwgts || graph->fvwgts); + hasvsizes = (graph->ivsizes || graph->fvsizes); + + /* write the header line */ + fprintf(fpout, "%d %zd", graph->nvtxs, graph->xadj[graph->nvtxs]/2); + if (hasvwgts || hasvsizes || hasewgts) + fprintf(fpout, " %d%d%d", hasvsizes, hasvwgts, hasewgts); + fprintf(fpout, "\n"); + + + for (i=0; invtxs; i++) { + if (hasvsizes) { + if (graph->ivsizes) + fprintf(fpout, " %d", graph->ivsizes[i]); + else + fprintf(fpout, " %f", graph->fvsizes[i]); + } + + if (hasvwgts) { + if (graph->ivwgts) + fprintf(fpout, " %d", graph->ivwgts[i]); + else + fprintf(fpout, " %f", graph->fvwgts[i]); + } + + for (j=graph->xadj[i]; jxadj[i+1]; j++) { + fprintf(fpout, " %d", graph->adjncy[j]+1); + if (hasewgts) { + if (graph->iadjwgt) + fprintf(fpout, " %d", graph->iadjwgt[j]); + else + fprintf(fpout, " %f", graph->fadjwgt[j]); + } + } + fprintf(fpout, "\n"); + } + if (filename) + gk_fclose(fpout); +} + + +/*************************************************************************/ +/*! Returns a copy of a graph. + \param graph is the graph to be duplicated. + \returns the newly created copy of the graph. +*/ +/**************************************************************************/ +gk_graph_t *gk_graph_Dup(gk_graph_t *graph) +{ + gk_graph_t *ngraph; + + ngraph = gk_graph_Create(); + + ngraph->nvtxs = graph->nvtxs; + + /* copy the adjacency structure */ + if (graph->xadj) + ngraph->xadj = gk_zcopy(graph->nvtxs+1, graph->xadj, + gk_zmalloc(graph->nvtxs+1, "gk_graph_Dup: xadj")); + if (graph->ivwgts) + ngraph->ivwgts = gk_i32copy(graph->nvtxs, graph->ivwgts, + gk_i32malloc(graph->nvtxs, "gk_graph_Dup: ivwgts")); + if (graph->ivsizes) + ngraph->ivsizes = gk_i32copy(graph->nvtxs, graph->ivsizes, + gk_i32malloc(graph->nvtxs, "gk_graph_Dup: ivsizes")); + if (graph->vlabels) + ngraph->vlabels = gk_i32copy(graph->nvtxs, graph->vlabels, + gk_i32malloc(graph->nvtxs, "gk_graph_Dup: ivlabels")); + if (graph->fvwgts) + ngraph->fvwgts = gk_fcopy(graph->nvtxs, graph->fvwgts, + gk_fmalloc(graph->nvtxs, "gk_graph_Dup: fvwgts")); + if (graph->fvsizes) + ngraph->fvsizes = gk_fcopy(graph->nvtxs, graph->fvsizes, + gk_fmalloc(graph->nvtxs, "gk_graph_Dup: fvsizes")); + + + if (graph->adjncy) + ngraph->adjncy = gk_i32copy(graph->xadj[graph->nvtxs], graph->adjncy, + gk_i32malloc(graph->xadj[graph->nvtxs], "gk_graph_Dup: adjncy")); + if (graph->iadjwgt) + ngraph->iadjwgt = gk_i32copy(graph->xadj[graph->nvtxs], graph->iadjwgt, + gk_i32malloc(graph->xadj[graph->nvtxs], "gk_graph_Dup: iadjwgt")); + if (graph->fadjwgt) + ngraph->fadjwgt = gk_fcopy(graph->xadj[graph->nvtxs], graph->fadjwgt, + gk_fmalloc(graph->xadj[graph->nvtxs], "gk_graph_Dup: fadjwgt")); + + return ngraph; +} + + +/*************************************************************************/ +/*! Returns a subgraph containing a set of consecutive vertices. + \param graph is the original graph. + \param vstart is the starting vertex. + \param nvtxs is the number of vertices from vstart to extract. + \returns the newly created subgraph. +*/ +/**************************************************************************/ +gk_graph_t *gk_graph_ExtractSubgraph(gk_graph_t *graph, int vstart, int nvtxs) +{ + ssize_t i; + gk_graph_t *ngraph; + + if (vstart+nvtxs > graph->nvtxs) + return NULL; + + ngraph = gk_graph_Create(); + + ngraph->nvtxs = nvtxs; + + /* copy the adjancy structure */ + if (graph->xadj) + ngraph->xadj = gk_zcopy(nvtxs+1, graph->xadj+vstart, + gk_zmalloc(nvtxs+1, "gk_graph_ExtractSubgraph: xadj")); + for (i=nvtxs; i>=0; i--) + ngraph->xadj[i] -= ngraph->xadj[0]; + ASSERT(ngraph->xadj[0] == 0); + + if (graph->ivwgts) + ngraph->ivwgts = gk_i32copy(nvtxs, graph->ivwgts+vstart, + gk_i32malloc(nvtxs, "gk_graph_ExtractSubgraph: ivwgts")); + if (graph->ivsizes) + ngraph->ivsizes = gk_i32copy(nvtxs, graph->ivsizes+vstart, + gk_i32malloc(nvtxs, "gk_graph_ExtractSubgraph: ivsizes")); + if (graph->vlabels) + ngraph->vlabels = gk_i32copy(nvtxs, graph->vlabels+vstart, + gk_i32malloc(nvtxs, "gk_graph_ExtractSubgraph: vlabels")); + + if (graph->fvwgts) + ngraph->fvwgts = gk_fcopy(nvtxs, graph->fvwgts+vstart, + gk_fmalloc(nvtxs, "gk_graph_ExtractSubgraph: fvwgts")); + if (graph->fvsizes) + ngraph->fvsizes = gk_fcopy(nvtxs, graph->fvsizes+vstart, + gk_fmalloc(nvtxs, "gk_graph_ExtractSubgraph: fvsizes")); + + + ASSERT(ngraph->xadj[nvtxs] == graph->xadj[vstart+nvtxs]-graph->xadj[vstart]); + if (graph->adjncy) + ngraph->adjncy = gk_i32copy(graph->xadj[vstart+nvtxs]-graph->xadj[vstart], + graph->adjncy+graph->xadj[vstart], + gk_i32malloc(graph->xadj[vstart+nvtxs]-graph->xadj[vstart], + "gk_graph_ExtractSubgraph: adjncy")); + if (graph->iadjwgt) + ngraph->iadjwgt = gk_i32copy(graph->xadj[vstart+nvtxs]-graph->xadj[vstart], + graph->iadjwgt+graph->xadj[vstart], + gk_i32malloc(graph->xadj[vstart+nvtxs]-graph->xadj[vstart], + "gk_graph_ExtractSubgraph: iadjwgt")); + if (graph->fadjwgt) + ngraph->fadjwgt = gk_fcopy(graph->xadj[vstart+nvtxs]-graph->xadj[vstart], + graph->fadjwgt+graph->xadj[vstart], + gk_fmalloc(graph->xadj[vstart+nvtxs]-graph->xadj[vstart], + "gk_graph_ExtractSubgraph: fadjwgt")); + + return ngraph; +} + + +/*************************************************************************/ +/*! Returns a graph that has been reordered according to the permutation. + \param[IN] graph is the graph to be re-ordered. + \param[IN] perm is the new ordering of the graph's vertices + \param[IN] iperm is the original ordering of the re-ordered graph's vertices + \returns the newly created copy of the graph. + + \note Either perm or iperm can be NULL but not both. +*/ +/**************************************************************************/ +gk_graph_t *gk_graph_Reorder(gk_graph_t *graph, int32_t *perm, int32_t *iperm) +{ + ssize_t j, jj, *xadj; + int i, k, u, v, nvtxs; + int freeperm=0, freeiperm=0; + int32_t *adjncy; + gk_graph_t *ngraph; + + if (perm == NULL && iperm == NULL) + return NULL; + + ngraph = gk_graph_Create(); + + ngraph->nvtxs = nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + /* allocate memory for the different structures that are present in graph */ + if (graph->xadj) + ngraph->xadj = gk_zmalloc(nvtxs+1, "gk_graph_Reorder: xadj"); + + if (graph->ivwgts) + ngraph->ivwgts = gk_i32malloc(nvtxs, "gk_graph_Reorder: ivwgts"); + + if (graph->ivsizes) + ngraph->ivsizes = gk_i32malloc(nvtxs, "gk_graph_Reorder: ivsizes"); + + if (graph->vlabels) + ngraph->vlabels = gk_i32malloc(nvtxs, "gk_graph_Reorder: ivlabels"); + + if (graph->fvwgts) + ngraph->fvwgts = gk_fmalloc(nvtxs, "gk_graph_Reorder: fvwgts"); + + if (graph->fvsizes) + ngraph->fvsizes = gk_fmalloc(nvtxs, "gk_graph_Reorder: fvsizes"); + + + if (graph->adjncy) + ngraph->adjncy = gk_i32malloc(graph->xadj[nvtxs], "gk_graph_Reorder: adjncy"); + + if (graph->iadjwgt) + ngraph->iadjwgt = gk_i32malloc(graph->xadj[nvtxs], "gk_graph_Reorder: iadjwgt"); + + if (graph->fadjwgt) + ngraph->fadjwgt = gk_fmalloc(graph->xadj[nvtxs], "gk_graph_Reorder: fadjwgt"); + + + /* create perm/iperm if not provided */ + if (perm == NULL) { + freeperm = 1; + perm = gk_i32malloc(nvtxs, "gk_graph_Reorder: perm"); + for (i=0; ixadj[0] = jj = 0; + for (v=0; vadjncy[jj] = perm[adjncy[j]]; + if (graph->iadjwgt) + ngraph->iadjwgt[jj] = graph->iadjwgt[j]; + if (graph->fadjwgt) + ngraph->fadjwgt[jj] = graph->fadjwgt[j]; + } + if (graph->ivwgts) + ngraph->ivwgts[v] = graph->ivwgts[u]; + if (graph->fvwgts) + ngraph->fvwgts[v] = graph->fvwgts[u]; + if (graph->ivsizes) + ngraph->ivsizes[v] = graph->ivsizes[u]; + if (graph->fvsizes) + ngraph->fvsizes[v] = graph->fvsizes[u]; + if (graph->vlabels) + ngraph->vlabels[v] = graph->vlabels[u]; + + ngraph->xadj[v+1] = jj; + } + + + /* free memory */ + if (freeperm) + gk_free((void **)&perm, LTERM); + if (freeiperm) + gk_free((void **)&iperm, LTERM); + + return ngraph; +} + + +/*************************************************************************/ +/*! This function finds the connected components in a graph. + + \param graph is the graph structure + \param cptr is the ptr structure of the CSR representation of the + components. The length of this vector must be graph->nvtxs+1. + \param cind is the indices structure of the CSR representation of + the components. The length of this vector must be graph->nvtxs. + + \returns the number of components that it found. + + \note The cptr and cind parameters can be NULL, in which case only the + number of connected components is returned. +*/ +/*************************************************************************/ +int gk_graph_FindComponents(gk_graph_t *graph, int32_t *cptr, int32_t *cind) +{ + ssize_t i, ii, j, jj, k, nvtxs, first, last, ntodo, ncmps; + ssize_t *xadj; + int32_t *adjncy, *pos, *todo; + int32_t mustfree_ccsr=0, mustfree_where=0; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + /* Deal with NULL supplied cptr/cind vectors */ + if (cptr == NULL) { + cptr = gk_i32malloc(nvtxs+1, "gk_graph_FindComponents: cptr"); + cind = gk_i32malloc(nvtxs, "gk_graph_FindComponents: cind"); + mustfree_ccsr = 1; + } + + /* The list of vertices that have not been touched yet. + The valid entries are from [0..ntodo). */ + todo = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_FindComponents: todo")); + + /* For a vertex that has not been visited, pos[i] is the position in the + todo list that this vertex is stored. + If a vertex has been visited, pos[i] = -1. */ + pos = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_FindComponents: pos")); + + + /* Find the connected componends */ + ncmps = -1; + ntodo = nvtxs; /* All vertices have not been visited */ + first = last = 0; /* Point to the first and last vertices that have been touched + but not explored. + These vertices are stored in cind[first]...cind[last-1]. */ + while (ntodo > 0) { + if (first == last) { /* Find another starting vertex */ + cptr[++ncmps] = first; /* Mark the end of the current CC */ + + ASSERT(pos[todo[0]] != -1); + i = todo[0]; + + cind[last++] = i; + pos[i] = -1; + } + + i = cind[first++]; /* Get the first visited but unexplored vertex */ + + /* Remove i from the todo list and put the last item in the todo + list at the position that i was so that the todo list will be + consequtive. The pos[] array is updated accordingly to keep track + the location of the vertices in the todo[] list. */ + k = pos[i]; + j = todo[k] = todo[--ntodo]; + pos[j] = k; + + for (j=xadj[i]; jnvtxs <= 0) + return; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + /* This array will function like pos + touched of the CC method */ + pos = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_ComputeBFSOrdering: pos")); + + /* This array ([C]losed[O]pen[T]odo => cot) serves three purposes. + Positions from [0...first) is the current iperm[] vector of the explored vertices; + Positions from [first...last) is the OPEN list (i.e., visited vertices); + Positions from [last...nvtxs) is the todo list. */ + cot = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_ComputeBFSOrdering: cot")); + + + /* put v at the front of the todo list */ + pos[0] = cot[0] = v; + pos[v] = cot[v] = 0; + + /* Find the connected componends induced by the partition */ + first = last = 0; + while (first < nvtxs) { + if (first == last) { /* Find another starting vertex */ + k = cot[last]; + ASSERT(pos[k] != -1); + pos[k] = -1; /* mark node as being visited */ + last++; + } + + i = cot[first++]; /* the ++ advances the explored vertices */ + for (j=xadj[i]; jnvtxs <= 0) + return; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + /* the degree of the vertices in the closed list */ + degrees = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: degrees"); + + /* the minimum vertex ID of an open vertex to the closed list */ + minIDs = gk_i32smalloc(nvtxs, nvtxs+1, "gk_graph_ComputeBestFOrdering: minIDs"); + + /* the open list */ + open = gk_i32malloc(nvtxs, "gk_graph_ComputeBestFOrdering: open"); + + /* if perm[i] >= 0, then perm[i] is the order of vertex i; + otherwise perm[i] == -1. + */ + perm = gk_i32smalloc(nvtxs, -1, "gk_graph_ComputeBestFOrdering: perm"); + + /* create the queue and put everything in it */ + queue = gk_i32pqCreate(nvtxs); + for (i=0; invtxs <= 0) + return; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + /* the degree of the vertices in the closed list */ + degrees = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: degrees"); + + /* the weighted degree of the vertices in the closed list for type==3 */ + wdegrees = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: wdegrees"); + + /* the sum of differences for type==4 */ + sod = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: sod"); + + /* the encountering level of a vertex type==5 */ + level = gk_i32smalloc(nvtxs, 0, "gk_graph_ComputeBestFOrdering: level"); + + /* The open+todo list of vertices. + The vertices from [0..nopen] are the open vertices. + The vertices from [nopen..ntodo) are the todo vertices. + */ + ot = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_FindComponents: ot")); + + /* For a vertex that has not been explored, pos[i] is the position in the ot list. */ + pos = gk_i32incset(nvtxs, 0, gk_i32malloc(nvtxs, "gk_graph_FindComponents: pos")); + + /* if perm[i] >= 0, then perm[i] is the order of vertex i; otherwise perm[i] == -1. */ + perm = gk_i32smalloc(nvtxs, -1, "gk_graph_ComputeBestFOrdering: perm"); + + /* create the queue and put the starting vertex in it */ + queue = gk_i32pqCreate(nvtxs); + gk_i32pqInsert(queue, v, 1); + + /* put v at the front of the open list */ + pos[0] = ot[0] = v; + pos[v] = ot[v] = 0; + nopen = 1; + ntodo = nvtxs; + + /* start processing the nodes */ + for (i=0; i= nopen) + gk_errexit(SIGERR, "The position of v is not in open list. pos[%d]=%d is >=%d.\n", v, pos[v], nopen); + + /* remove v from the open list and re-arrange the todo part of the list */ + ot[pos[v]] = ot[nopen-1]; + pos[ot[nopen-1]] = pos[v]; + if (ntodo > nopen) { + ot[nopen-1] = ot[ntodo-1]; + pos[ot[ntodo-1]] = nopen-1; + } + nopen--; + ntodo--; + + for (j=xadj[v]; jnvtxs <= 0) + return; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + inqueue = gk_i32smalloc(nvtxs, 0, "gk_graph_SingleSourceShortestPaths: inqueue"); + + /* determine if you will be computing using int32_t or float and proceed from there */ + if (graph->iadjwgt != NULL) { + gk_i32pq_t *queue; + int32_t *adjwgt; + int32_t *sps; + + adjwgt = graph->iadjwgt; + + queue = gk_i32pqCreate(nvtxs); + gk_i32pqInsert(queue, v, 0); + inqueue[v] = 1; + + sps = gk_i32smalloc(nvtxs, -1, "gk_graph_SingleSourceShortestPaths: sps"); + sps[v] = 0; + + /* start processing the nodes */ + while ((v = gk_i32pqGetTop(queue)) != -1) { + inqueue[v] = 2; + + /* relax the adjacent edges */ + for (i=xadj[v]; ifadjwgt; + + queue = gk_fpqCreate(nvtxs); + gk_fpqInsert(queue, v, 0); + inqueue[v] = 1; + + sps = gk_fsmalloc(nvtxs, -1, "gk_graph_SingleSourceShortestPaths: sps"); + sps[v] = 0; + + /* start processing the nodes */ + while ((v = gk_fpqGetTop(queue)) != -1) { + inqueue[v] = 2; + + /* relax the adjacent edges */ + for (i=xadj[v]; irowptr) + gk_errexit(SIGERR, "Row-based view of the graphrix does not exists.\n"); + + n = graph->nrows; + ptr = graph->rowptr; + ind = graph->rowind; + val = graph->rowval; + break; + + case GK_CSR_COL: + if (!graph->colptr) + gk_errexit(SIGERR, "Column-based view of the graphrix does not exists.\n"); + + n = graph->ncols; + ptr = graph->colptr; + ind = graph->colind; + val = graph->colval; + break; + + default: + gk_errexit(SIGERR, "Invalid index type of %d.\n", what); + return; + } + + #pragma omp parallel if (n > 100) + { + ssize_t i, j, k; + gk_ikv_t *cand; + float *tval; + + #pragma omp single + for (i=0; i ptr[i] && ind[j] < ind[j-1]) + k = 1; /* an inversion */ + cand[j-ptr[i]].val = j-ptr[i]; + cand[j-ptr[i]].key = ind[j]; + tval[j-ptr[i]] = val[j]; + } + if (k) { + gk_ikvsorti(ptr[i+1]-ptr[i], cand); + for (j=ptr[i]; jnrows = nrows; + ngraph->ncols = graph->ncols; + + for (nnz=0, i=0; irowptr[rind[i]+1]-graph->rowptr[rind[i]]; + + ngraph->rowptr = gk_zmalloc(ngraph->nrows+1, "gk_graph_ExtractPartition: rowptr"); + ngraph->rowind = gk_imalloc(nnz, "gk_graph_ExtractPartition: rowind"); + ngraph->rowval = gk_fmalloc(nnz, "gk_graph_ExtractPartition: rowval"); + + ngraph->rowptr[0] = 0; + for (nnz=0, j=0, ii=0; iirowptr[i+1]-graph->rowptr[i], graph->rowind+graph->rowptr[i], ngraph->rowind+nnz); + gk_fcopy(graph->rowptr[i+1]-graph->rowptr[i], graph->rowval+graph->rowptr[i], ngraph->rowval+nnz); + nnz += graph->rowptr[i+1]-graph->rowptr[i]; + ngraph->rowptr[++j] = nnz; + } + ASSERT(j == ngraph->nrows); + + return ngraph; +} + + +/*************************************************************************/ +/*! Returns a subgraphrix corresponding to a specified partitioning of rows. + \param graph is the original graphrix. + \param part is the partitioning vector of the rows. + \param pid is the partition ID that will be extracted. + \returns the row structure of the newly created subgraphrix. +*/ +/**************************************************************************/ +gk_graph_t *gk_graph_ExtractPartition(gk_graph_t *graph, int *part, int pid) +{ + ssize_t i, j, nnz; + gk_graph_t *ngraph; + + ngraph = gk_graph_Create(); + + ngraph->nrows = 0; + ngraph->ncols = graph->ncols; + + for (nnz=0, i=0; inrows; i++) { + if (part[i] == pid) { + ngraph->nrows++; + nnz += graph->rowptr[i+1]-graph->rowptr[i]; + } + } + + ngraph->rowptr = gk_zmalloc(ngraph->nrows+1, "gk_graph_ExtractPartition: rowptr"); + ngraph->rowind = gk_imalloc(nnz, "gk_graph_ExtractPartition: rowind"); + ngraph->rowval = gk_fmalloc(nnz, "gk_graph_ExtractPartition: rowval"); + + ngraph->rowptr[0] = 0; + for (nnz=0, j=0, i=0; inrows; i++) { + if (part[i] == pid) { + gk_icopy(graph->rowptr[i+1]-graph->rowptr[i], graph->rowind+graph->rowptr[i], ngraph->rowind+nnz); + gk_fcopy(graph->rowptr[i+1]-graph->rowptr[i], graph->rowval+graph->rowptr[i], ngraph->rowval+nnz); + nnz += graph->rowptr[i+1]-graph->rowptr[i]; + ngraph->rowptr[++j] = nnz; + } + } + ASSERT(j == ngraph->nrows); + + return ngraph; +} + + +/*************************************************************************/ +/*! Splits the graphrix into multiple sub-graphrices based on the provided + color array. + \param graph is the original graphrix. + \param color is an array of size equal to the number of non-zeros + in the graphrix (row-wise structure). The graphrix is split into + as many parts as the number of colors. For meaningfull results, + the colors should be numbered consecutively starting from 0. + \returns an array of graphrices for each supplied color number. +*/ +/**************************************************************************/ +gk_graph_t **gk_graph_Split(gk_graph_t *graph, int *color) +{ + ssize_t i, j; + int nrows, ncolors; + ssize_t *rowptr; + int *rowind; + float *rowval; + gk_graph_t **sgraphs; + + nrows = graph->nrows; + rowptr = graph->rowptr; + rowind = graph->rowind; + rowval = graph->rowval; + + ncolors = gk_imax(rowptr[nrows], color)+1; + + sgraphs = (gk_graph_t **)gk_malloc(sizeof(gk_graph_t *)*ncolors, "gk_graph_Split: sgraphs"); + for (i=0; inrows = graph->nrows; + sgraphs[i]->ncols = graph->ncols; + sgraphs[i]->rowptr = gk_zsmalloc(nrows+1, 0, "gk_graph_Split: sgraphs[i]->rowptr"); + } + + for (i=0; irowptr[i]++; + } + for (i=0; irowptr); + + for (i=0; irowind = gk_imalloc(sgraphs[i]->rowptr[nrows], "gk_graph_Split: sgraphs[i]->rowind"); + sgraphs[i]->rowval = gk_fmalloc(sgraphs[i]->rowptr[nrows], "gk_graph_Split: sgraphs[i]->rowval"); + } + + for (i=0; irowind[sgraphs[color[j]]->rowptr[i]] = rowind[j]; + sgraphs[color[j]]->rowval[sgraphs[color[j]]->rowptr[i]] = rowval[j]; + sgraphs[color[j]]->rowptr[i]++; + } + } + + for (i=0; irowptr); + + return sgraphs; +} + + +/*************************************************************************/ +/*! Prunes certain rows/columns of the graphrix. The prunning takes place + by analyzing the row structure of the graphrix. The prunning takes place + by removing rows/columns but it does not affect the numbering of the + remaining rows/columns. + + \param graph the graphrix to be prunned, + \param what indicates if the rows (GK_CSR_ROW) or the columns (GK_CSR_COL) + of the graphrix will be prunned, + \param minf is the minimum number of rows (columns) that a column (row) must + be present in order to be kept, + \param maxf is the maximum number of rows (columns) that a column (row) must + be present at in order to be kept. + \returns the prunned graphrix consisting only of its row-based structure. + The input graphrix is not modified. +*/ +/**************************************************************************/ +gk_graph_t *gk_graph_Prune(gk_graph_t *graph, int what, int minf, int maxf) +{ + ssize_t i, j, nnz; + int nrows, ncols; + ssize_t *rowptr, *nrowptr; + int *rowind, *nrowind, *collen; + float *rowval, *nrowval; + gk_graph_t *ngraph; + + ngraph = gk_graph_Create(); + + nrows = ngraph->nrows = graph->nrows; + ncols = ngraph->ncols = graph->ncols; + + rowptr = graph->rowptr; + rowind = graph->rowind; + rowval = graph->rowval; + + nrowptr = ngraph->rowptr = gk_zmalloc(nrows+1, "gk_graph_Prune: nrowptr"); + nrowind = ngraph->rowind = gk_imalloc(rowptr[nrows], "gk_graph_Prune: nrowind"); + nrowval = ngraph->rowval = gk_fmalloc(rowptr[nrows], "gk_graph_Prune: nrowval"); + + + switch (what) { + case GK_CSR_COL: + collen = gk_ismalloc(ncols, 0, "gk_graph_Prune: collen"); + + for (i=0; i= minf && collen[i] <= maxf ? 1 : 0); + + nrowptr[0] = 0; + for (nnz=0, i=0; i= minf && rowptr[i+1]-rowptr[i] <= maxf) { + for (j=rowptr[i]; jrowval) { + n = graph->nrows; + ptr = graph->rowptr; + val = graph->rowval; + + #pragma omp parallel if (ptr[n] > OMPMINOPS) + { + #pragma omp for private(j,sum) schedule(static) + for (i=0; i 0 */ + } + if (sum > 0) { + if (norm == 2) + sum=1.0/sqrt(sum); + else if (norm == 1) + sum=1.0/sum; + for (j=ptr[i]; jcolval) { + n = graph->ncols; + ptr = graph->colptr; + val = graph->colval; + + #pragma omp parallel if (ptr[n] > OMPMINOPS) + { + #pragma omp for private(j,sum) schedule(static) + for (i=0; i 0) { + if (norm == 2) + sum=1.0/sqrt(sum); + else if (norm == 1) + sum=1.0/sum; + for (j=ptr[i]; j + +/****************************************************************************** +* This function creates the hash-table +*******************************************************************************/ +gk_HTable_t *HTable_Create(int nelements) +{ + gk_HTable_t *htable; + + htable = gk_malloc(sizeof(gk_HTable_t), "HTable_Create: htable"); + htable->harray = gk_ikvmalloc(nelements, "HTable_Create: harray"); + htable->nelements = nelements; + + HTable_Reset(htable); + + return htable; +} + + +/****************************************************************************** +* This function resets the data-structures associated with the hash-table +*******************************************************************************/ +void HTable_Reset(gk_HTable_t *htable) +{ + int i; + + for (i=0; inelements; i++) + htable->harray[i].key = HTABLE_EMPTY; + htable->htsize = 0; + +} + +/****************************************************************************** +* This function resizes the hash-table +*******************************************************************************/ +void HTable_Resize(gk_HTable_t *htable, int nelements) +{ + int i, old_nelements; + gk_ikv_t *old_harray; + + old_nelements = htable->nelements; + old_harray = htable->harray; + + /* prepare larger hash */ + htable->nelements = nelements; + htable->htsize = 0; + htable->harray = gk_ikvmalloc(nelements, "HTable_Resize: harray"); + for (i=0; iharray[i].key = HTABLE_EMPTY; + + /* reassign the values */ + for (i=0; ihtsize > htable->nelements/2) + HTable_Resize(htable, 2*htable->nelements); + + first = HTable_HFunction(htable->nelements, key); + + for (i=first; inelements; i++) { + if (htable->harray[i].key == HTABLE_EMPTY || htable->harray[i].key == HTABLE_DELETED) { + htable->harray[i].key = key; + htable->harray[i].val = val; + htable->htsize++; + return; + } + } + + for (i=0; iharray[i].key == HTABLE_EMPTY || htable->harray[i].key == HTABLE_DELETED) { + htable->harray[i].key = key; + htable->harray[i].val = val; + htable->htsize++; + return; + } + } + +} + + +/****************************************************************************** +* This function deletes key from the htable +*******************************************************************************/ +void HTable_Delete(gk_HTable_t *htable, int key) +{ + int i, first; + + first = HTable_HFunction(htable->nelements, key); + + for (i=first; inelements; i++) { + if (htable->harray[i].key == key) { + htable->harray[i].key = HTABLE_DELETED; + htable->htsize--; + return; + } + } + + for (i=0; iharray[i].key == key) { + htable->harray[i].key = HTABLE_DELETED; + htable->htsize--; + return; + } + } + +} + + +/****************************************************************************** +* This function returns the data associated with the key in the hastable +*******************************************************************************/ +int HTable_Search(gk_HTable_t *htable, int key) +{ + int i, first; + + first = HTable_HFunction(htable->nelements, key); + + for (i=first; inelements; i++) { + if (htable->harray[i].key == key) + return htable->harray[i].val; + else if (htable->harray[i].key == HTABLE_EMPTY) + return -1; + } + + for (i=0; iharray[i].key == key) + return htable->harray[i].val; + else if (htable->harray[i].key == HTABLE_EMPTY) + return -1; + } + + return -1; +} + + +/****************************************************************************** +* This function returns the next key/val +*******************************************************************************/ +int HTable_GetNext(gk_HTable_t *htable, int key, int *r_val, int type) +{ + int i; + static int first, last; + + if (type == HTABLE_FIRST) + first = last = HTable_HFunction(htable->nelements, key); + + if (first > last) { + for (i=first; inelements; i++) { + if (htable->harray[i].key == key) { + *r_val = htable->harray[i].val; + first = i+1; + return 1; + } + else if (htable->harray[i].key == HTABLE_EMPTY) + return -1; + } + first = 0; + } + + for (i=first; iharray[i].key == key) { + *r_val = htable->harray[i].val; + first = i+1; + return 1; + } + else if (htable->harray[i].key == HTABLE_EMPTY) + return -1; + } + + return -1; +} + + +/****************************************************************************** +* This function returns the data associated with the key in the hastable +*******************************************************************************/ +int HTable_SearchAndDelete(gk_HTable_t *htable, int key) +{ + int i, first; + + first = HTable_HFunction(htable->nelements, key); + + for (i=first; inelements; i++) { + if (htable->harray[i].key == key) { + htable->harray[i].key = HTABLE_DELETED; + htable->htsize--; + return htable->harray[i].val; + } + else if (htable->harray[i].key == HTABLE_EMPTY) + gk_errexit(SIGERR, "HTable_SearchAndDelete: Failed to find the key!\n"); + } + + for (i=0; iharray[i].key == key) { + htable->harray[i].key = HTABLE_DELETED; + htable->htsize--; + return htable->harray[i].val; + } + else if (htable->harray[i].key == HTABLE_EMPTY) + gk_errexit(SIGERR, "HTable_SearchAndDelete: Failed to find the key!\n"); + } + + return -1; + +} + + + +/****************************************************************************** +* This function destroys the data structures associated with the hash-table +*******************************************************************************/ +void HTable_Destroy(gk_HTable_t *htable) +{ + gk_free((void **)&htable->harray, &htable, LTERM); +} + + +/****************************************************************************** +* This is the hash-function. Based on multiplication +*******************************************************************************/ +int HTable_HFunction(int nelements, int key) +{ + return (int)(key%nelements); +} diff --git a/src/metis/GKlib/io.c b/src/metis/GKlib/io.c new file mode 100644 index 0000000..caaedcb --- /dev/null +++ b/src/metis/GKlib/io.c @@ -0,0 +1,384 @@ +/*! +\file io.c +\brief Various file I/O functions. + +This file contains various functions that perform I/O. + +\date Started 4/10/95 +\author George +\version\verbatim $Id: io.c 12591 2012-09-01 19:03:15Z karypis $ \endverbatim +*/ + +#ifdef HAVE_GETLINE +/* Get getline to be defined. */ +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#endif + +#include + +/************************************************************************* +* This function opens a file +**************************************************************************/ +FILE *gk_fopen(char *fname, char *mode, const char *msg) +{ + FILE *fp; + char errmsg[8192]; + + fp = fopen(fname, mode); + if (fp != NULL) + return fp; + + sprintf(errmsg,"file: %s, mode: %s, [%s]", fname, mode, msg); + perror(errmsg); + errexit("Failed on gk_fopen()\n"); + + return NULL; +} + + +/************************************************************************* +* This function closes a file +**************************************************************************/ +void gk_fclose(FILE *fp) +{ + fclose(fp); +} + + +/*************************************************************************/ +/*! This function is the GKlib implementation of glibc's getline() + function. + \returns -1 if the EOF has been reached, otherwise it returns the + number of bytes read. +*/ +/*************************************************************************/ +gk_idx_t gk_getline(char **lineptr, size_t *n, FILE *stream) +{ +#ifdef HAVE_GETLINE + return getline(lineptr, n, stream); +#else + size_t i; + int ch; + + if (feof(stream)) + return -1; + + /* Initial memory allocation if *lineptr is NULL */ + if (*lineptr == NULL || *n == 0) { + *n = 1024; + *lineptr = gk_malloc((*n)*sizeof(char), "gk_getline: lineptr"); + } + + /* get into the main loop */ + i = 0; + while ((ch = getc(stream)) != EOF) { + (*lineptr)[i++] = (char)ch; + + /* reallocate memory if reached at the end of the buffer. The +1 is for '\0' */ + if (i+1 == *n) { + *n = 2*(*n); + *lineptr = gk_realloc(*lineptr, (*n)*sizeof(char), "gk_getline: lineptr"); + } + + if (ch == '\n') + break; + } + (*lineptr)[i] = '\0'; + + return (i == 0 ? -1 : i); +#endif +} + + +/*************************************************************************/ +/*! This function reads the contents of a text file and returns it in the + form of an array of strings. + \param fname is the name of the file + \param r_nlines is the number of lines in the file. If it is NULL, + this information is not returned. +*/ +/*************************************************************************/ +char **gk_readfile(char *fname, gk_idx_t *r_nlines) +{ + size_t lnlen, nlines; + char *line=NULL, **lines=NULL; + FILE *fpin; + + gk_getfilestats(fname, &nlines, NULL, NULL, NULL); + if (nlines > 0) { + lines = (char **)gk_malloc(nlines*sizeof(char *), "gk_readfile: lines"); + + fpin = gk_fopen(fname, "r", "gk_readfile"); + nlines = 0; + while (gk_getline(&line, &lnlen, fpin) != -1) { + gk_strtprune(line, "\n\r"); + lines[nlines++] = gk_strdup(line); + } + gk_fclose(fpin); + } + + gk_free((void **)&line, LTERM); + + if (r_nlines != NULL) + *r_nlines = nlines; + + return lines; +} + + +/*************************************************************************/ +/*! This function reads the contents of a file and returns it in the + form of an array of int32_t. + \param fname is the name of the file + \param r_nlines is the number of lines in the file. If it is NULL, + this information is not returned. +*/ +/*************************************************************************/ +int32_t *gk_i32readfile(char *fname, gk_idx_t *r_nlines) +{ + size_t lnlen, nlines; + char *line=NULL; + int32_t *array=NULL; + FILE *fpin; + + gk_getfilestats(fname, &nlines, NULL, NULL, NULL); + if (nlines > 0) { + array = gk_i32malloc(nlines, "gk_i32readfile: array"); + + fpin = gk_fopen(fname, "r", "gk_readfile"); + nlines = 0; + + while (gk_getline(&line, &lnlen, fpin) != -1) { + sscanf(line, "%"SCNd32, &array[nlines++]); + } + + gk_fclose(fpin); + } + + gk_free((void **)&line, LTERM); + + if (r_nlines != NULL) + *r_nlines = nlines; + + return array; +} + + +/*************************************************************************/ +/*! This function reads the contents of a file and returns it in the + form of an array of int64_t. + \param fname is the name of the file + \param r_nlines is the number of lines in the file. If it is NULL, + this information is not returned. +*/ +/*************************************************************************/ +int64_t *gk_i64readfile(char *fname, gk_idx_t *r_nlines) +{ + size_t lnlen, nlines; + char *line=NULL; + int64_t *array=NULL; + FILE *fpin; + + gk_getfilestats(fname, &nlines, NULL, NULL, NULL); + if (nlines > 0) { + array = gk_i64malloc(nlines, "gk_i64readfile: array"); + + fpin = gk_fopen(fname, "r", "gk_readfile"); + nlines = 0; + + while (gk_getline(&line, &lnlen, fpin) != -1) { + sscanf(line, "%"SCNd64, &array[nlines++]); + } + + gk_fclose(fpin); + } + + gk_free((void **)&line, LTERM); + + if (r_nlines != NULL) + *r_nlines = nlines; + + return array; +} + +/*************************************************************************/ +/*! This function reads the contents of a binary file and returns it in the + form of an array of int32_t. + \param fname is the name of the file + \param r_nlines is the number of lines in the file. If it is NULL, + this information is not returned. +*/ +/*************************************************************************/ +int32_t *gk_i32readfilebin(char *fname, ssize_t *r_nelmnts) +{ + ssize_t fsize, nelmnts; + int32_t *array=NULL; + FILE *fpin; + + *r_nelmnts = -1; + + fsize = (ssize_t) gk_getfsize(fname); + if (fsize%sizeof(int32_t) != 0) { + gk_errexit(SIGERR, "The size of the file is not in multiples of sizeof(int32_t).\n"); + return NULL; + } + + nelmnts = fsize/sizeof(int32_t); + array = gk_i32malloc(nelmnts, "gk_i32readfilebin: array"); + + fpin = gk_fopen(fname, "rb", "gk_i32readfilebin"); + + if (fread(array, sizeof(int32_t), nelmnts, fpin) != nelmnts) { + gk_errexit(SIGERR, "Failed to read the number of words requested. %zd\n", nelmnts); + gk_free((void **)&array, LTERM); + return NULL; + } + gk_fclose(fpin); + + *r_nelmnts = nelmnts; + + return array; +} + +/*************************************************************************/ +/*! This function reads the contents of a binary file and returns it in the + form of an array of int64_t. + \param fname is the name of the file + \param r_nlines is the number of lines in the file. If it is NULL, + this information is not returned. +*/ +/*************************************************************************/ +int64_t *gk_i64readfilebin(char *fname, ssize_t *r_nelmnts) +{ + ssize_t fsize, nelmnts; + int64_t *array=NULL; + FILE *fpin; + + *r_nelmnts = -1; + + fsize = (ssize_t) gk_getfsize(fname); + if (fsize%sizeof(int64_t) != 0) { + gk_errexit(SIGERR, "The size of the file is not in multiples of sizeof(int64_t).\n"); + return NULL; + } + + nelmnts = fsize/sizeof(int64_t); + array = gk_i64malloc(nelmnts, "gk_i64readfilebin: array"); + + fpin = gk_fopen(fname, "rb", "gk_i64readfilebin"); + + if (fread(array, sizeof(int64_t), nelmnts, fpin) != nelmnts) { + gk_errexit(SIGERR, "Failed to read the number of words requested. %zd\n", nelmnts); + gk_free((void **)&array, LTERM); + return NULL; + } + gk_fclose(fpin); + + *r_nelmnts = nelmnts; + + return array; +} + +/*************************************************************************/ +/*! This function reads the contents of a binary file and returns it in the + form of an array of float. + \param fname is the name of the file + \param r_nlines is the number of lines in the file. If it is NULL, + this information is not returned. +*/ +/*************************************************************************/ +float *gk_freadfilebin(char *fname, ssize_t *r_nelmnts) +{ + ssize_t fsize, nelmnts; + float *array=NULL; + FILE *fpin; + + *r_nelmnts = -1; + + fsize = (ssize_t) gk_getfsize(fname); + if (fsize%sizeof(float) != 0) { + gk_errexit(SIGERR, "The size of the file is not in multiples of sizeof(float).\n"); + return NULL; + } + + nelmnts = fsize/sizeof(float); + array = gk_fmalloc(nelmnts, "gk_freadfilebin: array"); + + fpin = gk_fopen(fname, "rb", "gk_freadfilebin"); + + if (fread(array, sizeof(float), nelmnts, fpin) != nelmnts) { + gk_errexit(SIGERR, "Failed to read the number of words requested. %zd\n", nelmnts); + gk_free((void **)&array, LTERM); + return NULL; + } + gk_fclose(fpin); + + *r_nelmnts = nelmnts; + + return array; +} + + +/*************************************************************************/ +/*! This function writes the contents of an array into a binary file. + \param fname is the name of the file + \param n the number of elements in the array. + \param a the array to be written out. +*/ +/*************************************************************************/ +size_t gk_fwritefilebin(char *fname, size_t n, float *a) +{ + size_t fsize; + FILE *fp; + + fp = gk_fopen(fname, "wb", "gk_fwritefilebin"); + + fsize = fwrite(a, sizeof(float), n, fp); + + gk_fclose(fp); + + return fsize; +} + + +/*************************************************************************/ +/*! This function reads the contents of a binary file and returns it in the + form of an array of double. + \param fname is the name of the file + \param r_nlines is the number of lines in the file. If it is NULL, + this information is not returned. +*/ +/*************************************************************************/ +double *gk_dreadfilebin(char *fname, ssize_t *r_nelmnts) +{ + ssize_t fsize, nelmnts; + double *array=NULL; + FILE *fpin; + + *r_nelmnts = -1; + + fsize = (ssize_t) gk_getfsize(fname); + if (fsize%sizeof(double) != 0) { + gk_errexit(SIGERR, "The size of the file is not in multiples of sizeof(double).\n"); + return NULL; + } + + nelmnts = fsize/sizeof(double); + array = gk_dmalloc(nelmnts, "gk_dreadfilebin: array"); + + fpin = gk_fopen(fname, "rb", "gk_dreadfilebin"); + + if (fread(array, sizeof(double), nelmnts, fpin) != nelmnts) { + gk_errexit(SIGERR, "Failed to read the number of words requested. %zd\n", nelmnts); + gk_free((void **)&array, LTERM); + return NULL; + } + gk_fclose(fpin); + + *r_nelmnts = nelmnts; + + return array; +} + diff --git a/src/metis/GKlib/itemsets.c b/src/metis/GKlib/itemsets.c new file mode 100644 index 0000000..65b5af4 --- /dev/null +++ b/src/metis/GKlib/itemsets.c @@ -0,0 +1,210 @@ +/*! + * \file + * \brief Frequent/Closed itemset discovery routines + * + * This file contains the code for finding frequent/closed itemests. These routines + * are implemented using a call-back mechanism to deal with the discovered itemsets. + * + * \date 6/13/2008 + * \author George Karypis + * \version\verbatim $Id: itemsets.c 11075 2011-11-11 22:31:52Z karypis $ \endverbatim + */ + +#include + +/*-------------------------------------------------------------*/ +/*! Data structures for use within this module */ +/*-------------------------------------------------------------*/ +typedef struct { + int minfreq; /* the minimum frequency of a pattern */ + int maxfreq; /* the maximum frequency of a pattern */ + int minlen; /* the minimum length of the requested pattern */ + int maxlen; /* the maximum length of the requested pattern */ + int tnitems; /* the initial range of the item space */ + + /* the call-back function */ + void (*callback)(void *stateptr, int nitems, int *itemids, int ntrans, int *transids); + void *stateptr; /* the user-supplied pointer to pass to the callback */ + + /* workspace variables */ + int *rmarker; + gk_ikv_t *cand; +} isparams_t; + + +/*-------------------------------------------------------------*/ +/*! Prototypes for this module */ +/*-------------------------------------------------------------*/ +void itemsets_find_frequent_itemsets(isparams_t *params, gk_csr_t *mat, + int preflen, int *prefix); +gk_csr_t *itemsets_project_matrix(isparams_t *param, gk_csr_t *mat, int cid); + + + +/*************************************************************************/ +/*! The entry point of the frequent itemset discovery code */ +/*************************************************************************/ +void gk_find_frequent_itemsets(int ntrans, ssize_t *tranptr, int *tranind, + int minfreq, int maxfreq, int minlen, int maxlen, + void (*process_itemset)(void *stateptr, int nitems, int *itemids, + int ntrans, int *transids), + void *stateptr) +{ + ssize_t i; + gk_csr_t *mat, *pmat; + isparams_t params; + int *pattern; + + /* Create the matrix */ + mat = gk_csr_Create(); + mat->nrows = ntrans; + mat->ncols = tranind[gk_iargmax(tranptr[ntrans], tranind)]+1; + mat->rowptr = gk_zcopy(ntrans+1, tranptr, gk_zmalloc(ntrans+1, "gk_find_frequent_itemsets: mat.rowptr")); + mat->rowind = gk_icopy(tranptr[ntrans], tranind, gk_imalloc(tranptr[ntrans], "gk_find_frequent_itemsets: mat.rowind")); + mat->colids = gk_iincset(mat->ncols, 0, gk_imalloc(mat->ncols, "gk_find_frequent_itemsets: mat.colids")); + + /* Setup the parameters */ + params.minfreq = minfreq; + params.maxfreq = (maxfreq == -1 ? mat->nrows : maxfreq); + params.minlen = minlen; + params.maxlen = (maxlen == -1 ? mat->ncols : maxlen); + params.tnitems = mat->ncols; + params.callback = process_itemset; + params.stateptr = stateptr; + params.rmarker = gk_ismalloc(mat->nrows, 0, "gk_find_frequent_itemsets: rmarker"); + params.cand = gk_ikvmalloc(mat->ncols, "gk_find_frequent_itemsets: cand"); + + /* Perform the initial projection */ + gk_csr_CreateIndex(mat, GK_CSR_COL); + pmat = itemsets_project_matrix(¶ms, mat, -1); + gk_csr_Free(&mat); + + pattern = gk_imalloc(pmat->ncols, "gk_find_frequent_itemsets: pattern"); + itemsets_find_frequent_itemsets(¶ms, pmat, 0, pattern); + + gk_csr_Free(&pmat); + gk_free((void **)&pattern, ¶ms.rmarker, ¶ms.cand, LTERM); + +} + + + +/*************************************************************************/ +/*! The recursive routine for DFS-based frequent pattern discovery */ +/*************************************************************************/ +void itemsets_find_frequent_itemsets(isparams_t *params, gk_csr_t *mat, + int preflen, int *prefix) +{ + ssize_t i; + gk_csr_t *cmat; + + /* Project each frequent column */ + for (i=0; incols; i++) { + prefix[preflen] = mat->colids[i]; + + if (preflen+1 >= params->minlen) + (*params->callback)(params->stateptr, preflen+1, prefix, + mat->colptr[i+1]-mat->colptr[i], mat->colind+mat->colptr[i]); + + if (preflen+1 < params->maxlen) { + cmat = itemsets_project_matrix(params, mat, i); + itemsets_find_frequent_itemsets(params, cmat, preflen+1, prefix); + gk_csr_Free(&cmat); + } + } + +} + + +/******************************************************************************/ +/*! This function projects a matrix w.r.t. to a particular column. + It performs the following steps: + - Determines the length of each column that is remaining + - Sorts the columns in increasing length + - Creates a column-based version of the matrix with the proper + column ordering and renamed rowids. + */ +/*******************************************************************************/ +gk_csr_t *itemsets_project_matrix(isparams_t *params, gk_csr_t *mat, int cid) +{ + ssize_t i, j, k, ii, pnnz; + int nrows, ncols, pnrows, pncols; + ssize_t *colptr, *pcolptr; + int *colind, *colids, *pcolind, *pcolids, *rmarker; + gk_csr_t *pmat; + gk_ikv_t *cand; + + nrows = mat->nrows; + ncols = mat->ncols; + colptr = mat->colptr; + colind = mat->colind; + colids = mat->colids; + + rmarker = params->rmarker; + cand = params->cand; + + + /* Allocate space for the projected matrix based on what you know thus far */ + pmat = gk_csr_Create(); + pmat->nrows = pnrows = (cid == -1 ? nrows : colptr[cid+1]-colptr[cid]); + + + /* Mark the rows that will be kept and determine the prowids */ + if (cid == -1) { /* Initial projection */ + gk_iset(nrows, 1, rmarker); + } + else { /* The other projections */ + for (i=colptr[cid]; i= params->minfreq && k <= params->maxfreq) { + cand[pncols].val = i; + cand[pncols++].key = k; + pnnz += k; + } + } + + /* Sort the columns in increasing order */ + gk_ikvsorti(pncols, cand); + + + /* Allocate space for the remaining fields of the projected matrix */ + pmat->ncols = pncols; + pmat->colids = pcolids = gk_imalloc(pncols, "itemsets_project_matrix: pcolids"); + pmat->colptr = pcolptr = gk_zmalloc(pncols+1, "itemsets_project_matrix: pcolptr"); + pmat->colind = pcolind = gk_imalloc(pnnz, "itemsets_project_matrix: pcolind"); + + + /* Populate the projected matrix */ + pcolptr[0] = 0; + for (pnnz=0, ii=0; ii + + +/*************************************************************************/ +/*! This function creates an mcore + */ +/*************************************************************************/ +gk_mcore_t *gk_mcoreCreate(size_t coresize) +{ + gk_mcore_t *mcore; + + mcore = (gk_mcore_t *)gk_malloc(sizeof(gk_mcore_t), "gk_mcoreCreate: mcore"); + memset(mcore, 0, sizeof(gk_mcore_t)); + + mcore->coresize = coresize; + mcore->corecpos = 0; + + mcore->core = (coresize == 0 ? NULL : gk_malloc(mcore->coresize, "gk_mcoreCreate: core")); + + /* allocate the memory for keeping track of malloc ops */ + mcore->nmops = 2048; + mcore->cmop = 0; + mcore->mops = (gk_mop_t *)gk_malloc(mcore->nmops*sizeof(gk_mop_t), "gk_mcoreCreate: mcore->mops"); + + return mcore; +} + + +/*************************************************************************/ +/*! This function creates an mcore. This version is used for gkmcore. + */ +/*************************************************************************/ +gk_mcore_t *gk_gkmcoreCreate() +{ + gk_mcore_t *mcore; + + if ((mcore = (gk_mcore_t *)malloc(sizeof(gk_mcore_t))) == NULL) + return NULL; + memset(mcore, 0, sizeof(gk_mcore_t)); + + /* allocate the memory for keeping track of malloc ops */ + mcore->nmops = 2048; + mcore->cmop = 0; + if ((mcore->mops = (gk_mop_t *)malloc(mcore->nmops*sizeof(gk_mop_t))) == NULL) { + free(mcore); + return NULL; + } + + return mcore; +} + + +/*************************************************************************/ +/*! This function destroys an mcore. + */ +/*************************************************************************/ +void gk_mcoreDestroy(gk_mcore_t **r_mcore, int showstats) +{ + gk_mcore_t *mcore = *r_mcore; + + if (mcore == NULL) + return; + + if (showstats) + printf("\n gk_mcore statistics\n" + " coresize: %12zu nmops: %12zu cmop: %6zu\n" + " num_callocs: %12zu num_hallocs: %12zu\n" + " size_callocs: %12zu size_hallocs: %12zu\n" + " cur_callocs: %12zu cur_hallocs: %12zu\n" + " max_callocs: %12zu max_hallocs: %12zu\n", + mcore->coresize, mcore->nmops, mcore->cmop, + mcore->num_callocs, mcore->num_hallocs, + mcore->size_callocs, mcore->size_hallocs, + mcore->cur_callocs, mcore->cur_hallocs, + mcore->max_callocs, mcore->max_hallocs); + + if (mcore->cur_callocs != 0 || mcore->cur_hallocs != 0 || mcore->cmop != 0) { + printf("***Warning: mcore memory was not fully freed when destroyed.\n" + " cur_callocs: %6zu cur_hallocs: %6zu cmop: %6zu\n", + mcore->cur_callocs, mcore->cur_hallocs, mcore->cmop); + } + + gk_free((void **)&mcore->core, &mcore->mops, &mcore, LTERM); + + *r_mcore = NULL; +} + + +/*************************************************************************/ +/*! This function destroys an mcore. This version is for gkmcore. + */ +/*************************************************************************/ +void gk_gkmcoreDestroy(gk_mcore_t **r_mcore, int showstats) +{ + gk_mcore_t *mcore = *r_mcore; + + if (mcore == NULL) + return; + + if (showstats) + printf("\n gk_mcore statistics\n" + " nmops: %12zu cmop: %6zu\n" + " num_hallocs: %12zu\n" + " size_hallocs: %12zu\n" + " cur_hallocs: %12zu\n" + " max_hallocs: %12zu\n", + mcore->nmops, mcore->cmop, + mcore->num_hallocs, + mcore->size_hallocs, + mcore->cur_hallocs, + mcore->max_hallocs); + + if (mcore->cur_hallocs != 0 || mcore->cmop != 0) { + printf("***Warning: mcore memory was not fully freed when destroyed.\n" + " cur_hallocs: %6zu cmop: %6zu\n", + mcore->cur_hallocs, mcore->cmop); + } + + free(mcore->mops); + free(mcore); + + *r_mcore = NULL; +} + + +/*************************************************************************/ +/*! This function allocate space from the core/heap + */ +/*************************************************************************/ +void *gk_mcoreMalloc(gk_mcore_t *mcore, size_t nbytes) +{ + void *ptr; + + /* pad to make pointers 8-byte aligned */ + nbytes += (nbytes%8 == 0 ? 0 : 8 - nbytes%8); + + if (mcore->corecpos + nbytes < mcore->coresize) { + /* service this request from the core */ + ptr = ((char *)mcore->core)+mcore->corecpos; + mcore->corecpos += nbytes; + + gk_mcoreAdd(mcore, GK_MOPT_CORE, nbytes, ptr); + } + else { + /* service this request from the heap */ + ptr = gk_malloc(nbytes, "gk_mcoremalloc: ptr"); + + gk_mcoreAdd(mcore, GK_MOPT_HEAP, nbytes, ptr); + } + + /* + printf("MCMALLOC: %zu %d %8zu\n", mcore->cmop-1, + mcore->mops[mcore->cmop-1].type, mcore->mops[mcore->cmop-1].nbytes); + */ + + return ptr; +} + + +/*************************************************************************/ +/*! This function sets a marker in the stack of malloc ops to be used + subsequently for freeing purposes + */ +/*************************************************************************/ +void gk_mcorePush(gk_mcore_t *mcore) +{ + gk_mcoreAdd(mcore, GK_MOPT_MARK, 0, NULL); + /* printf("MCPPUSH: %zu\n", mcore->cmop-1); */ +} + + +/*************************************************************************/ +/*! This function sets a marker in the stack of malloc ops to be used + subsequently for freeing purposes. This is the gkmcore version. + */ +/*************************************************************************/ +void gk_gkmcorePush(gk_mcore_t *mcore) +{ + gk_gkmcoreAdd(mcore, GK_MOPT_MARK, 0, NULL); + /* printf("MCPPUSH: %zu\n", mcore->cmop-1); */ +} + + +/*************************************************************************/ +/*! This function frees all mops since the last push + */ +/*************************************************************************/ +void gk_mcorePop(gk_mcore_t *mcore) +{ + while (mcore->cmop > 0) { + mcore->cmop--; + switch (mcore->mops[mcore->cmop].type) { + case GK_MOPT_MARK: /* push marker */ + goto DONE; + break; + + case GK_MOPT_CORE: /* core free */ + if (mcore->corecpos < mcore->mops[mcore->cmop].nbytes) + errexit("Internal Error: wspace's core is about to be over-freed [%zu, %zu, %zd]\n", + mcore->coresize, mcore->corecpos, mcore->mops[mcore->cmop].nbytes); + + mcore->corecpos -= mcore->mops[mcore->cmop].nbytes; + mcore->cur_callocs -= mcore->mops[mcore->cmop].nbytes; + break; + + case GK_MOPT_HEAP: /* heap free */ + gk_free((void **)&mcore->mops[mcore->cmop].ptr, LTERM); + mcore->cur_hallocs -= mcore->mops[mcore->cmop].nbytes; + break; + + default: + gk_errexit(SIGMEM, "Unknown mop type of %d\n", mcore->mops[mcore->cmop].type); + } + } + +DONE: + ; + /*printf("MCPPOP: %zu\n", mcore->cmop); */ +} + + +/*************************************************************************/ +/*! This function frees all mops since the last push. This version is + for poping the gkmcore and it uses free instead of gk_free. + */ +/*************************************************************************/ +void gk_gkmcorePop(gk_mcore_t *mcore) +{ + while (mcore->cmop > 0) { + mcore->cmop--; + switch (mcore->mops[mcore->cmop].type) { + case GK_MOPT_MARK: /* push marker */ + goto DONE; + break; + + case GK_MOPT_HEAP: /* heap free */ + free(mcore->mops[mcore->cmop].ptr); + mcore->cur_hallocs -= mcore->mops[mcore->cmop].nbytes; + break; + + default: + gk_errexit(SIGMEM, "Unknown mop type of %d\n", mcore->mops[mcore->cmop].type); + } + } + +DONE: + ; +} + + +/*************************************************************************/ +/*! Adds a memory allocation at the end of the list. + */ +/*************************************************************************/ +void gk_mcoreAdd(gk_mcore_t *mcore, int type, size_t nbytes, void *ptr) +{ + if (mcore->cmop == mcore->nmops) { + mcore->nmops *= 2; + mcore->mops = realloc(mcore->mops, mcore->nmops*sizeof(gk_mop_t)); + if (mcore->mops == NULL) + gk_errexit(SIGMEM, "***Memory allocation for gkmcore failed.\n"); + } + + mcore->mops[mcore->cmop].type = type; + mcore->mops[mcore->cmop].nbytes = nbytes; + mcore->mops[mcore->cmop].ptr = ptr; + mcore->cmop++; + + switch (type) { + case GK_MOPT_MARK: + break; + + case GK_MOPT_CORE: + mcore->num_callocs++; + mcore->size_callocs += nbytes; + mcore->cur_callocs += nbytes; + if (mcore->max_callocs < mcore->cur_callocs) + mcore->max_callocs = mcore->cur_callocs; + break; + + case GK_MOPT_HEAP: + mcore->num_hallocs++; + mcore->size_hallocs += nbytes; + mcore->cur_hallocs += nbytes; + if (mcore->max_hallocs < mcore->cur_hallocs) + mcore->max_hallocs = mcore->cur_hallocs; + break; + default: + gk_errexit(SIGMEM, "Incorrect mcore type operation.\n"); + } +} + + +/*************************************************************************/ +/*! Adds a memory allocation at the end of the list. This is the gkmcore + version. + */ +/*************************************************************************/ +void gk_gkmcoreAdd(gk_mcore_t *mcore, int type, size_t nbytes, void *ptr) +{ + if (mcore->cmop == mcore->nmops) { + mcore->nmops *= 2; + mcore->mops = realloc(mcore->mops, mcore->nmops*sizeof(gk_mop_t)); + if (mcore->mops == NULL) + gk_errexit(SIGMEM, "***Memory allocation for gkmcore failed.\n"); + } + + mcore->mops[mcore->cmop].type = type; + mcore->mops[mcore->cmop].nbytes = nbytes; + mcore->mops[mcore->cmop].ptr = ptr; + mcore->cmop++; + + switch (type) { + case GK_MOPT_MARK: + break; + + case GK_MOPT_HEAP: + mcore->num_hallocs++; + mcore->size_hallocs += nbytes; + mcore->cur_hallocs += nbytes; + if (mcore->max_hallocs < mcore->cur_hallocs) + mcore->max_hallocs = mcore->cur_hallocs; + break; + default: + gk_errexit(SIGMEM, "Incorrect mcore type operation.\n"); + } +} + + +/*************************************************************************/ +/*! This function deletes the mop associated with the supplied pointer. + The mop has to be a heap allocation, otherwise it fails violently. + */ +/*************************************************************************/ +void gk_mcoreDel(gk_mcore_t *mcore, void *ptr) +{ + int i; + + for (i=mcore->cmop-1; i>=0; i--) { + if (mcore->mops[i].type == GK_MOPT_MARK) + gk_errexit(SIGMEM, "Could not find pointer %p in mcore\n", ptr); + + if (mcore->mops[i].ptr == ptr) { + if (mcore->mops[i].type != GK_MOPT_HEAP) + gk_errexit(SIGMEM, "Trying to delete a non-HEAP mop.\n"); + + mcore->cur_hallocs -= mcore->mops[i].nbytes; + mcore->mops[i] = mcore->mops[--mcore->cmop]; + return; + } + } + + gk_errexit(SIGMEM, "mcoreDel should never have been here!\n"); +} + + +/*************************************************************************/ +/*! This function deletes the mop associated with the supplied pointer. + The mop has to be a heap allocation, otherwise it fails violently. + This is the gkmcore version. + */ +/*************************************************************************/ +void gk_gkmcoreDel(gk_mcore_t *mcore, void *ptr) +{ + int i; + + for (i=mcore->cmop-1; i>=0; i--) { + if (mcore->mops[i].type == GK_MOPT_MARK) + gk_errexit(SIGMEM, "Could not find pointer %p in mcore\n", ptr); + + if (mcore->mops[i].ptr == ptr) { + if (mcore->mops[i].type != GK_MOPT_HEAP) + gk_errexit(SIGMEM, "Trying to delete a non-HEAP mop.\n"); + + mcore->cur_hallocs -= mcore->mops[i].nbytes; + mcore->mops[i] = mcore->mops[--mcore->cmop]; + return; + } + } + + gk_errexit(SIGMEM, "gkmcoreDel should never have been here!\n"); +} + diff --git a/src/metis/GKlib/memory.c b/src/metis/GKlib/memory.c new file mode 100644 index 0000000..cdd00fa --- /dev/null +++ b/src/metis/GKlib/memory.c @@ -0,0 +1,252 @@ +/*! +\file memory.c +\brief This file contains various allocation routines + +The allocation routines included are for 1D and 2D arrays of the +most datatypes that GKlib support. Many of these routines are +defined with the help of the macros in gk_memory.h. These macros +can be used to define other memory allocation routines. + +\date Started 4/3/2007 +\author George +\version\verbatim $Id: memory.c 10783 2011-09-21 23:19:56Z karypis $ \endverbatim +*/ + + +#include + +/* This is for the global mcore that tracks all heap allocations */ +static __thread gk_mcore_t *gkmcore = NULL; + + +/*************************************************************************/ +/*! Define the set of memory allocation routines for each data type */ +/**************************************************************************/ +GK_MKALLOC(gk_c, char) +GK_MKALLOC(gk_i, int) +GK_MKALLOC(gk_i32, int32_t) +GK_MKALLOC(gk_i64, int64_t) +GK_MKALLOC(gk_z, ssize_t) +GK_MKALLOC(gk_f, float) +GK_MKALLOC(gk_d, double) +GK_MKALLOC(gk_idx, gk_idx_t) + +GK_MKALLOC(gk_ckv, gk_ckv_t) +GK_MKALLOC(gk_ikv, gk_ikv_t) +GK_MKALLOC(gk_i32kv, gk_i32kv_t) +GK_MKALLOC(gk_i64kv, gk_i64kv_t) +GK_MKALLOC(gk_zkv, gk_zkv_t) +GK_MKALLOC(gk_fkv, gk_fkv_t) +GK_MKALLOC(gk_dkv, gk_dkv_t) +GK_MKALLOC(gk_skv, gk_skv_t) +GK_MKALLOC(gk_idxkv, gk_idxkv_t) + + + + + + +/*************************************************************************/ +/*! This function allocates a two-dimensional matrix. + */ +/*************************************************************************/ +void gk_AllocMatrix(void ***r_matrix, size_t elmlen, size_t ndim1, size_t ndim2) +{ + gk_idx_t i, j; + void **matrix; + + *r_matrix = NULL; + + if ((matrix = (void **)gk_malloc(ndim1*sizeof(void *), "gk_AllocMatrix: matrix")) == NULL) + return; + + for (i=0; icmop == 0) { + gk_gkmcoreDestroy(&gkmcore, showstats); + gkmcore = NULL; + } + } +} + + +/*************************************************************************/ +/*! This function is my wrapper around malloc that provides the following + enhancements over malloc: + * It always allocates one byte of memory, even if 0 bytes are requested. + This is to ensure that checks of returned values do not lead to NULL + due to 0 bytes requested. + * It zeros-out the memory that is allocated. This is for a quick init + of the underlying datastructures. +*/ +/**************************************************************************/ +void *gk_malloc(size_t nbytes, char *msg) +{ + void *ptr=NULL; + + if (nbytes == 0) + nbytes++; /* Force mallocs to actually allocate some memory */ + + ptr = (void *)malloc(nbytes); + + if (ptr == NULL) { + fprintf(stderr, " Current memory used: %10zu bytes\n", gk_GetCurMemoryUsed()); + fprintf(stderr, " Maximum memory used: %10zu bytes\n", gk_GetMaxMemoryUsed()); + gk_errexit(SIGMEM, "***Memory allocation failed for %s. Requested size: %zu bytes", + msg, nbytes); + return NULL; + } + + /* add this memory allocation */ + if (gkmcore != NULL) gk_gkmcoreAdd(gkmcore, GK_MOPT_HEAP, nbytes, ptr); + + /* zero-out the allocated space */ +#ifndef NDEBUG + memset(ptr, 0, nbytes); +#endif + + return ptr; +} + + +/************************************************************************* +* This function is my wrapper around realloc +**************************************************************************/ +void *gk_realloc(void *oldptr, size_t nbytes, char *msg) +{ + void *ptr=NULL; + + if (nbytes == 0) + nbytes++; /* Force mallocs to actually allocate some memory */ + + /* remove this memory de-allocation */ + if (gkmcore != NULL && oldptr != NULL) gk_gkmcoreDel(gkmcore, oldptr); + + ptr = (void *)realloc(oldptr, nbytes); + + if (ptr == NULL) { + fprintf(stderr, " Maximum memory used: %10zu bytes\n", gk_GetMaxMemoryUsed()); + fprintf(stderr, " Current memory used: %10zu bytes\n", gk_GetCurMemoryUsed()); + gk_errexit(SIGMEM, "***Memory realloc failed for %s. " "Requested size: %zu bytes", + msg, nbytes); + return NULL; + } + + /* add this memory allocation */ + if (gkmcore != NULL) gk_gkmcoreAdd(gkmcore, GK_MOPT_HEAP, nbytes, ptr); + + return ptr; +} + + +/************************************************************************* +* This function is my wrapper around free, allows multiple pointers +**************************************************************************/ +void gk_free(void **ptr1,...) +{ + va_list plist; + void **ptr; + + if (*ptr1 != NULL) { + free(*ptr1); + + /* remove this memory de-allocation */ + if (gkmcore != NULL) gk_gkmcoreDel(gkmcore, *ptr1); + } + *ptr1 = NULL; + + va_start(plist, ptr1); + while ((ptr = va_arg(plist, void **)) != LTERM) { + if (*ptr != NULL) { + free(*ptr); + + /* remove this memory de-allocation */ + if (gkmcore != NULL) gk_gkmcoreDel(gkmcore, *ptr); + } + *ptr = NULL; + } + va_end(plist); +} + + +/************************************************************************* +* This function returns the current ammount of dynamically allocated +* memory that is used by the system +**************************************************************************/ +size_t gk_GetCurMemoryUsed() +{ + if (gkmcore == NULL) + return 0; + else + return gkmcore->cur_hallocs; +} + + +/************************************************************************* +* This function returns the maximum ammount of dynamically allocated +* memory that was used by the system +**************************************************************************/ +size_t gk_GetMaxMemoryUsed() +{ + if (gkmcore == NULL) + return 0; + else + return gkmcore->max_hallocs; +} diff --git a/src/metis/GKlib/ms_inttypes.h b/src/metis/GKlib/ms_inttypes.h new file mode 100644 index 0000000..e26204b --- /dev/null +++ b/src/metis/GKlib/ms_inttypes.h @@ -0,0 +1,301 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "ms_stdint.h" + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + + +#endif // _MSC_INTTYPES_H_ ] diff --git a/src/metis/GKlib/ms_stat.h b/src/metis/GKlib/ms_stat.h new file mode 100644 index 0000000..a1ef6fa --- /dev/null +++ b/src/metis/GKlib/ms_stat.h @@ -0,0 +1,22 @@ +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MS_STAT_H_ +#define _MS_STAT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include +/* Test macros for file types. */ + +#define __S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask)) + +#define S_ISDIR(mode) __S_ISTYPE((mode), S_IFDIR) +#define S_ISCHR(mode) __S_ISTYPE((mode), S_IFCHR) +#define S_ISBLK(mode) __S_ISTYPE((mode), S_IFBLK) +#define S_ISREG(mode) __S_ISTYPE((mode), S_IFREG) + +#endif diff --git a/src/metis/GKlib/ms_stdint.h b/src/metis/GKlib/ms_stdint.h new file mode 100644 index 0000000..7e200dc --- /dev/null +++ b/src/metis/GKlib/ms_stdint.h @@ -0,0 +1,222 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if (_MSC_VER < 1300) && defined(__cplusplus) + extern "C++" { +#endif +# include +#if (_MSC_VER < 1300) && defined(__cplusplus) + } +#endif + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef int intptr_t; + typedef unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/src/metis/GKlib/omp.c b/src/metis/GKlib/omp.c new file mode 100644 index 0000000..bdd543a --- /dev/null +++ b/src/metis/GKlib/omp.c @@ -0,0 +1,27 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * omp.c + * + * This file contains "fake" implementations of OpenMP's runtime libraries + * + */ + +#include + +#ifdef GK_NOOPENMP /* remove those for now */ +#if !defined(_OPENMP) +void omp_set_num_threads(int num_threads) { return; } +int omp_get_num_threads(void) { return 1; } +int omp_get_max_threads(void) { return 1; } +int omp_get_thread_num(void) { return 0; } +int omp_get_num_procs(void) { return 1; } +int omp_in_parallel(void) { return 0; } +void omp_set_dynamic(int num_threads) { return; } +int omp_get_dynamic(void) { return 0; } +void omp_set_nested(int nested) { return; } +int omp_get_nested(void) { return 0; } +#endif +#endif + + diff --git a/src/metis/GKlib/pdb.c b/src/metis/GKlib/pdb.c new file mode 100644 index 0000000..b4d2226 --- /dev/null +++ b/src/metis/GKlib/pdb.c @@ -0,0 +1,460 @@ +/************************************************************************/ +/*! \file pdb.c + +\brief Functions for parsing pdb files. + +Pdb reader (parser). Loads arrays of pointers for easy backbone access. + +\date Started 10/20/06 +\author Kevin +\version $Id: pdb.c 10711 2011-08-31 22:23:04Z karypis $ +*/ +/************************************************************************/ +#include + +/************************************************************************/ +/*! \brief Converts three-letter amino acid codes to one-leter codes. + +This function takes a three letter \c * and converts it to a single \c + +\param res is the three-letter code to be converted. +\returns A \c representing the amino acid. +*/ +/************************************************************************/ +char gk_threetoone(char *res) { /* {{{ */ + /* make sure the matching works */ + res[0] = toupper(res[0]); + res[1] = toupper(res[1]); + res[2] = toupper(res[2]); + if(strcmp(res,"ALA") == 0) { + return 'A'; + } + else if(strcmp(res,"CYS") == 0) { + return 'C'; + } + else if(strcmp(res,"ASP") == 0) { + return 'D'; + } + else if(strcmp(res,"GLU") == 0) { + return 'E'; + } + else if(strcmp(res,"PHE") == 0) { + return 'F'; + } + else if(strcmp(res,"GLY") == 0) { + return 'G'; + } + else if(strcmp(res,"HIS") == 0) { + return 'H'; + } + else if(strcmp(res,"ILE") == 0) { + return 'I'; + } + else if(strcmp(res,"LYS") == 0) { + return 'K'; + } + else if(strcmp(res,"LEU") == 0) { + return 'L'; + } + else if(strcmp(res,"MET") == 0) { + return 'M'; + } + else if(strcmp(res,"ASN") == 0) { + return 'N'; + } + else if(strcmp(res,"PRO") == 0) { + return 'P'; + } + else if(strcmp(res,"GLN") == 0) { + return 'Q'; + } + else if(strcmp(res,"ARG") == 0) { + return 'R'; + } + else if(strcmp(res,"SER") == 0) { + return 'S'; + } + else if(strcmp(res,"THR") == 0) { + return 'T'; + } + else if(strcmp(res,"SCY") == 0) { + return 'U'; + } + else if(strcmp(res,"VAL") == 0) { + return 'V'; + } + else if(strcmp(res,"TRP") == 0) { + return 'W'; + } + else if(strcmp(res,"TYR") == 0) { + return 'Y'; + } + else { + return 'X'; + } +} /* }}} */ + +/************************************************************************/ +/*! \brief Frees the memory of a pdbf structure. + +This function takes a pdbf pointer and frees all the memory below it. + +\param p is the pdbf structure to be freed. +*/ +/************************************************************************/ +void gk_freepdbf(pdbf *p) { /* {{{ */ + int i; + if(p != NULL) { + gk_free((void **)&p->resSeq, LTERM); + for(i=0; inatoms; i++) { + gk_free((void **)&p->atoms[i].name, &p->atoms[i].resname, LTERM); + } + for(i=0; inresidues; i++) { + gk_free((void *)&p->threeresSeq[i], LTERM); + } + /* this may look like it's wrong, but it's just a 1-d array of pointers, and + the pointers themselves are freed above */ + gk_free((void **)&p->bbs, &p->cas, &p->atoms, &p->cm, &p->threeresSeq, LTERM); + } + gk_free((void **)&p, LTERM); +} /* }}} */ + +/************************************************************************/ +/*! \brief Reads a pdb file into a pdbf structure + +This function allocates a pdbf structure and reads the file fname into +that structure. + +\param fname is the file name to be read +\returns A filled pdbf structure. +*/ +/************************************************************************/ +pdbf *gk_readpdbfile(char *fname) { /* {{{ */ + int i=0, res=0; + char linetype[6]; + int aserial; + char aname[5] = " \0"; + char altLoc = ' '; + char rname[4] = " \0"; + char chainid = ' '; + char oldchainid = ' '; + int rserial; + int oldRserial = -37; + char icode = ' '; + char element = ' '; + double x; + double y; + double z; + double avgx; + double avgy; + double avgz; + double opcy; + double tmpt; + char line[MAXLINELEN]; + int corruption=0; + int nresatoms; + + int atoms=0, residues=0, cas=0, bbs=0, firstres=1; + pdbf *toFill = gk_malloc(sizeof(pdbf),"fillme"); + FILE *FPIN; + + FPIN = gk_fopen(fname,"r",fname); + while(fgets(line, 256, FPIN)) { + sscanf(line,"%s ",linetype); + /* It seems the only reliable parts are through temperature, so we only use these parts */ + /* if(strstr(linetype, "ATOM") != NULL || strstr(linetype, "HETATM") != NULL) { */ + if(strstr(linetype, "ATOM") != NULL) { + sscanf(line, "%6s%5d%*1c%4c%1c%3c%*1c%1c%4d%1c%*3c%8lf%8lf%8lf%6lf%6lf %c\n", + linetype,&aserial,aname,&altLoc,rname,&chainid,&rserial,&icode,&x,&y,&z,&opcy,&tmpt,&element); + sscanf(linetype, " %s ",linetype); + sscanf(aname, " %s ",aname); + sscanf(rname, " %s ",rname); + if(altLoc != ' ') { + corruption = corruption|CRP_ALTLOCS; + } + + if(firstres == 1) { + oldRserial = rserial; + oldchainid = chainid; + residues++; + firstres = 0; + } + if(oldRserial != rserial) { + residues++; + oldRserial = rserial; + } + if(oldchainid != chainid) { + corruption = corruption|CRP_MULTICHAIN; + } + oldchainid = chainid; + atoms++; + if(strcmp(aname,"CA") == 0) { + cas++; + } + if(strcmp(aname,"N") == 0 || strcmp(aname,"CA") == 0 || + strcmp(aname,"C") == 0 || strcmp(aname,"O") == 0) { + bbs++; + } + } + else if(strstr(linetype, "ENDMDL") != NULL || strstr(linetype, "END") != NULL || strstr(linetype, "TER") != NULL) { + break; + } + } + fclose(FPIN); + + /* printf("File has coordinates for %d atoms in %d residues\n",atoms,residues); */ + toFill->natoms = atoms; + toFill->ncas = cas; + toFill->nbbs = bbs; + toFill->nresidues = residues; + toFill->resSeq = (char *) gk_malloc (residues*sizeof(char),"residue seq"); + toFill->threeresSeq = (char **)gk_malloc (residues*sizeof(char *),"residue seq"); + toFill->atoms = (atom *) gk_malloc (atoms*sizeof(atom), "atoms"); + toFill->bbs = (atom **)gk_malloc ( bbs*sizeof(atom *),"bbs"); + toFill->cas = (atom **)gk_malloc ( cas*sizeof(atom *),"cas"); + toFill->cm = (center_of_mass *)gk_malloc(residues*sizeof(center_of_mass),"center of mass"); + res=0; firstres=1; cas=0; bbs=0; i=0; + avgx = 0.0; avgy = 0.0; avgz = 0.0; + nresatoms = 0; + + FPIN = gk_fopen(fname,"r",fname); + while(fgets(line, 256, FPIN)) { + sscanf(line,"%s ",linetype); + /* It seems the only reliable parts are through temperature, so we only use these parts */ + /* if(strstr(linetype, "ATOM") != NULL || strstr(linetype, "HETATM") != NULL) { */ + if(strstr(linetype, "ATOM") != NULL ) { + + /* to ensure our memory doesn't get corrupted by the biologists, we only read this far */ + sscanf(line, "%6s%5d%*1c%4c%1c%3c%*1c%1c%4d%1c%*3c%8lf%8lf%8lf%6lf%6lf %c\n", + linetype,&aserial,aname,&altLoc,rname,&chainid,&rserial,&icode,&x,&y,&z,&opcy,&tmpt,&element); + sscanf(aname, "%s",aname); + sscanf(rname, "%s",rname); + + if(firstres == 1) { + toFill->resSeq[res] = gk_threetoone(rname); + toFill->threeresSeq[res] = gk_strdup(rname); + oldRserial = rserial; + res++; + firstres = 0; + } + if(oldRserial != rserial) { + /* we're changing residues. store the center of mass from the last one & reset */ + toFill->cm[res-1].x = avgx/nresatoms; + toFill->cm[res-1].y = avgy/nresatoms; + toFill->cm[res-1].z = avgz/nresatoms; + avgx = 0.0; avgy = 0.0; avgz = 0.0; + nresatoms = 0; + toFill->cm[res-1].name = toFill->resSeq[res-1]; + + toFill->threeresSeq[res] = gk_strdup(rname); + toFill->resSeq[res] = gk_threetoone(rname); + res++; + oldRserial = rserial; + } + avgx += x; + avgy += y; + avgz += z; + nresatoms++; + + toFill->atoms[i].x = x; + toFill->atoms[i].y = y; + toFill->atoms[i].z = z; + toFill->atoms[i].opcy = opcy; + toFill->atoms[i].tmpt = tmpt; + toFill->atoms[i].element = element; + toFill->atoms[i].serial = aserial; + toFill->atoms[i].chainid = chainid; + toFill->atoms[i].altLoc = altLoc; + toFill->atoms[i].rserial = rserial; + toFill->atoms[i].icode = icode; + toFill->atoms[i].name = gk_strdup(aname); + toFill->atoms[i].resname = gk_strdup(rname); + /* Set up pointers for the backbone and c-alpha shortcuts */ + if(strcmp(aname,"CA") == 0) { + toFill->cas[cas] = &(toFill->atoms[i]); + cas++; + } + if(strcmp(aname,"N") == 0 || strcmp(aname,"CA") == 0 || strcmp(aname,"C") == 0 || strcmp(aname,"O") == 0) { + toFill->bbs[bbs] = &(toFill->atoms[i]); + bbs++; + } + i++; + } + else if(strstr(linetype, "ENDMDL") != NULL || strstr(linetype, "END") != NULL || strstr(linetype, "TER") != NULL) { + break; + } + } + /* get that last average */ + toFill->cm[res-1].x = avgx/nresatoms; + toFill->cm[res-1].y = avgy/nresatoms; + toFill->cm[res-1].z = avgz/nresatoms; + /* Begin test code */ + if(cas != residues) { + printf("Number of residues and CA coordinates differs by %d (!)\n",residues-cas); + if(cas < residues) { + corruption = corruption|CRP_MISSINGCA; + } + else if(cas > residues) { + corruption = corruption|CRP_MULTICA; + } + } + if(bbs < residues*4) { + corruption = corruption|CRP_MISSINGBB; + } + else if(bbs > residues*4) { + corruption = corruption|CRP_MULTIBB; + } + fclose(FPIN); + toFill->corruption = corruption; + /* if(corruption == 0) + printf("File was clean!\n"); */ + return(toFill); +} /* }}} */ + +/************************************************************************/ +/*! \brief Writes the sequence of residues from a pdb file. + +This function takes a pdbf structure and a filename, and writes out +the amino acid sequence according to the atomic coordinates. The output +is in fasta format. + + +\param p is the pdbf structure with the sequence of interest +\param fname is the file name to be written +*/ +/************************************************************************/ +void gk_writefastafrompdb(pdbf *pb, char *fname) { + int i; + FILE *FPOUT; + + FPOUT = gk_fopen(fname,"w",fname); + fprintf(FPOUT,"> %s\n",fname); + + for(i=0; inresidues; i++) + fprintf(FPOUT,"%c",pb->resSeq[i]); + + fprintf(FPOUT,"\n"); + fclose(FPOUT); +} + +/************************************************************************/ +/*! \brief Writes all centers of mass in pdb-format to file fname. + +This function takes a pdbf structure and writes out the calculated +mass center information to file fname as though each one was a c-alpha. + +\param p is the pdbf structure to write out +\param fname is the file name to be written +*/ +/************************************************************************/ +void gk_writecentersofmass(pdbf *p, char *fname) { + int i; + FILE *FPIN; + FPIN = gk_fopen(fname,"w",fname); + for(i=0; inresidues; i++) { + fprintf(FPIN,"%-6s%5d %4s%1c%3s %1c%4d%1c %8.3lf%8.3lf%8.3lf%6.2f%6.2f\n", + "ATOM ",i,"CA",' ',p->threeresSeq[i],' ',i,' ',p->cm[i].x,p->cm[i].y,p->cm[i].z,1.0,-37.0); + } + fclose(FPIN); +} + +/************************************************************************/ +/*! \brief Writes all atoms in p in pdb-format to file fname. + +This function takes a pdbf structure and writes out all the atom +information to file fname. + +\param p is the pdbf structure to write out +\param fname is the file name to be written +*/ +/************************************************************************/ +void gk_writefullatom(pdbf *p, char *fname) { + int i; + FILE *FPIN; + FPIN = gk_fopen(fname,"w",fname); + for(i=0; inatoms; i++) { + fprintf(FPIN,"%-6s%5d %4s%1c%3s %1c%4d%1c %8.3lf%8.3lf%8.3lf%6.2f%6.2f\n", + "ATOM ",p->atoms[i].serial,p->atoms[i].name,p->atoms[i].altLoc,p->atoms[i].resname,p->atoms[i].chainid,p->atoms[i].rserial,p->atoms[i].icode,p->atoms[i].x,p->atoms[i].y,p->atoms[i].z,p->atoms[i].opcy,p->atoms[i].tmpt); + } + fclose(FPIN); +} + +/************************************************************************/ +/*! \brief Writes out all the backbone atoms of a structure in pdb format + +This function takes a pdbf structure p and writes only the backbone atoms +to a filename fname. + +\param p is the pdb structure to write out. +\param fname is the file name to be written. +*/ +/************************************************************************/ +void gk_writebackbone(pdbf *p, char *fname) { + int i; + FILE *FPIN; + FPIN = gk_fopen(fname,"w",fname); + for(i=0; inbbs; i++) { + fprintf(FPIN,"%-6s%5d %4s%1c%3s %1c%4d%1c %8.3lf%8.3lf%8.3lf%6.2f%6.2f\n", + "ATOM ",p->bbs[i]->serial,p->bbs[i]->name,p->bbs[i]->altLoc,p->bbs[i]->resname,p->bbs[i]->chainid,p->bbs[i]->rserial,p->bbs[i]->icode,p->bbs[i]->x,p->bbs[i]->y,p->bbs[i]->z,p->bbs[i]->opcy,p->bbs[i]->tmpt); + } + fclose(FPIN); +} + +/************************************************************************/ +/*! \brief Writes out all the alpha carbon atoms of a structure + +This function takes a pdbf structure p and writes only the alpha carbon +atoms to a filename fname. + +\param p is the pdb structure to write out. +\param fname is the file name to be written. +*/ +/************************************************************************/ +void gk_writealphacarbons(pdbf *p, char *fname) { + int i; + FILE *FPIN; + FPIN = gk_fopen(fname,"w",fname); + for(i=0; incas; i++) { + fprintf(FPIN,"%-6s%5d %4s%1c%3s %1c%4d%1c %8.3lf%8.3lf%8.3lf%6.2f%6.2f\n", + "ATOM ",p->cas[i]->serial,p->cas[i]->name,p->cas[i]->altLoc,p->cas[i]->resname,p->cas[i]->chainid,p->cas[i]->rserial,p->cas[i]->icode,p->cas[i]->x,p->cas[i]->y,p->cas[i]->z,p->cas[i]->opcy,p->cas[i]->tmpt); + } + fclose(FPIN); +} + +/************************************************************************/ +/*! \brief Decodes the corruption bitswitch and prints any problems + +Due to the totally unreliable nature of the pdb format, reading a pdb +file stores a corruption bitswitch, and this function decodes that switch +and prints the result on stdout. + +\param p is the pdb structure to write out. +\param fname is the file name to be written. +*/ +/************************************************************************/ +void gk_showcorruption(pdbf *p) { + int corruption = p->corruption; + if(corruption&CRP_ALTLOCS) + printf("Multiple coordinate sets for at least one atom\n"); + if(corruption&CRP_MISSINGCA) + printf("Missing coordiantes for at least one CA atom\n"); + if(corruption&CRP_MISSINGBB) + printf("Missing coordiantes for at least one backbone atom (N,CA,C,O)\n"); + if(corruption&CRP_MULTICHAIN) + printf("File contains coordinates for multiple chains\n"); + if(corruption&CRP_MULTICA) + printf("Multiple CA atoms found for the same residue (could be alternate locators)\n"); + if(corruption&CRP_MULTICA) + printf("Multiple copies of backbone atoms found for the same residue (could be alternate locators)\n"); +} + /* sscanf(line, "%6s%5d%*1c%4s%1c%3s%*1c%1c%4d%1c%*3c%8lf%8lf%8lf%6lf%6lf%*6c%4s%2s%2s\n", + linetype,&aserial,aname,&altLoc,rname,&chainid,&rserial,&icode,&x,&y,&z,&opcy,&tmpt,segId,element,charge); + printf(".%s.%s.%s.\n",segId,element,charge); + printf("%-6s%5d%-1s%-4s%1c%3s%1s%1c%4d%1c%3s%8.3lf%8.3lf%8.3lf%6.2f%6.2f%6s%4s%2s%2s\n", + linetype,aserial," ",aname,altLoc,rname," ",chainid,rserial,icode," ",x,y,z,opcy,tmpt," ",segId,element,charge); */ + + /* and we could probably get away with this using astral files, */ + /* sscanf(line, "%6s%5d%*1c%4s%1c%3s%*1c%1c%4d%1c%*3c%8lf%8lf%8lf%6lf%6lf%*6c%6s\n", + linetype,&aserial,aname,&altLoc,rname,&chainid,&rserial,&icode,&x,&y,&z,&opcy,&tmpt,element); + printf("%-6s%5d%-1s%-4s%1c%3s%1s%1c%4d%1c%3s%8.3lf%8.3lf%8.3lf%6.2f%6.2f%6s%6s\n", + linetype,aserial," ",aname,altLoc,rname," ",chainid,rserial,icode," ",x,y,z,opcy,tmpt," ",element); */ diff --git a/src/metis/GKlib/pqueue.c b/src/metis/GKlib/pqueue.c new file mode 100644 index 0000000..2fb8515 --- /dev/null +++ b/src/metis/GKlib/pqueue.c @@ -0,0 +1,25 @@ +/*! +\file pqueue.c +\brief This file implements various max-priority queues. + +The priority queues are generated using the GK_MKPQUEUE macro. + +\date Started 3/27/2007 +\author George +\version\verbatim $Id: pqueue.c 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + +#include + + +/*************************************************************************/ +/*! Create the various max priority queues */ +/*************************************************************************/ +#define key_gt(a, b) ((a) > (b)) +GK_MKPQUEUE(gk_ipq, gk_ipq_t, gk_ikv_t, int, gk_idx_t, gk_ikvmalloc, INT_MAX, key_gt) +GK_MKPQUEUE(gk_i32pq, gk_i32pq_t, gk_i32kv_t, int32_t, gk_idx_t, gk_i32kvmalloc, INT32_MAX, key_gt) +GK_MKPQUEUE(gk_i64pq, gk_i64pq_t, gk_i64kv_t, int64_t, gk_idx_t, gk_i64kvmalloc, INT64_MAX, key_gt) +GK_MKPQUEUE(gk_fpq, gk_fpq_t, gk_fkv_t, float, gk_idx_t, gk_fkvmalloc, FLT_MAX, key_gt) +GK_MKPQUEUE(gk_dpq, gk_dpq_t, gk_dkv_t, double, gk_idx_t, gk_dkvmalloc, DBL_MAX, key_gt) +GK_MKPQUEUE(gk_idxpq, gk_idxpq_t, gk_idxkv_t, gk_idx_t, gk_idx_t, gk_idxkvmalloc, GK_IDX_MAX, key_gt) +#undef key_gt diff --git a/src/metis/GKlib/random.c b/src/metis/GKlib/random.c new file mode 100644 index 0000000..d18e718 --- /dev/null +++ b/src/metis/GKlib/random.c @@ -0,0 +1,134 @@ +/*! +\file +\brief Various routines for providing portable 32 and 64 bit random number + generators. + +\date Started 5/17/2007 +\author George +\version\verbatim $Id: random.c 11793 2012-04-04 21:03:02Z karypis $ \endverbatim +*/ + +#include + + +/*************************************************************************/ +/*! Create the various random number functions */ +/*************************************************************************/ +GK_MKRANDOM(gk_c, size_t, char) +GK_MKRANDOM(gk_i, size_t, int) +GK_MKRANDOM(gk_f, size_t, float) +GK_MKRANDOM(gk_d, size_t, double) +GK_MKRANDOM(gk_idx, size_t, gk_idx_t) +GK_MKRANDOM(gk_z, size_t, ssize_t) + + + +/*************************************************************************/ +/*! GKlib's built in random number generator for portability across + different architectures */ +/*************************************************************************/ +#ifdef USE_GKRAND +/* + A C-program for MT19937-64 (2004/9/29 version). + Coded by Takuji Nishimura and Makoto Matsumoto. + + This is a 64-bit version of Mersenne Twister pseudorandom number + generator. + + Before using, initialize the state by using init_genrand64(seed) + or init_by_array64(init_key, key_length). + + Copyright (C) 2004, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define NN 312 +#define MM 156 +#define MATRIX_A 0xB5026F5AA96619E9ULL +#define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */ +#define LM 0x7FFFFFFFULL /* Least significant 31 bits */ + + +/* The array for the state vector */ +static uint64_t mt[NN]; +/* mti==NN+1 means mt[NN] is not initialized */ +static int mti=NN+1; +#endif /* USE_GKRAND */ + +/* initializes mt[NN] with a seed */ +void gk_randinit(uint64_t seed) +{ +#ifdef USE_GKRAND + mt[0] = seed; + for (mti=1; mti> 62)) + mti); +#else + srand((unsigned int) seed); +#endif +} + + +/* generates a random number on [0, 2^64-1]-interval */ +uint64_t gk_randint64(void) +{ +#ifdef USE_GKRAND + int i; + unsigned long long x; + static uint64_t mag01[2]={0ULL, MATRIX_A}; + + if (mti >= NN) { /* generate NN words at one time */ + /* if init_genrand64() has not been called, */ + /* a default initial seed is used */ + if (mti == NN+1) + gk_randinit(5489ULL); + + for (i=0; i>1) ^ mag01[(int)(x&1ULL)]; + } + for (; i>1) ^ mag01[(int)(x&1ULL)]; + } + x = (mt[NN-1]&UM)|(mt[0]&LM); + mt[NN-1] = mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; + + mti = 0; + } + + x = mt[mti++]; + + x ^= (x >> 29) & 0x5555555555555555ULL; + x ^= (x << 17) & 0x71D67FFFEDA60000ULL; + x ^= (x << 37) & 0xFFF7EEE000000000ULL; + x ^= (x >> 43); + + return x & 0x7FFFFFFFFFFFFFFF; +#else + return (uint64_t)(((uint64_t) rand()) << 32 | ((uint64_t) rand())); +#endif +} + +/* generates a random number on [0, 2^32-1]-interval */ +uint32_t gk_randint32(void) +{ +#ifdef USE_GKRAND + return (uint32_t)(gk_randint64() & 0x7FFFFFFF); +#else + return (uint32_t)rand(); +#endif +} + + diff --git a/src/metis/GKlib/rw.c b/src/metis/GKlib/rw.c new file mode 100644 index 0000000..7cd4391 --- /dev/null +++ b/src/metis/GKlib/rw.c @@ -0,0 +1,103 @@ +/*! + * \file + * + * \brief Various routines that perform random-walk based operations + on graphs stored as gk_csr_t matrices. + * + * \author George Karypis + * \version\verbatim $Id: rw.c 11078 2011-11-12 00:20:44Z karypis $ \endverbatim + */ + +#include + + +/*************************************************************************/ +/*! Computes the (personalized) page-rank of the vertices in a graph. + + \param mat is the matrix storing the graph. + \param lamda is the restart probability. + \param eps is the error tolerance for convergance. + \param max_niter is the maximum number of allowed iterations. + \param pr on entry stores the restart distribution of the vertices. + This allows for the computation of personalized page-rank scores + by appropriately setting that parameter. + On return, pr stores the computed page ranks. + + \returns the number of iterations that were performed. +*/ +/**************************************************************************/ +int gk_rw_PageRank(gk_csr_t *mat, float lamda, float eps, int max_niter, float *pr) +{ + ssize_t i, j, k, iter, nrows; + double *rscale, *prold, *prnew, *prtmp; + double fromsinks, error; + ssize_t *rowptr; + int *rowind; + float *rowval; + + nrows = mat->nrows; + rowptr = mat->rowptr; + rowind = mat->rowind; + rowval = mat->rowval; + + prold = gk_dsmalloc(nrows, 0, "gk_rw_PageRank: prnew"); + prnew = gk_dsmalloc(nrows, 0, "gk_rw_PageRank: prold"); + rscale = gk_dsmalloc(nrows, 0, "gk_rw_PageRank: rscale"); + + /* compute the scaling factors to get adjacency weights into transition + probabilities */ + for (i=0; i 0) + rscale[i] = 1.0/rscale[i]; + } + + /* the restart distribution is the initial pr scores */ + for (i=0; i error ? fabs(prnew[i]-prold[i]) : error); + + //printf("nrm1: %le maxfabserr: %le\n", gk_dsum(nrows, prnew, 1), error); + + if (error < eps) + break; + } + + /* store the computed pr scores into pr for output */ + for (i=0; i + + + + +/*********************************************************/ +/* ! \brief Initializes the gk_seq_t variable + + + + +\param A pointer to gk_seq_t itself +\returns null +*/ +/***********************************************************************/ + +void gk_seq_init(gk_seq_t *seq) +{ + + seq->len = 0; + seq->sequence = NULL; + + seq->pssm = NULL; + seq->psfm = NULL; + + seq->name = NULL; + +} + +/***********************************************************************/ +/*! \brief This function creates the localizations for the various sequences + +\param string i.e amino acids, nucleotides, sequences +\returns gk_i2cc2i_t variable +*/ +/*********************************************************************/ + +gk_i2cc2i_t *gk_i2cc2i_create_common(char *alphabet) +{ + + + int nsymbols; + gk_idx_t i; + gk_i2cc2i_t *t; + + nsymbols = strlen(alphabet); + t = gk_malloc(sizeof(gk_i2cc2i_t),"gk_i2c_create_common"); + t->n = nsymbols; + t->i2c = gk_cmalloc(256, "gk_i2c_create_common"); + t->c2i = gk_imalloc(256, "gk_i2c_create_common"); + + + gk_cset(256, -1, t->i2c); + gk_iset(256, -1, t->c2i); + + for(i=0;ii2c[i] = alphabet[i]; + t->c2i[(int)alphabet[i]] = i; + } + + return t; + +} + + +/*********************************************************************/ +/*! \brief This function reads a pssm in the format of gkmod pssm + +\param file_name is the name of the pssm file +\returns gk_seq_t +*/ +/********************************************************************/ +gk_seq_t *gk_seq_ReadGKMODPSSM(char *filename) +{ + gk_seq_t *seq; + gk_idx_t i, j, ii; + size_t ntokens, nbytes, len; + FILE *fpin; + + + gk_Tokens_t tokens; + static char *AAORDER = "ARNDCQEGHILKMFPSTWYVBZX*"; + static int PSSMWIDTH = 20; + char *header, line[MAXLINELEN]; + gk_i2cc2i_t *converter; + + header = gk_cmalloc(PSSMWIDTH, "gk_seq_ReadGKMODPSSM: header"); + + converter = gk_i2cc2i_create_common(AAORDER); + + gk_getfilestats(filename, &len, &ntokens, NULL, &nbytes); + len --; + + seq = gk_malloc(sizeof(gk_seq_t),"gk_seq_ReadGKMODPSSM"); + gk_seq_init(seq); + + seq->len = len; + seq->sequence = gk_imalloc(len, "gk_seq_ReadGKMODPSSM"); + seq->pssm = gk_iAllocMatrix(len, PSSMWIDTH, 0, "gk_seq_ReadGKMODPSSM"); + seq->psfm = gk_iAllocMatrix(len, PSSMWIDTH, 0, "gk_seq_ReadGKMODPSSM"); + + seq->nsymbols = PSSMWIDTH; + seq->name = gk_getbasename(filename); + + fpin = gk_fopen(filename,"r","gk_seq_ReadGKMODPSSM"); + + + /* Read the header line */ + if (fgets(line, MAXLINELEN-1, fpin) == NULL) + errexit("Unexpected end of file: %s\n", filename); + gk_strtoupper(line); + gk_strtokenize(line, " \t\n", &tokens); + + for (i=0; isequence[i] = converter->c2i[(int)tokens.list[1][0]]; + + for (j=0; jpssm[i][converter->c2i[(int)header[j]]] = atoi(tokens.list[2+j]); + seq->psfm[i][converter->c2i[(int)header[j]]] = atoi(tokens.list[2+PSSMWIDTH+j]); + } + + + + gk_freetokenslist(&tokens); + i++; + } + + seq->len = i; /* Reset the length if certain characters were skipped */ + + gk_free((void **)&header, LTERM); + gk_fclose(fpin); + + return seq; +} + + +/**************************************************************************/ +/*! \brief This function frees the memory allocated to the seq structure. + +\param gk_seq_t +\returns nothing +*/ +/**************************************************************************/ +void gk_seq_free(gk_seq_t *seq) +{ + gk_iFreeMatrix(&seq->pssm, seq->len, seq->nsymbols); + gk_iFreeMatrix(&seq->psfm, seq->len, seq->nsymbols); + gk_free((void **)&seq->name, &seq->sequence, LTERM); + //gk_free((void **)&seq, LTERM); + gk_free((void **) &seq, LTERM); + +} diff --git a/src/metis/GKlib/sort.c b/src/metis/GKlib/sort.c new file mode 100644 index 0000000..bde30f5 --- /dev/null +++ b/src/metis/GKlib/sort.c @@ -0,0 +1,327 @@ +/*! +\file sort.c +\brief This file contains GKlib's various sorting routines + +These routines are implemented using the GKSORT macro that is defined +in gk_qsort.h and is based on GNU's GLIBC qsort() implementation. + +Additional sorting routines can be created using the same way that +these routines where defined. + +\date Started 4/4/07 +\author George +\version\verbatim $Id: sort.c 10796 2011-09-23 21:33:09Z karypis $ \endverbatim +*/ + +#include + + + +/*************************************************************************/ +/*! Sorts an array of chars in increasing order */ +/*************************************************************************/ +void gk_csorti(size_t n, char *base) +{ +#define char_lt(a, b) ((*a) < (*b)) + GK_MKQSORT(char, base, n, char_lt); +#undef char_lt +} + + +/*************************************************************************/ +/*! Sorts an array of chars in decreasing order */ +/*************************************************************************/ +void gk_csortd(size_t n, char *base) +{ +#define char_gt(a, b) ((*a) > (*b)) + GK_MKQSORT(char, base, n, char_gt); +#undef char_gt +} + + +/*************************************************************************/ +/*! Sorts an array of integers in increasing order */ +/*************************************************************************/ +void gk_isorti(size_t n, int *base) +{ +#define int_lt(a, b) ((*a) < (*b)) + GK_MKQSORT(int, base, n, int_lt); +#undef int_lt +} + + +/*************************************************************************/ +/*! Sorts an array of integers in decreasing order */ +/*************************************************************************/ +void gk_isortd(size_t n, int *base) +{ +#define int_gt(a, b) ((*a) > (*b)) + GK_MKQSORT(int, base, n, int_gt); +#undef int_gt +} + + +/*************************************************************************/ +/*! Sorts an array of floats in increasing order */ +/*************************************************************************/ +void gk_fsorti(size_t n, float *base) +{ +#define float_lt(a, b) ((*a) < (*b)) + GK_MKQSORT(float, base, n, float_lt); +#undef float_lt +} + + +/*************************************************************************/ +/*! Sorts an array of floats in decreasing order */ +/*************************************************************************/ +void gk_fsortd(size_t n, float *base) +{ +#define float_gt(a, b) ((*a) > (*b)) + GK_MKQSORT(float, base, n, float_gt); +#undef float_gt +} + + +/*************************************************************************/ +/*! Sorts an array of doubles in increasing order */ +/*************************************************************************/ +void gk_dsorti(size_t n, double *base) +{ +#define double_lt(a, b) ((*a) < (*b)) + GK_MKQSORT(double, base, n, double_lt); +#undef double_lt +} + + +/*************************************************************************/ +/*! Sorts an array of doubles in decreasing order */ +/*************************************************************************/ +void gk_dsortd(size_t n, double *base) +{ +#define double_gt(a, b) ((*a) > (*b)) + GK_MKQSORT(double, base, n, double_gt); +#undef double_gt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_idx_t in increasing order */ +/*************************************************************************/ +void gk_idxsorti(size_t n, gk_idx_t *base) +{ +#define idx_lt(a, b) ((*a) < (*b)) + GK_MKQSORT(gk_idx_t, base, n, idx_lt); +#undef idx_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_idx_t in decreasing order */ +/*************************************************************************/ +void gk_idxsortd(size_t n, gk_idx_t *base) +{ +#define idx_gt(a, b) ((*a) > (*b)) + GK_MKQSORT(gk_idx_t, base, n, idx_gt); +#undef idx_gt +} + + + + +/*************************************************************************/ +/*! Sorts an array of gk_ckv_t in increasing order */ +/*************************************************************************/ +void gk_ckvsorti(size_t n, gk_ckv_t *base) +{ +#define ckey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(gk_ckv_t, base, n, ckey_lt); +#undef ckey_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_ckv_t in decreasing order */ +/*************************************************************************/ +void gk_ckvsortd(size_t n, gk_ckv_t *base) +{ +#define ckey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(gk_ckv_t, base, n, ckey_gt); +#undef ckey_gt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_ikv_t in increasing order */ +/*************************************************************************/ +void gk_ikvsorti(size_t n, gk_ikv_t *base) +{ +#define ikey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(gk_ikv_t, base, n, ikey_lt); +#undef ikey_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_ikv_t in decreasing order */ +/*************************************************************************/ +void gk_ikvsortd(size_t n, gk_ikv_t *base) +{ +#define ikey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(gk_ikv_t, base, n, ikey_gt); +#undef ikey_gt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_i32kv_t in increasing order */ +/*************************************************************************/ +void gk_i32kvsorti(size_t n, gk_i32kv_t *base) +{ +#define ikey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(gk_i32kv_t, base, n, ikey_lt); +#undef ikey_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_i32kv_t in decreasing order */ +/*************************************************************************/ +void gk_i32kvsortd(size_t n, gk_i32kv_t *base) +{ +#define ikey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(gk_i32kv_t, base, n, ikey_gt); +#undef ikey_gt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_i64kv_t in increasing order */ +/*************************************************************************/ +void gk_i64kvsorti(size_t n, gk_i64kv_t *base) +{ +#define ikey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(gk_i64kv_t, base, n, ikey_lt); +#undef ikey_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_i64kv_t in decreasing order */ +/*************************************************************************/ +void gk_i64kvsortd(size_t n, gk_i64kv_t *base) +{ +#define ikey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(gk_i64kv_t, base, n, ikey_gt); +#undef ikey_gt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_zkv_t in increasing order */ +/*************************************************************************/ +void gk_zkvsorti(size_t n, gk_zkv_t *base) +{ +#define zkey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(gk_zkv_t, base, n, zkey_lt); +#undef zkey_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_zkv_t in decreasing order */ +/*************************************************************************/ +void gk_zkvsortd(size_t n, gk_zkv_t *base) +{ +#define zkey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(gk_zkv_t, base, n, zkey_gt); +#undef zkey_gt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_fkv_t in increasing order */ +/*************************************************************************/ +void gk_fkvsorti(size_t n, gk_fkv_t *base) +{ +#define fkey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(gk_fkv_t, base, n, fkey_lt); +#undef fkey_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_fkv_t in decreasing order */ +/*************************************************************************/ +void gk_fkvsortd(size_t n, gk_fkv_t *base) +{ +#define fkey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(gk_fkv_t, base, n, fkey_gt); +#undef fkey_gt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_dkv_t in increasing order */ +/*************************************************************************/ +void gk_dkvsorti(size_t n, gk_dkv_t *base) +{ +#define dkey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(gk_dkv_t, base, n, dkey_lt); +#undef dkey_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_fkv_t in decreasing order */ +/*************************************************************************/ +void gk_dkvsortd(size_t n, gk_dkv_t *base) +{ +#define dkey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(gk_dkv_t, base, n, dkey_gt); +#undef dkey_gt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_skv_t in increasing order */ +/*************************************************************************/ +void gk_skvsorti(size_t n, gk_skv_t *base) +{ +#define skey_lt(a, b) (strcmp((a)->key, (b)->key) < 0) + GK_MKQSORT(gk_skv_t, base, n, skey_lt); +#undef skey_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_skv_t in decreasing order */ +/*************************************************************************/ +void gk_skvsortd(size_t n, gk_skv_t *base) +{ +#define skey_gt(a, b) (strcmp((a)->key, (b)->key) > 0) + GK_MKQSORT(gk_skv_t, base, n, skey_gt); +#undef skey_gt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_idxkv_t in increasing order */ +/*************************************************************************/ +void gk_idxkvsorti(size_t n, gk_idxkv_t *base) +{ +#define idxkey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(gk_idxkv_t, base, n, idxkey_lt); +#undef idxkey_lt +} + + +/*************************************************************************/ +/*! Sorts an array of gk_idxkv_t in decreasing order */ +/*************************************************************************/ +void gk_idxkvsortd(size_t n, gk_idxkv_t *base) +{ +#define idxkey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(gk_idxkv_t, base, n, idxkey_gt); +#undef idxkey_gt +} diff --git a/src/metis/GKlib/string.c b/src/metis/GKlib/string.c new file mode 100644 index 0000000..5d28452 --- /dev/null +++ b/src/metis/GKlib/string.c @@ -0,0 +1,529 @@ +/************************************************************************/ +/*! \file + +\brief Functions for manipulating strings. + +Various functions for manipulating strings. Some of these functions +provide new functionality, whereas others are drop-in replacements +of standard functions (but with enhanced functionality). + +\date Started 11/1/99 +\author George +\version $Id: string.c 10711 2011-08-31 22:23:04Z karypis $ +*/ +/************************************************************************/ + +#include + + + +/************************************************************************/ +/*! \brief Replaces certain characters in a string. + +This function takes a string and replaces all the characters in the +\c fromlist with the corresponding characters from the \c tolist. +That is, each occurence of fromlist[i] is replaced by +tolist[i]. +If the \c tolist is shorter than \c fromlist, then the corresponding +characters are deleted. The modifications on \c str are done in place. +It tries to provide a functionality similar to Perl's \b tr// function. + +\param str is the string whose characters will be replaced. +\param fromlist is the set of characters to be replaced. +\param tolist is the set of replacement characters . +\returns A pointer to \c str itself. +*/ +/************************************************************************/ +char *gk_strchr_replace(char *str, char *fromlist, char *tolist) +{ + gk_idx_t i, j, k; + size_t len, fromlen, tolen; + + len = strlen(str); + fromlen = strlen(fromlist); + tolen = strlen(tolist); + + for (i=j=0; i s// regular-expression +based substitution function. + +\param str + is the input string on which the operation will be performed. +\param pattern + is the regular expression for the pattern to be matched for substitution. +\param replacement + is the replacement string, in which the possible captured pattern substrings + are referred to as $1, $2, ..., $9. The entire matched pattern is refered + to as $0. +\param options + is a string specified options for the substitution operation. Currently the + "i" (case insensitive) and "g" (global substitution) are + supported. +\param new_str + is a reference to a pointer that will store a pointer to the newly created + string that results from the substitutions. This string is allocated via + gk_malloc() and needs to be freed using gk_free(). The string is returned + even if no substitutions were performed. +\returns + If successful, it returns 1 + the number of substitutions that were performed. + Thus, if no substitutions were performed, the returned value will be 1. + Otherwise it returns 0. In case of error, a meaningful error message is + returned in newstr, which also needs to be freed afterwards. +*/ +/************************************************************************/ +int gk_strstr_replace(char *str, char *pattern, char *replacement, char *options, + char **new_str) +{ + gk_idx_t i; + int j, rc, flags, global, nmatches; + size_t len, rlen, nlen, offset, noffset; + regex_t re; + regmatch_t matches[10]; + + + /* Parse the options */ + flags = REG_EXTENDED; + if (strchr(options, 'i') != NULL) + flags = flags | REG_ICASE; + global = (strchr(options, 'g') != NULL ? 1 : 0); + + + /* Compile the regex */ + if ((rc = regcomp(&re, pattern, flags)) != 0) { + len = regerror(rc, &re, NULL, 0); + *new_str = gk_cmalloc(len, "gk_strstr_replace: new_str"); + regerror(rc, &re, *new_str, len); + return 0; + } + + /* Prepare the output string */ + len = strlen(str); + nlen = 2*len; + noffset = 0; + *new_str = gk_cmalloc(nlen+1, "gk_strstr_replace: new_str"); + + + /* Get into the matching-replacing loop */ + rlen = strlen(replacement); + offset = 0; + nmatches = 0; + do { + rc = regexec(&re, str+offset, 10, matches, 0); + + if (rc == REG_ESPACE) { + gk_free((void **)new_str, LTERM); + *new_str = gk_strdup("regexec ran out of memory."); + regfree(&re); + return 0; + } + else if (rc == REG_NOMATCH) { + if (nlen-noffset < len-offset) { + nlen += (len-offset) - (nlen-noffset); + *new_str = (char *)gk_realloc(*new_str, (nlen+1)*sizeof(char), "gk_strstr_replace: new_str"); + } + strcpy(*new_str+noffset, str+offset); + noffset += (len-offset); + break; + } + else { /* A match was found! */ + nmatches++; + + /* Copy the left unmatched portion of the string */ + if (matches[0].rm_so > 0) { + if (nlen-noffset < matches[0].rm_so) { + nlen += matches[0].rm_so - (nlen-noffset); + *new_str = (char *)gk_realloc(*new_str, (nlen+1)*sizeof(char), "gk_strstr_replace: new_str"); + } + strncpy(*new_str+noffset, str+offset, matches[0].rm_so); + noffset += matches[0].rm_so; + } + + /* Go and append the replacement string */ + for (i=0; i 9) { + gk_free((void **)new_str, LTERM); + *new_str = gk_strdup("Error in captured subexpression specification."); + regfree(&re); + return 0; + } + + if (nlen-noffset < matches[j].rm_eo-matches[j].rm_so) { + nlen += nlen + (matches[j].rm_eo-matches[j].rm_so); + *new_str = (char *)gk_realloc(*new_str, (nlen+1)*sizeof(char), "gk_strstr_replace: new_str"); + } + + strncpy(*new_str+noffset, str+offset+matches[j].rm_so, matches[j].rm_eo); + noffset += matches[j].rm_eo-matches[j].rm_so; + } + else { + gk_free((void **)new_str, LTERM); + *new_str = gk_strdup("Error in replacement string. Missing subexpression number folloing '$'."); + regfree(&re); + return 0; + } + break; + + default: + if (nlen-noffset < 1) { + nlen += nlen + 1; + *new_str = (char *)gk_realloc(*new_str, (nlen+1)*sizeof(char), "gk_strstr_replace: new_str"); + } + (*new_str)[noffset++] = replacement[i]; + } + } + + /* Update the offset of str for the next match */ + offset += matches[0].rm_eo; + + if (!global) { + /* Copy the right portion of the string if no 'g' option */ + if (nlen-noffset < len-offset) { + nlen += (len-offset) - (nlen-noffset); + *new_str = (char *)gk_realloc(*new_str, (nlen+1)*sizeof(char), "gk_strstr_replace: new_str"); + } + strcpy(*new_str+noffset, str+offset); + noffset += (len-offset); + } + } + } while (global); + + (*new_str)[noffset] = '\0'; + + regfree(&re); + return nmatches + 1; + +} + + + +/************************************************************************/ +/*! \brief Prunes characters from the end of the string. + +This function removes any trailing characters that are included in the +\c rmlist. The trimming stops at the last character (i.e., first character +from the end) that is not in \c rmlist. +This function can be used to removed trailing spaces, newlines, etc. +This is a distructive operation as it modifies the string. + +\param str is the string that will be trimmed. +\param rmlist contains the set of characters that will be removed. +\returns A pointer to \c str itself. +\sa gk_strhprune() +*/ +/*************************************************************************/ +char *gk_strtprune(char *str, char *rmlist) +{ + gk_idx_t i, j; + size_t len; + + len = strlen(rmlist); + + for (i=strlen(str)-1; i>=0; i--) { + for (j=0; j0) { /* If something needs to be removed */ + for (j=0; str[i]; i++, j++) + str[j] = str[i]; + str[j] = '\0'; + } + + return str; +} + + +/************************************************************************/ +/*! \brief Converts a string to upper case. + +This function converts a string to upper case. This operation modifies the +string itself. + +\param str is the string whose case will be changed. +\returns A pointer to \c str itself. +\sa gk_strtolower() +*/ +/*************************************************************************/ +char *gk_strtoupper(char *str) +{ + int i; + + for (i=0; str[i]!='\0'; str[i]=toupper(str[i]), i++); + return str; +} + + +/************************************************************************/ +/*! \brief Converts a string to lower case. + +This function converts a string to lower case. This operation modifies the +string itself. + +\param str is the string whose case will be changed. +\returns A pointer to \c str itself. +\sa gk_strtoupper() +*/ +/*************************************************************************/ +char *gk_strtolower(char *str) +{ + int i; + + for (i=0; str[i]!='\0'; str[i]=tolower(str[i]), i++); + return str; +} + + +/************************************************************************/ +/*! \brief Duplicates a string + +This function is a replacement for C's standard strdup() function. +The key differences between the two are that gk_strdup(): + - uses the dynamic memory allocation routines of \e GKlib. + - it correctly handles NULL input strings. + +The string that is returned must be freed by gk_free(). + +\param orgstr is the string that will be duplicated. +\returns A pointer to the newly created string. +\sa gk_free() +*/ +/*************************************************************************/ +char *gk_strdup(char *orgstr) +{ + int len; + char *str=NULL; + + if (orgstr != NULL) { + len = strlen(orgstr)+1; + str = gk_malloc(len*sizeof(char), "gk_strdup: str"); + strcpy(str, orgstr); + } + + return str; +} + + +/************************************************************************/ +/*! \brief Case insensitive string comparison. + +This function compares two strings for equality by ignoring the case of the +strings. + +\warning This function is \b not equivalent to a case-insensitive + strcmp() function, as it does not return ordering + information. + +\todo Remove the above warning. + +\param s1 is the first string to be compared. +\param s2 is the second string to be compared. +\retval 1 if the strings are identical, +\retval 0 otherwise. +*/ +/*************************************************************************/ +int gk_strcasecmp(char *s1, char *s2) +{ + int i=0; + + if (strlen(s1) != strlen(s2)) + return 0; + + while (s1[i] != '\0') { + if (tolower(s1[i]) != tolower(s2[i])) + return 0; + i++; + } + + return 1; +} + + +/************************************************************************/ +/*! \brief Compare two strings in revere order + +This function is similar to strcmp but it performs the comparison as +if the two strings were reversed. + +\param s1 is the first string to be compared. +\param s2 is the second string to be compared. +\retval -1, 0, 1, if the s1 < s2, s1 == s2, or s1 > s2. +*/ +/*************************************************************************/ +int gk_strrcmp(char *s1, char *s2) +{ + int i1 = strlen(s1)-1; + int i2 = strlen(s2)-1; + + while ((i1 >= 0) && (i2 >= 0)) { + if (s1[i1] != s2[i2]) + return (s1[i1] - s2[i2]); + i1--; + i2--; + } + + /* i1 == -1 and/or i2 == -1 */ + + if (i1 < i2) + return -1; + if (i1 > i2) + return 1; + return 0; +} + + + +/************************************************************************/ +/*! \brief Converts a time_t time into a string + +This function takes a time_t-specified time and returns a string-formated +representation of the corresponding time. The format of the string is +mm/dd/yyyy hh:mm:ss, in which the hours are in military time. + +\param time is the time to be converted. +\return It returns a pointer to a statically allocated string that is + over-written in successive calls of this function. If the + conversion failed, it returns NULL. + +*/ +/*************************************************************************/ +char *gk_time2str(time_t time) +{ + static char datestr[128]; + struct tm *tm; + + tm = localtime(&time); + + if (strftime(datestr, 128, "%m/%d/%Y %H:%M:%S", tm) == 0) + return NULL; + else + return datestr; +} + + + +#if !defined(WIN32) && !defined(__MINGW32__) +/************************************************************************/ +/*! \brief Converts a date/time string into its equivalent time_t value + +This function takes date and/or time specification and converts it in +the equivalent time_t representation. The conversion is done using the +strptime() function. The format that gk_str2time() understands is +mm/dd/yyyy hh:mm:ss, in which the hours are in military time. + +\param str is the date/time string to be converted. +\return If the conversion was successful it returns the time, otherwise + it returns -1. +*/ +/*************************************************************************/ +time_t gk_str2time(char *str) +{ + struct tm time; + time_t rtime; + + memset(&time, '\0', sizeof(time)); + + if (strptime(str, "%m/%d/%Y %H:%M:%S", &time) == NULL) + return -1; + + rtime = mktime(&time); + return (rtime < 0 ? 0 : rtime); +} +#endif + + +/************************************************************************* +* This function returns the ID of a particular string based on the +* supplied StringMap array +**************************************************************************/ +int gk_GetStringID(gk_StringMap_t *strmap, char *key) +{ + int i; + + for (i=0; strmap[i].name; i++) { + if (gk_strcasecmp(key, strmap[i].name)) + return strmap[i].id; + } + + return -1; +} diff --git a/src/metis/GKlib/test/CMakeLists.txt b/src/metis/GKlib/test/CMakeLists.txt new file mode 100644 index 0000000..372b0e2 --- /dev/null +++ b/src/metis/GKlib/test/CMakeLists.txt @@ -0,0 +1,13 @@ +# Where the header files reside +#include_directories(../) + +# Build program. +add_executable(strings strings.c) +add_executable(gksort gksort.c) +add_executable(fis fis.c) +add_executable(rw rw.c) +add_executable(gkgraph gkgraph.c) +foreach(prog strings gksort fis rw gkgraph) + target_link_libraries(${prog} GKlib) +endforeach(prog) + diff --git a/src/metis/GKlib/test/Makefile.in.old b/src/metis/GKlib/test/Makefile.in.old new file mode 100644 index 0000000..cac4f52 --- /dev/null +++ b/src/metis/GKlib/test/Makefile.in.old @@ -0,0 +1,258 @@ +#************************************************************************* +# Global flags +#************************************************************************* +gdb = yes +debug = no +memdbg = no +openmp = no +x86compiler = gcc + +VERNUM = 0.1.0 + + + +#************************************************************************* +# System-specific compilation flags +#************************************************************************* +# Get some basic information about the system that you are working on +cputype = $(shell uname -m | sed "s/\\ /_/g") +systype = $(shell uname) +ifeq ($(findstring CYGWIN, $(systype)),CYGWIN) +# systype = CYGWIN + systype = MSWIN + cputype = x86 +endif + + +GKLIBINCDIR = $(HOME)/work/algorithms/GKlib/trunk/ +GKLIBBUILDDIR = $(HOME)/work/algorithms/GKlib/builds/$(systype)-$(cputype) + + +ifeq ($(systype),MSWIN) + #------------------------------------------------------------------- + # These defs are very much Visual Studio Specific + #------------------------------------------------------------------- + #Compiler information + CC = cl + OPTFLAGS = /Ox + COPTIONS = -DWIN32 -DMSC -D_CRT_SECURE_NO_DEPRECATE + + #Compile input/output file specification + SOURCEFILE = /c $< + OUTPUTFILE = /Fo$@ + + #Output specification for executables + EXEOUTPUTFILE = /Fe$@ # This option is when cl is used for linking + #EXEOUTPUTFILE = /OUT:$@ # This option is used when link is used for linking + + #Linker information + LDOPTIONS = /MT + #LD = /cygdrive/c/Program\ Files/Microsoft\ Visual\ Studio\ 8/VC/BIN/link + LD = cl + MERGEMANIFEST = + + #Library creation information + AR = lib /OUT:$@ + RANLIB = + + ifeq ($(openmp),yes) + COPTIONS += -D__OPENMP__ /openmp + LDOPTIONS += /openmp + MERGEMANIFEST = vc_mt -manifest $@.manifest -outputresource:$@\;1 + endif + + #Library information + ifeq ($(cputype),i386) + LIBPLOTDIR = ../Libplot/Win32 + else + LIBPLOTDIR = ../Libplot/Win64 + endif + LIBS = $(LIBPLOTDIR)/libplot.lib $(BUILDDIR)/libcluto.lib $(GKLIBBUILDDIR)/libGKlib.lib + + # Standard file extensions + OBJEXT = .obj + LIBEXT = .lib + EXEEXT = .exe +else + ifeq ($(systype),Linux) + ifeq ($(x86compiler),gcc) + #Compiler information + CC = gcc + OPTFLAGS = -O6 + COPTIONS = -DLINUX -D_FILE_OFFSET_BITS=64 -pedantic -std=c99 -pthread + + #Linker information + LDOPTIONS = + LD = gcc + + MERGEMANIFEST = + + #Library creation information + AR = ar rv + RANLIB = ar -ts + else + #Compiler information + CC = icc + OPTFLAGS = -O3 + COPTIONS = -DLINUX -D_FILE_OFFSET_BITS=64 -std=c99 + + #Linker information + LDOPTIONS = + LD = icc + + #Library creation information + AR = ar rv + RANLIB = ar -ts + + ifeq ($(openmp),yes) + COPTIONS += -D__OPENMP__ -openmp -openmp-report2 + LDOPTIONS += -openmp + endif + endif + + #Library information + ifeq ($(cputype),x86_64) + LIBPLOTDIR = ../Libplot/Linux64 + else + LIBPLOTDIR = ../Libplot/Linux32 + endif + endif + + + ifeq ($(systype),SunOS) + #Compiler information + CC = /opt/SUNWspro/bin/cc + OPTFLAGS = -xO4 + COPTIONS =-DSUNOS + + #Linker information + LDOPTIONS = + LD = /opt/SUNWspro/bin/cc + + + #Library creation information + AR = ar rv + RANLIB = ar -ts + + #Library information + LIBPLOTDIR = ../Libplot/SunOS + endif + + + ifeq ($(systype),Darwin) + #Compiler information + CC = gcc + OPTFLAGS = -O6 + COPTIONS = -DDARWIN -D_FILE_OFFSET_BITS=64 -pedantic -std=c99 + + #Linker information + LDOPTIONS = -fvisibility=default + LD = gcc + + #Library creation information + AR = ar rv + RANLIB = ar -ts + + #Library information + ifeq ($(cputype),i386) + LIBPLOTDIR = ../Libplot/Darwini386 + else + LIBPLOTDIR = ../Libplot/DarwinPPC + endif + endif + + ifeq ($(systype),CYGWIN) + #Compiler information + CC = gcc + OPTFLAGS = -O6 + COPTIONS = -DCYGWIN -DWIN32 -D_FILE_OFFSET_BITS=64 -Wall -std=c99 -pedantic -mno-cygwin + + #Linker information + LDOPTIONS = -mno-cygwin + LD = gcc + + #Library creation information + AR = ar crv + RANLIB = ar -ts + + #Library information + LIBPLOTDIR = ../Libplot/CYGWIN + endif + + + #------------------------------------------------------------------- + # These defs are common among the GNU/GCC based systems + #------------------------------------------------------------------- + #Compile input/output file specification + SOURCEFILE = -c $< + OUTPUTFILE = -o $@ + + #Output specification for executables + EXEOUTPUTFILE = -o $@ + + #Library creation information + AR = ar crv $@ + RANLIB = ar -ts $@ + + #Libraries needed for linking + LIBSDIR = -L$(BUILDDIR) -L$(GKLIBBUILDDIR) -L$(HOME)/local/lib + LIBS = -lGKlib -lpcreposix -lpcre -lz -lm + + # Standard file extensions + OBJEXT = .o + LIBEXT = .a + EXEEXT = +endif + + +#************************************************************************** +DMALLOCINC = +DMALLOCFLAGS = +DEBUGFLAGS = + +ifeq ($(dmalloc),yes) + DMALLOCINC = -I$(HOME)/local/include + DMALLOCFLAGS = -DDMALLOC + OPTFLAGS = -g +endif + +ifeq ($(debug),yes) + DEBUGFLAGS = -DDEBUG + OPTFLAGS = -g +endif + +ifeq ($(gdb),yes) + OPTFLAGS += -g +endif +#************************************************************************** + + +#************************************************************************** +# Create the build directory if it does not exist +#************************************************************************** +ifeq ($(systype),Darwin) + BINDIR = $(HOME) +else + BINDIR = $(HOME)/work/bin/$(systype)-$(cputype) + $(shell mkdir -p $(BINDIR)) +endif + +ifeq ($(openmp),no) + BUILDDIR = ./builds/$(systype)-$(cputype) +else + BUILDDIR = ./builds/$(systype)-$(cputype)-openmp +endif + +LIBBUILDDIR = $(BUILDDIR)/lib +PRGBUILDDIR = $(BUILDDIR)/prg +$(shell mkdir -p $(BUILDDIR)) +$(shell mkdir -p $(LIBBUILDDIR)) +$(shell mkdir -p $(PRGBUILDDIR)) + + + + +INCLUDES = -I./ -I$(GKLIBINCDIR) -I$(LIBPLOTDIR) -I$(HOME)/local/include +CFLAGS = $(COPTIONS) $(OPTFLAGS) $(DEBUGFLAGS) $(INCLUDES) + + diff --git a/src/metis/GKlib/test/Makefile.old b/src/metis/GKlib/test/Makefile.old new file mode 100644 index 0000000..1ca357e --- /dev/null +++ b/src/metis/GKlib/test/Makefile.old @@ -0,0 +1,39 @@ +include Makefile.in + +STRINGSOBJS = $(PRGBUILDDIR)/strings$(OBJEXT) +GKSORTOBJS = $(PRGBUILDDIR)/gksort$(OBJEXT) +FISOBJS = $(PRGBUILDDIR)/fis$(OBJEXT) + +HEADERS = $(wildcard $(GKLIBINCDIR)/*.h) + + +default: $(BUILDDIR)/strings$(EXEEXT) $(BUILDDIR)/gksort$(EXEEXT) $(BUILDDIR)/fis$(EXEEXT) + + +$(BUILDDIR)/strings$(EXEEXT): $(STRINGSOBJS) $(GKLIBBUILDDIR)/libGKlib.a + $(LD) $(LDOPTIONS) $(EXEOUTPUTFILE) $(STRINGSOBJS) $(LIBSDIR) $(LIBS) ; $(MERGEMANIFEST) + chmod 744 $@ + +$(BUILDDIR)/gksort$(EXEEXT): $(GKSORTOBJS) $(GKLIBBUILDDIR)/libGKlib.a + $(LD) $(LDOPTIONS) $(EXEOUTPUTFILE) $(GKSORTOBJS) $(LIBSDIR) $(LIBS) ; $(MERGEMANIFEST) + chmod 744 $@ + +$(BUILDDIR)/fis$(EXEEXT): $(FISOBJS) $(GKLIBBUILDDIR)/libGKlib.a + $(LD) $(LDOPTIONS) $(EXEOUTPUTFILE) $(FISOBJS) $(LIBSDIR) $(LIBS) ; $(MERGEMANIFEST) + chmod 744 $@ + + +clean: + rm -rf $(PRGBUILDDIR) + +realclean: + rm -rf $(PRGBUILDDIR) ;\ + rm -rf $(BUILDDIR) ; + + +$(STRINGSOBJS) : $(HEADERS) Makefile.in Makefile $(GKLIBBUILDDIR)/libGKlib.a + + +$(PRGBUILDDIR)/%$(OBJEXT) : %.c + $(CC) $(CFLAGS) $(SOURCEFILE) $(OUTPUTFILE) + diff --git a/src/metis/GKlib/test/fis.c b/src/metis/GKlib/test/fis.c new file mode 100644 index 0000000..084a4b6 --- /dev/null +++ b/src/metis/GKlib/test/fis.c @@ -0,0 +1,286 @@ +/*! +\file +\brief A simple frequent itemset discovery program to test GKlib's routines + +\date 6/12/2008 +\author George +\version \verbatim $Id: fis.c 11075 2011-11-11 22:31:52Z karypis $ \endverbatim +*/ + +#include + +/*************************************************************************/ +/*! Data structures for the code */ +/*************************************************************************/ +typedef struct { + ssize_t minlen, maxlen; + ssize_t minfreq, maxfreq; + char *filename; + int silent; + ssize_t nitemsets; + char *clabelfile; + char **clabels; +} params_t; + +/*************************************************************************/ +/*! Constants */ +/*************************************************************************/ +#define CMD_MINLEN 1 +#define CMD_MAXLEN 2 +#define CMD_MINFREQ 3 +#define CMD_MAXFREQ 4 +#define CMD_SILENT 5 +#define CMD_CLABELFILE 6 +#define CMD_HELP 10 + + +/*************************************************************************/ +/*! Local variables */ +/*************************************************************************/ +static struct gk_option long_options[] = { + {"minlen", 1, 0, CMD_MINLEN}, + {"maxlen", 1, 0, CMD_MAXLEN}, + {"minfreq", 1, 0, CMD_MINFREQ}, + {"maxfreq", 1, 0, CMD_MAXFREQ}, + {"silent", 0, 0, CMD_SILENT}, + {"clabels", 1, 0, CMD_CLABELFILE}, + {"help", 0, 0, CMD_HELP}, + {0, 0, 0, 0} +}; + + +/*-------------------------------------------------------------------*/ +/* Mini help */ +/*-------------------------------------------------------------------*/ +static char helpstr[][100] = { +" ", +"Usage: fis [options] ", +" ", +" Required parameters", +" mat-file", +" The name of the file storing the transactions. The file is in ", +" Cluto's .mat format.", +" ", +" Optional parameters", +" -minlen=int", +" Specifies the minimum length of the patterns. [default: 1]", +" ", +" -maxlen=int", +" Specifies the maximum length of the patterns. [default: none]", +" ", +" -minfreq=int", +" Specifies the minimum frequency of the patterns. [default: 10]", +" ", +" -maxfreq=int", +" Specifies the maximum frequency of the patterns. [default: none]", +" ", +" -silent", +" Does not print the discovered itemsets.", +" ", +" -clabels=filename", +" Specifies the name of the file that stores the column labels.", +" ", +" -help", +" Prints this message.", +"" +}; + +static char shorthelpstr[][100] = { +" ", +" Usage: fis [options] ", +" use 'fis -help' for a summary of the options.", +"" +}; + + + +/*************************************************************************/ +/*! Function prototypes */ +/*************************************************************************/ +void print_init_info(params_t *params, gk_csr_t *mat); +void print_final_info(params_t *params); +params_t *parse_cmdline(int argc, char *argv[]); +void print_an_itemset(void *stateptr, int nitems, int *itemind, + int ntrans, int *tranind); + + +/*************************************************************************/ +/*! the entry point */ +/**************************************************************************/ +int main(int argc, char *argv[]) +{ + ssize_t i; + char line[8192]; + FILE *fpin; + params_t *params; + gk_csr_t *mat; + + params = parse_cmdline(argc, argv); + params->nitemsets = 0; + + /* read the data */ + mat = gk_csr_Read(params->filename, GK_CSR_FMT_CLUTO, 1, 1); + gk_csr_CreateIndex(mat, GK_CSR_COL); + + /* read the column labels */ + params->clabels = (char **)gk_malloc(mat->ncols*sizeof(char *), "main: clabels"); + if (params->clabelfile == NULL) { + for (i=0; incols; i++) { + sprintf(line, "%zd", i); + params->clabels[i] = gk_strdup(line); + } + } + else { + fpin = gk_fopen(params->clabelfile, "r", "main: fpin"); + for (i=0; incols; i++) { + if (fgets(line, 8192, fpin) == NULL) + errexit("Failed on fgets.\n"); + params->clabels[i] = gk_strdup(gk_strtprune(line, " \n\t")); + } + gk_fclose(fpin); + } + + + print_init_info(params, mat); + + gk_find_frequent_itemsets(mat->nrows, mat->rowptr, mat->rowind, + params->minfreq, params->maxfreq, params->minlen, params->maxlen, + &print_an_itemset, (void *)params); + + printf("Total itemsets found: %zd\n", params->nitemsets); + + print_final_info(params); +} + + + +/*************************************************************************/ +/*! This function prints run parameters */ +/*************************************************************************/ +void print_init_info(params_t *params, gk_csr_t *mat) +{ + printf("*******************************************************************************\n"); + printf(" fis\n\n"); + printf("Matrix Information ---------------------------------------------------------\n"); + printf(" input file=%s, [%d, %d, %zd]\n", + params->filename, mat->nrows, mat->ncols, mat->rowptr[mat->nrows]); + + printf("\n"); + printf("Options --------------------------------------------------------------------\n"); + printf(" minlen=%zd, maxlen=%zd, minfeq=%zd, maxfreq=%zd\n", + params->minlen, params->maxlen, params->minfreq, params->maxfreq); + + printf("\n"); + printf("Finding patterns... -----------------------------------------------------\n"); +} + + +/*************************************************************************/ +/*! This function prints final statistics */ +/*************************************************************************/ +void print_final_info(params_t *params) +{ + printf("\n"); + printf("Memory Usage Information -----------------------------------------------------\n"); + printf(" Maximum memory used: %10zd bytes\n", (ssize_t) gk_GetMaxMemoryUsed()); + printf(" Current memory used: %10zd bytes\n", (ssize_t) gk_GetCurMemoryUsed()); + printf("********************************************************************************\n"); +} + + +/*************************************************************************/ +/*! This is the entry point of the command-line argument parser */ +/*************************************************************************/ +params_t *parse_cmdline(int argc, char *argv[]) +{ + int i; + int c, option_index; + params_t *params; + + params = (params_t *)gk_malloc(sizeof(params_t), "parse_cmdline: params"); + + /* initialize the params data structure */ + params->minlen = 1; + params->maxlen = -1; + params->minfreq = 10; + params->maxfreq = -1; + params->silent = 0; + params->filename = NULL; + params->clabelfile = NULL; + + + /* Parse the command line arguments */ + while ((c = gk_getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case CMD_MINLEN: + if (gk_optarg) params->minlen = atoi(gk_optarg); + break; + case CMD_MAXLEN: + if (gk_optarg) params->maxlen = atoi(gk_optarg); + break; + case CMD_MINFREQ: + if (gk_optarg) params->minfreq = atoi(gk_optarg); + break; + case CMD_MAXFREQ: + if (gk_optarg) params->maxfreq = atoi(gk_optarg); + break; + + case CMD_SILENT: + params->silent = 1; + break; + + case CMD_CLABELFILE: + if (gk_optarg) params->clabelfile = gk_strdup(gk_optarg); + break; + + case CMD_HELP: + for (i=0; strlen(helpstr[i]) > 0; i++) + printf("%s\n", helpstr[i]); + exit(0); + break; + case '?': + default: + printf("Illegal command-line option(s)\nUse %s -help for a summary of the options.\n", argv[0]); + exit(0); + } + } + + if (argc-gk_optind != 1) { + printf("Unrecognized parameters."); + for (i=0; strlen(shorthelpstr[i]) > 0; i++) + printf("%s\n", shorthelpstr[i]); + exit(0); + } + + params->filename = gk_strdup(argv[gk_optind++]); + + if (!gk_fexists(params->filename)) + errexit("input file %s does not exist.\n", params->filename); + + return params; +} + + + +/*************************************************************************/ +/*! This is the callback function for the itemset discovery routine */ +/*************************************************************************/ +void print_an_itemset(void *stateptr, int nitems, int *itemids, int ntrans, + int *transids) +{ + ssize_t i; + params_t *params; + + params = (params_t *)stateptr; + params->nitemsets++; + + if (!params->silent) { + printf("%4zd %4d %4d => ", params->nitemsets, nitems, ntrans); + for (i=0; iclabels[itemids[i]]); + printf("\n"); + for (i=0; i + +/*************************************************************************/ +/*! Data structures for the code */ +/*************************************************************************/ +typedef struct { + int type; + int niter; + float eps; + float lamda; + + char *infile; + char *outfile; +} params_t; + +/*************************************************************************/ +/*! Constants */ +/*************************************************************************/ +#define CMD_NITER 1 +#define CMD_EPS 2 +#define CMD_LAMDA 3 +#define CMD_TYPE 4 +#define CMD_HELP 10 + + +/*************************************************************************/ +/*! Local variables */ +/*************************************************************************/ +static struct gk_option long_options[] = { + {"type", 1, 0, CMD_TYPE}, + {"niter", 1, 0, CMD_NITER}, + {"lamda", 1, 0, CMD_LAMDA}, + {"eps", 1, 0, CMD_EPS}, + {"help", 0, 0, CMD_HELP}, + {0, 0, 0, 0} +}; + + +/*-------------------------------------------------------------------*/ +/* Mini help */ +/*-------------------------------------------------------------------*/ +static char helpstr[][100] = { +" ", +"Usage: gkgraph [options] []", +" ", +" Required parameters", +" graph-file", +" The name of the file storing the graph. The file is in ", +" Metis' graph format.", +" ", +" Optional parameters", +" -niter=int", +" Specifies the maximum number of iterations. [default: 100]", +" ", +" -lamda=float", +" Specifies the follow-the-adjacent-links probability. [default: 0.80]", +" ", +" -eps=float", +" Specifies the error tollerance. [default: 1e-10]", +" ", +" -help", +" Prints this message.", +"" +}; + +static char shorthelpstr[][100] = { +" ", +" Usage: gkgraph [options] []", +" use 'gkgraph -help' for a summary of the options.", +"" +}; + + + +/*************************************************************************/ +/*! Function prototypes */ +/*************************************************************************/ +double compute_compactness(params_t *params, gk_graph_t *graph, int32_t *perm); +void reorder_centroid(params_t *params, gk_graph_t *graph, int32_t *perm); +void print_init_info(params_t *params, gk_graph_t *graph); +void print_final_info(params_t *params); +params_t *parse_cmdline(int argc, char *argv[]); + + +/*************************************************************************/ +/*! the entry point */ +/**************************************************************************/ +int main(int argc, char *argv[]) +{ + ssize_t i, j, v; + params_t *params; + gk_graph_t *graph, *pgraph; + int32_t *perm; + + /* get command-line options */ + params = parse_cmdline(argc, argv); + + /* read the data */ + graph = gk_graph_Read(params->infile, GK_GRAPH_FMT_METIS, 0, 0, 0); + + /* display some basic stats */ + print_init_info(params, graph); + + + /* determine the initial compactness of the graph */ + printf("Initial compactness: %le\n", compute_compactness(params, graph, NULL)); + + /* compute the BFS ordering and re-order the graph */ + //for (i=0; initer; i++) { + for (i=0; i<1; i++) { + v = RandomInRange(graph->nvtxs); + gk_graph_ComputeBFSOrdering(graph, v, &perm, NULL); + printf("BFS from %8d. Compactness: %le\n", + (int) v, compute_compactness(params, graph, perm)); + + pgraph = gk_graph_Reorder(graph, perm, NULL); + gk_graph_Write(pgraph, "bfs.metis", GK_GRAPH_FMT_METIS); + gk_graph_Free(&pgraph); + + gk_graph_ComputeBestFOrdering(graph, v, params->type, &perm, NULL); + printf("BestF from %8d. Compactness: %le\n", + (int) v, compute_compactness(params, graph, perm)); + + pgraph = gk_graph_Reorder(graph, perm, NULL); + gk_graph_Write(pgraph, "bestf.metis", GK_GRAPH_FMT_METIS); + gk_graph_Free(&pgraph); + +#ifdef XXX + for (j=0; jniter; j++) { + reorder_centroid(params, graph, perm); + printf("\tAfter centroid; Compactness: %le\n", + compute_compactness(params, graph, perm)); + } + + pgraph = gk_graph_Reorder(graph, perm, NULL); + gk_graph_Write(pgraph, "centroid.metis", GK_GRAPH_FMT_METIS); + gk_graph_Free(&pgraph); +#endif + gk_free((void **)&perm, LTERM); + } + + gk_graph_Free(&graph); + //gk_graph_Free(&pgraph); + + print_final_info(params); +} + + + + +/*************************************************************************/ +/*! This function computes the compactness of the graph's adjacency list */ +/*************************************************************************/ +double compute_compactness(params_t *params, gk_graph_t *graph, int32_t *perm) +{ + int i, v, u, nvtxs; + ssize_t j, *xadj; + int32_t *adjncy; + double compactness=0.0; + int *freq; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + freq = gk_ismalloc(nvtxs, 0, "compute_compactness: freq"); + + for (i=0; i 0) + printf("%7d %6d\n", i, freq[i]); + } + */ + printf("\tnsmall: %d\n", freq[1]+freq[2]+freq[3]); + + return compactness/xadj[nvtxs]; +} + + +/*************************************************************************/ +/*! This function uses a centroid-based approach to refine the ordering */ +/*************************************************************************/ +void reorder_centroid(params_t *params, gk_graph_t *graph, int32_t *perm) +{ + int i, v, u, nvtxs; + ssize_t j, *xadj; + int32_t *adjncy; + gk_fkv_t *cand; + double displacement; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + cand = gk_fkvmalloc(nvtxs, "reorder_centroid: cand"); + + for (i=0; ilamda/(xadj[i+1]-xadj[i]); + } + + /* sort them based on the target position in increasing order */ + gk_fkvsorti(nvtxs, cand); + + + /* derive the permutation from the ordered list */ + gk_i32set(nvtxs, -1, perm); + for (i=0; iinfile, graph->nvtxs, graph->xadj[graph->nvtxs]); + + printf("\n"); + printf("Options --------------------------------------------------------------------\n"); + printf(" type=%d, niter=%d, lamda=%f, eps=%e\n", + params->type, params->niter, params->lamda, params->eps); + + printf("\n"); + printf("Working... -----------------------------------------------------------------\n"); +} + + +/*************************************************************************/ +/*! This function prints final statistics */ +/*************************************************************************/ +void print_final_info(params_t *params) +{ + printf("\n"); + printf("Memory Usage Information -----------------------------------------------------\n"); + printf(" Maximum memory used: %10zd bytes\n", (ssize_t) gk_GetMaxMemoryUsed()); + printf(" Current memory used: %10zd bytes\n", (ssize_t) gk_GetCurMemoryUsed()); + printf("********************************************************************************\n"); +} + + +/*************************************************************************/ +/*! This is the entry point of the command-line argument parser */ +/*************************************************************************/ +params_t *parse_cmdline(int argc, char *argv[]) +{ + int i; + int c, option_index; + params_t *params; + + params = (params_t *)gk_malloc(sizeof(params_t), "parse_cmdline: params"); + + /* initialize the params data structure */ + params->type = 1; + params->niter = 1; + params->eps = 1e-10; + params->lamda = 0.20; + params->infile = NULL; + + + /* Parse the command line arguments */ + while ((c = gk_getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case CMD_TYPE: + if (gk_optarg) params->type = atoi(gk_optarg); + break; + case CMD_NITER: + if (gk_optarg) params->niter = atoi(gk_optarg); + break; + case CMD_EPS: + if (gk_optarg) params->eps = atof(gk_optarg); + break; + case CMD_LAMDA: + if (gk_optarg) params->lamda = atof(gk_optarg); + break; + + case CMD_HELP: + for (i=0; strlen(helpstr[i]) > 0; i++) + printf("%s\n", helpstr[i]); + exit(0); + break; + case '?': + default: + printf("Illegal command-line option(s)\nUse %s -help for a summary of the options.\n", argv[0]); + exit(0); + } + } + + if (argc-gk_optind != 1) { + printf("Unrecognized parameters."); + for (i=0; strlen(shorthelpstr[i]) > 0; i++) + printf("%s\n", shorthelpstr[i]); + exit(0); + } + + params->infile = gk_strdup(argv[gk_optind++]); + + if (argc-gk_optind > 0) + params->outfile = gk_strdup(argv[gk_optind++]); + else + params->outfile = gk_strdup("gkgraph.out"); + + if (!gk_fexists(params->infile)) + errexit("input file %s does not exist.\n", params->infile); + + return params; +} + diff --git a/src/metis/GKlib/test/gksort.c b/src/metis/GKlib/test/gksort.c new file mode 100644 index 0000000..6543836 --- /dev/null +++ b/src/metis/GKlib/test/gksort.c @@ -0,0 +1,346 @@ +/*! +\file gksort.c +\brief Testing module for the various sorting routines in GKlib + +\date Started 4/4/2007 +\author George +\version\verbatim $Id: gksort.c 11058 2011-11-10 00:02:50Z karypis $ \endverbatim +*/ + +#include + +#define N 10000 + +/*************************************************************************/ +/*! Testing module for gk_?isort() routine */ +/*************************************************************************/ +void test_isort() +{ + gk_idx_t i; + int array[N]; + + /* test the increasing sort */ + printf("Testing iisort...\n"); + for (i=0; i array[i+1]) + printf("gk_isorti error at index %jd [%d %d]\n", (intmax_t)i, array[i], array[i+1]); + } + + + /* test the decreasing sort */ + printf("Testing disort...\n"); + for (i=0; i array[i+1]) + printf("gk_fsorti error at index %jd [%f %f]\n", (intmax_t)i, array[i], array[i+1]); + } + + + /* test the decreasing sort */ + printf("Testing dfsort...\n"); + for (i=0; i array[i+1]) + printf("gk_idxsorti error at index %zd [%zd %zd]\n", (ssize_t)i, (ssize_t)array[i], (ssize_t)array[i+1]); + } + + + /* test the decreasing sort */ + printf("Testing idxsortd...\n"); + for (i=0; i array[i+1].key) + printf("gk_ikvsorti error at index %jd [%d %d] [%jd %jd]\n", (intmax_t)i, array[i].key, array[i+1].key, (intmax_t)array[i].val, (intmax_t)array[i+1].val); + } + + + /* test the decreasing sort */ + printf("Testing ikvsortd...\n"); + for (i=0; i array[i+1].key) + printf("gk_fkvsorti error at index %jd [%f %f] [%jd %jd]\n", (intmax_t)i, array[i].key, array[i+1].key, (intmax_t)array[i].val, (intmax_t)array[i+1].val); + } + + + /* test the decreasing sort */ + printf("Testing fkvsortd...\n"); + for (i=0; i array[i+1].key) + printf("gk_dkvsorti error at index %jd [%lf %lf] [%jd %jd]\n", (intmax_t)i, array[i].key, array[i+1].key, (intmax_t)array[i].val, (intmax_t)array[i+1].val); + } + + + /* test the decreasing sort */ + printf("Testing dkvsortd...\n"); + for (i=0; i 0) + printf("gk_skvsorti error at index %jd [%s %s] [%jd %jd]\n", (intmax_t)i, array[i].key, array[i+1].key, (intmax_t)array[i].val, (intmax_t)array[i+1].val); + } + + + /* test the decreasing sort */ + printf("Testing skvsortd...\n"); + for (i=0; i array[i+1].key) + printf("gk_idxkvsorti error at index %zd [%zd %zd] [%zd %zd]\n", + (ssize_t)i, (ssize_t)array[i].key, (ssize_t)array[i+1].key, + (ssize_t)array[i].val, (ssize_t)array[i+1].val); + } + + + /* test the decreasing sort */ + printf("Testing idxkvsortd...\n"); + for (i=0; i + +/*************************************************************************/ +/*! Data structures for the code */ +/*************************************************************************/ +typedef struct { + int niter; + int ntvs; + int ppr; + float eps; + float lamda; + char *infile; + char *outfile; +} params_t; + +/*************************************************************************/ +/*! Constants */ +/*************************************************************************/ +#define CMD_NITER 1 +#define CMD_EPS 2 +#define CMD_LAMDA 3 +#define CMD_PPR 4 +#define CMD_NTVS 5 +#define CMD_HELP 10 + + +/*************************************************************************/ +/*! Local variables */ +/*************************************************************************/ +static struct gk_option long_options[] = { + {"niter", 1, 0, CMD_NITER}, + {"lamda", 1, 0, CMD_LAMDA}, + {"eps", 1, 0, CMD_EPS}, + {"ppr", 1, 0, CMD_PPR}, + {"ntvs", 1, 0, CMD_NTVS}, + {"help", 0, 0, CMD_HELP}, + {0, 0, 0, 0} +}; + + +/*-------------------------------------------------------------------*/ +/* Mini help */ +/*-------------------------------------------------------------------*/ +static char helpstr[][100] = { +" ", +"Usage: rw [options] ", +" ", +" Required parameters", +" graph-file", +" The name of the file storing the transactions. The file is in ", +" Metis' graph format.", +" ", +" Optional parameters", +" -niter=int", +" Specifies the maximum number of iterations. [default: 100]", +" ", +" -lamda=float", +" Specifies the follow-the-adjacent-links probability. [default: 0.80]", +" ", +" -eps=float", +" Specifies the error tollerance. [default: 1e-10]", +" ", +" -ppr=int", +" Specifies the source of the personalized PR. [default: -1]", +" ", +" -ntvs=int", +" Specifies the number of test-vectors to compute. [default: -1]", +" ", +" -help", +" Prints this message.", +"" +}; + +static char shorthelpstr[][100] = { +" ", +" Usage: rw [options] ", +" use 'rw -help' for a summary of the options.", +"" +}; + + + +/*************************************************************************/ +/*! Function prototypes */ +/*************************************************************************/ +void print_init_info(params_t *params, gk_csr_t *mat); +void print_final_info(params_t *params); +params_t *parse_cmdline(int argc, char *argv[]); + + +/*************************************************************************/ +/*! the entry point */ +/**************************************************************************/ +int main(int argc, char *argv[]) +{ + ssize_t i, j, niter; + params_t *params; + gk_csr_t *mat; + FILE *fpout; + + /* get command-line options */ + params = parse_cmdline(argc, argv); + + /* read the data */ + mat = gk_csr_Read(params->infile, GK_CSR_FMT_METIS, 1, 1); + + /* display some basic stats */ + print_init_info(params, mat); + + + + if (params->ntvs != -1) { + /* compute the pr for different randomly generated restart-distribution vectors */ + float **prs; + + prs = gk_fAllocMatrix(params->ntvs, mat->nrows, 0.0, "main: prs"); + + /* generate the random restart vectors */ + for (j=0; jntvs; j++) { + for (i=0; inrows; i++) + prs[j][i] = RandomInRange(931); + gk_fscale(mat->nrows, 1.0/gk_fsum(mat->nrows, prs[j], 1), prs[j], 1); + + niter = gk_rw_PageRank(mat, params->lamda, params->eps, params->niter, prs[j]); + printf("tvs#: %zd; niters: %zd\n", j, niter); + } + + /* output the computed pr scores */ + fpout = gk_fopen(params->outfile, "w", "main: outfile"); + for (i=0; inrows; i++) { + for (j=0; jntvs; j++) + fprintf(fpout, "%.4e ", prs[j][i]); + fprintf(fpout, "\n"); + } + gk_fclose(fpout); + + gk_fFreeMatrix(&prs, params->ntvs, mat->nrows); + } + else if (params->ppr != -1) { + /* compute the personalized pr from the specified vertex */ + float *pr; + + pr = gk_fsmalloc(mat->nrows, 0.0, "main: pr"); + + pr[params->ppr-1] = 1.0; + + niter = gk_rw_PageRank(mat, params->lamda, params->eps, params->niter, pr); + printf("ppr: %d; niters: %zd\n", params->ppr, niter); + + /* output the computed pr scores */ + fpout = gk_fopen(params->outfile, "w", "main: outfile"); + for (i=0; inrows; i++) + fprintf(fpout, "%.4e\n", pr[i]); + gk_fclose(fpout); + + gk_free((void **)&pr, LTERM); + } + else { + /* compute the standard pr */ + int jmax; + float diff, maxdiff; + float *pr; + + pr = gk_fsmalloc(mat->nrows, 1.0/mat->nrows, "main: pr"); + + niter = gk_rw_PageRank(mat, params->lamda, params->eps, params->niter, pr); + printf("pr; niters: %zd\n", niter); + + /* output the computed pr scores */ + fpout = gk_fopen(params->outfile, "w", "main: outfile"); + for (i=0; inrows; i++) { + for (jmax=i, maxdiff=0.0, j=mat->rowptr[i]; jrowptr[i+1]; j++) { + if ((diff = fabs(pr[i]-pr[mat->rowind[j]])) > maxdiff) { + maxdiff = diff; + jmax = mat->rowind[j]; + } + } + fprintf(fpout, "%.4e %10zd %.4e %10d\n", pr[i], + mat->rowptr[i+1]-mat->rowptr[i], maxdiff, jmax+1); + } + gk_fclose(fpout); + + gk_free((void **)&pr, LTERM); + } + + gk_csr_Free(&mat); + + /* display some final stats */ + print_final_info(params); +} + + + +/*************************************************************************/ +/*! This function prints run parameters */ +/*************************************************************************/ +void print_init_info(params_t *params, gk_csr_t *mat) +{ + printf("*******************************************************************************\n"); + printf(" fis\n\n"); + printf("Matrix Information ---------------------------------------------------------\n"); + printf(" input file=%s, [%d, %d, %zd]\n", + params->infile, mat->nrows, mat->ncols, mat->rowptr[mat->nrows]); + + printf("\n"); + printf("Options --------------------------------------------------------------------\n"); + printf(" niter=%d, ntvs=%d, ppr=%d, lamda=%f, eps=%e\n", + params->niter, params->ntvs, params->ppr, params->lamda, params->eps); + + printf("\n"); + printf("Performing random walks... ----------------------------------------------\n"); +} + + +/*************************************************************************/ +/*! This function prints final statistics */ +/*************************************************************************/ +void print_final_info(params_t *params) +{ + printf("\n"); + printf("Memory Usage Information -----------------------------------------------------\n"); + printf(" Maximum memory used: %10zd bytes\n", (ssize_t) gk_GetMaxMemoryUsed()); + printf(" Current memory used: %10zd bytes\n", (ssize_t) gk_GetCurMemoryUsed()); + printf("********************************************************************************\n"); +} + + +/*************************************************************************/ +/*! This is the entry point of the command-line argument parser */ +/*************************************************************************/ +params_t *parse_cmdline(int argc, char *argv[]) +{ + int i; + int c, option_index; + params_t *params; + + params = (params_t *)gk_malloc(sizeof(params_t), "parse_cmdline: params"); + + /* initialize the params data structure */ + params->niter = 100; + params->ppr = -1; + params->ntvs = -1; + params->eps = 1e-10; + params->lamda = 0.80; + params->infile = NULL; + params->outfile = NULL; + + + /* Parse the command line arguments */ + while ((c = gk_getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case CMD_NITER: + if (gk_optarg) params->niter = atoi(gk_optarg); + break; + case CMD_NTVS: + if (gk_optarg) params->ntvs = atoi(gk_optarg); + break; + case CMD_PPR: + if (gk_optarg) params->ppr = atoi(gk_optarg); + break; + case CMD_EPS: + if (gk_optarg) params->eps = atof(gk_optarg); + break; + case CMD_LAMDA: + if (gk_optarg) params->lamda = atof(gk_optarg); + break; + + case CMD_HELP: + for (i=0; strlen(helpstr[i]) > 0; i++) + printf("%s\n", helpstr[i]); + exit(0); + break; + case '?': + default: + printf("Illegal command-line option(s)\nUse %s -help for a summary of the options.\n", argv[0]); + exit(0); + } + } + + if (argc-gk_optind != 2) { + printf("Unrecognized parameters."); + for (i=0; strlen(shorthelpstr[i]) > 0; i++) + printf("%s\n", shorthelpstr[i]); + exit(0); + } + + params->infile = gk_strdup(argv[gk_optind++]); + params->outfile = gk_strdup(argv[gk_optind++]); + + if (!gk_fexists(params->infile)) + errexit("input file %s does not exist.\n", params->infile); + + if (params->ppr != -1 && params->ntvs != -1) + errexit("Only one of the -ppr and -ntvs options can be specified.\n"); + + return params; +} + diff --git a/src/metis/GKlib/test/strings.c b/src/metis/GKlib/test/strings.c new file mode 100644 index 0000000..b241d3f --- /dev/null +++ b/src/metis/GKlib/test/strings.c @@ -0,0 +1,82 @@ +/*! +\file strings.c +\brief Testing module for the string functions in GKlib + +\date Started 3/5/2007 +\author George +\version\verbatim $Id: strings.c 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + +#include + + +/*************************************************************************/ +/*! Testing module for gk_strstr_replace() */ +/*************************************************************************/ +void test_strstr_replace() +{ + char *new_str; + int rc; + + rc = gk_strstr_replace("This is a simple string", "s", "S", "", &new_str); + printf("%d, %s.\n", rc, new_str); + gk_free((void **)&new_str, LTERM); + + + rc = gk_strstr_replace("This is a simple string", "s", "S", "g", &new_str); + printf("%d, %s.\n", rc, new_str); + gk_free((void **)&new_str, LTERM); + + + rc = gk_strstr_replace("This is a simple SS & ss string", "s", "T", "g", &new_str); + printf("%d, %s.\n", rc, new_str); + gk_free((void **)&new_str, LTERM); + + + rc = gk_strstr_replace("This is a simple SS & ss string", "s", "T", "ig", &new_str); + printf("%d, %s.\n", rc, new_str); + gk_free((void **)&new_str, LTERM); + + rc = gk_strstr_replace("This is a simple SS & ss string", "\\b\\w(\\w+)\\w\\b", "$1", "ig", &new_str); + printf("%d, %s.\n", rc, new_str); + gk_free((void **)&new_str, LTERM); + + rc = gk_strstr_replace("This is a simple SS & ss string", "\\b\\w+\\b", "word", "ig", &new_str); + printf("%d, %s.\n", rc, new_str); + gk_free((void **)&new_str, LTERM); + + rc = gk_strstr_replace("http://www.cs.umn.edu/This-is-something-T12323?pp=20&page=4", + "(http://www\\.cs\\.umn\\.edu/)(.*)-T(\\d+)", "$1$2-P$3", "g", &new_str); + printf("%d, %s.\n", rc, new_str); + gk_free((void **)&new_str, LTERM); + + rc = gk_strstr_replace("http://www.cs.umn.edu/This-is-something-T12323?pp=20&page=4", + "(\\d+)", "number:$1", "ig", &new_str); + printf("%d, %s.\n", rc, new_str); + gk_free((void **)&new_str, LTERM); + + + rc = gk_strstr_replace("http://www.cs.umn.edu/This-is-something-T12323?pp=20&page=4", + "(http://www\\.cs\\.umn\\.edu/)", "[$1]", "g", &new_str); + printf("%d, %s.\n", rc, new_str); + gk_free((void **)&new_str, LTERM); + + + +} + + + +int main() +{ + test_strstr_replace(); + +/* + { + int i; + for (i=0; i<1000; i++) + printf("%d\n", RandomInRange(3)); + } +*/ +} + diff --git a/src/metis/GKlib/timers.c b/src/metis/GKlib/timers.c new file mode 100644 index 0000000..bb8f296 --- /dev/null +++ b/src/metis/GKlib/timers.c @@ -0,0 +1,52 @@ +/*! +\file timers.c +\brief Various timing functions + +\date Started 4/12/2007 +\author George +\version\verbatim $Id: timers.c 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + + +#include + + + + +/************************************************************************* +* This function returns the CPU seconds +**************************************************************************/ +double gk_WClockSeconds(void) +{ +#ifdef __GNUC__ + struct timeval ctime; + + gettimeofday(&ctime, NULL); + + return (double)ctime.tv_sec + (double).000001*ctime.tv_usec; +#else + return (double)time(NULL); +#endif +} + + +/************************************************************************* +* This function returns the CPU seconds +**************************************************************************/ +double gk_CPUSeconds(void) +{ +//#ifdef __OPENMP__ +#ifdef __OPENMPXXXX__ + return omp_get_wtime(); +#else + #if defined(WIN32) || defined(__MINGW32__) + return((double) clock()/CLOCKS_PER_SEC); + #else + struct rusage r; + + getrusage(RUSAGE_SELF, &r); + return ((r.ru_utime.tv_sec + r.ru_stime.tv_sec) + 1.0e-6*(r.ru_utime.tv_usec + r.ru_stime.tv_usec)); + #endif +#endif +} + diff --git a/src/metis/GKlib/tokenizer.c b/src/metis/GKlib/tokenizer.c new file mode 100644 index 0000000..5efd262 --- /dev/null +++ b/src/metis/GKlib/tokenizer.c @@ -0,0 +1,77 @@ +/*! +\file tokenizer.c +\brief String tokenization routines + +This file contains various routines for splitting an input string into +tokens and returning them in form of a list. The goal is to mimic perl's +split function. + +\date Started 11/23/04 +\author George +\version\verbatim $Id: tokenizer.c 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + + +#include + + +/************************************************************************ +* This function tokenizes a string based on the user-supplied delimiters +* list. The resulting tokens are returned into an array of strings. +*************************************************************************/ +void gk_strtokenize(char *str, char *delim, gk_Tokens_t *tokens) +{ + int i, ntoks, slen; + + tokens->strbuf = gk_strdup(str); + + slen = strlen(str); + str = tokens->strbuf; + + /* Scan once to determine the number of tokens */ + for (ntoks=0, i=0; intoks = ntoks; + tokens->list = (char **)gk_malloc(ntoks*sizeof(char *), "strtokenize: tokens->list"); + + + /* Scan a second time to mark and link the tokens */ + for (ntoks=0, i=0; ilist[ntoks++] = str+i; + + /* Consume all the consecutive characters from the token */ + while (ilist, &tokens->strbuf, LTERM); +} + diff --git a/src/metis/GKlib/util.c b/src/metis/GKlib/util.c new file mode 100644 index 0000000..e75d68b --- /dev/null +++ b/src/metis/GKlib/util.c @@ -0,0 +1,108 @@ +/*! +\file util.c +\brief Various utility routines + +\date Started 4/12/2007 +\author George +\version\verbatim $Id: util.c 10711 2011-08-31 22:23:04Z karypis $ \endverbatim +*/ + + +#include + + + +/************************************************************************* +* This file randomly permutes the contents of an array. +* flag == 0, don't initialize perm +* flag == 1, set p[i] = i +**************************************************************************/ +void gk_RandomPermute(size_t n, int *p, int flag) +{ + gk_idx_t i, u, v; + int tmp; + + if (flag == 1) { + for (i=0; i 1; i++, a = a>>1); + return i-1; +} + + +/************************************************************************* +* This function checks if the argument is a power of 2 +**************************************************************************/ +int gk_ispow2(int a) +{ + return (a == (1< - -/************************************************************************* -* This function is the entry poidxtype of the bisection balancing algorithms. -**************************************************************************/ -void Balance2Way(CtrlType *ctrl, GraphType *graph, idxtype *tpwgts, float ubfactor) -{ - idxtype i, j, nvtxs, from, imax, gain, mindiff; - idxtype *id, *ed; - - /* Return right away if the balance is OK */ - mindiff = idxtype_abs(tpwgts[0]-graph->pwgts[0]); - if (mindiff < 3*(graph->pwgts[0]+graph->pwgts[1])/graph->nvtxs) - return; - if (graph->pwgts[0] > tpwgts[0] && graph->pwgts[0] < (int)(ubfactor*tpwgts[0])) - return; - if (graph->pwgts[1] > tpwgts[1] && graph->pwgts[1] < (int)(ubfactor*tpwgts[1])) - return; - - if (graph->nbnd > 0) - Bnd2WayBalance(ctrl, graph, tpwgts); - else - General2WayBalance(ctrl, graph, tpwgts); - -} - - - -/************************************************************************* -* This function balances two partitions by moving boundary nodes -* from the domain that is overweight to the one that is underweight. -**************************************************************************/ -void Bnd2WayBalance(CtrlType *ctrl, GraphType *graph, idxtype *tpwgts) -{ - idxtype i, ii, j, k, kwgt, nvtxs, nbnd, nswaps, from, to, pass, me, tmp; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind, *pwgts; - idxtype *moved, *perm; - PQueueType parts; - idxtype higain, oldgain, mincut, mindiff; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - id = graph->id; - ed = graph->ed; - pwgts = graph->pwgts; - bndptr = graph->bndptr; - bndind = graph->bndind; - - moved = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - - /* Determine from which domain you will be moving data */ - mindiff = idxtype_abs(tpwgts[0]-pwgts[0]); - from = (pwgts[0] < tpwgts[0] ? 1 : 0); - to = (from+1)%2; - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D] T[%6D %6D], Nv-Nb[%6D %6D]. ICut: %6D [B]\n", - pwgts[0], pwgts[1], tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); - - tmp = graph->adjwgtsum[idxargmax(nvtxs, graph->adjwgtsum)]; - PQueueInit(ctrl, &parts, nvtxs, tmp); - - idxset(nvtxs, -1, moved); - - ASSERT(ComputeCut(graph, where) == graph->mincut); - ASSERT(CheckBnd(graph)); - - /* Insert the boundary nodes of the proper partition whose size is OK in the priority queue */ - nbnd = graph->nbnd; - RandomPermute(nbnd, perm, 1); - for (ii=0; ii 0 || id[bndind[i]] == 0); - ASSERT(bndptr[bndind[i]] != -1); - if (where[bndind[i]] == from && vwgt[bndind[i]] <= mindiff) - PQueueInsert(&parts, bndind[i], ed[bndind[i]]-id[bndind[i]]); - } - - mincut = graph->mincut; - for (nswaps=0; nswaps tpwgts[to]) - break; - - mincut -= (ed[higain]-id[higain]); - INC_DEC(pwgts[to], pwgts[from], vwgt[higain]); - - where[higain] = to; - moved[higain] = nswaps; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, - mprintf("Moved %6D from %D. [%3D %3D] %5D [%4D %4D]\n", higain, from, ed[higain]-id[higain], vwgt[higain], mincut, pwgts[0], pwgts[1])); - - /************************************************************** - * Update the id[i]/ed[i] values of the affected nodes - ***************************************************************/ - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - - for (j=xadj[higain]; j 0) { /* It will now become a boundary vertex */ - BNDInsert(nbnd, bndind, bndptr, k); - if (moved[k] == -1 && where[k] == from && vwgt[k] <= mindiff) - PQueueInsert(&parts, k, ed[k]-id[k]); - } - } - } - } - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\tMinimum cut: %6D, PWGTS: [%6D %6D], NBND: %6D\n", mincut, pwgts[0], pwgts[1], nbnd)); - - graph->mincut = mincut; - graph->nbnd = nbnd; - - PQueueFree(ctrl, &parts); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - -/************************************************************************* -* This function balances two partitions by moving the highest gain -* (including negative gain) vertices to the other domain. -* It is used only when tha unbalance is due to non contigous -* subdomains. That is, the are no boundary vertices. -* It moves vertices from the domain that is overweight to the one that -* is underweight. -**************************************************************************/ -void General2WayBalance(CtrlType *ctrl, GraphType *graph, idxtype *tpwgts) -{ - idxtype i, ii, j, k, kwgt, nvtxs, nbnd, nswaps, from, to, pass, me, tmp; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind, *pwgts; - idxtype *moved, *perm; - PQueueType parts; - idxtype higain, oldgain, mincut, mindiff; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - id = graph->id; - ed = graph->ed; - pwgts = graph->pwgts; - bndptr = graph->bndptr; - bndind = graph->bndind; - - moved = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - - /* Determine from which domain you will be moving data */ - mindiff = idxtype_abs(tpwgts[0]-pwgts[0]); - from = (pwgts[0] < tpwgts[0] ? 1 : 0); - to = (from+1)%2; - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D] T[%6D %6D], Nv-Nb[%6D %6D]. ICut: %6D [B]\n", - pwgts[0], pwgts[1], tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); - - tmp = graph->adjwgtsum[idxargmax(nvtxs, graph->adjwgtsum)]; - PQueueInit(ctrl, &parts, nvtxs, tmp); - - idxset(nvtxs, -1, moved); - - ASSERT(ComputeCut(graph, where) == graph->mincut); - ASSERT(CheckBnd(graph)); - - /* Insert the nodes of the proper partition whose size is OK in the priority queue */ - RandomPermute(nvtxs, perm, 1); - for (ii=0; iimincut; - nbnd = graph->nbnd; - for (nswaps=0; nswaps tpwgts[to]) - break; - - mincut -= (ed[higain]-id[higain]); - INC_DEC(pwgts[to], pwgts[from], vwgt[higain]); - - where[higain] = to; - moved[higain] = nswaps; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, - mprintf("Moved %6D from %D. [%3D %3D] %5D [%4D %4D]\n", higain, from, ed[higain]-id[higain], vwgt[higain], mincut, pwgts[0], pwgts[1])); - - /************************************************************** - * Update the id[i]/ed[i] values of the affected nodes - ***************************************************************/ - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - if (ed[higain] > 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - for (j=xadj[higain]; j 0 && bndptr[k] == -1) - BNDInsert(nbnd, bndind, bndptr, k); - } - } - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\tMinimum cut: %6D, PWGTS: [%6D %6D], NBND: %6D\n", mincut, pwgts[0], pwgts[1], nbnd)); - - graph->mincut = mincut; - graph->nbnd = nbnd; - - PQueueFree(ctrl, &parts); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} diff --git a/src/metis/ccgraph.c b/src/metis/ccgraph.c deleted file mode 100644 index aeae019..0000000 --- a/src/metis/ccgraph.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * ccgraph.c - * - * This file contains the functions that create the coarse graph - * - * Started 8/11/97 - * George - * - */ - -#include - - - -/************************************************************************* -* This function creates the coarser graph -**************************************************************************/ -void CreateCoarseGraph(CtrlType *ctrl, GraphType *graph, idxtype cnvtxs, idxtype *match, idxtype *perm) -{ - idxtype i, j, jj, k, kk, l, m, istart, iend, nvtxs, nedges, ncon, cnedges, v, u, mask, dovsize; - idxtype *xadj, *vwgt, *vsize, *adjncy, *adjwgt, *adjwgtsum, *auxadj; - idxtype *cmap, *htable; - idxtype *cxadj, *cvwgt, *cvsize, *cadjncy, *cadjwgt, *cadjwgtsum; - float *nvwgt, *cnvwgt; - GraphType *cgraph; - - dovsize = (ctrl->optype == OP_KVMETIS ? 1 : 0); - - mask = HTLENGTH; - if (cnvtxs < 8*mask || graph->nedges/graph->nvtxs > 15) { - CreateCoarseGraphNoMask(ctrl, graph, cnvtxs, match, perm); - return; - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ContractTmr)); - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - vwgt = graph->vwgt; - vsize = graph->vsize; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - cmap = graph->cmap; - - /* Initialize the coarser graph */ - cgraph = SetUpCoarseGraph(graph, cnvtxs, dovsize); - cxadj = cgraph->xadj; - cvwgt = cgraph->vwgt; - cvsize = cgraph->vsize; - cnvwgt = cgraph->nvwgt; - cadjwgtsum = cgraph->adjwgtsum; - cadjncy = cgraph->adjncy; - cadjwgt = cgraph->adjwgt; - - - iend = xadj[nvtxs]; - auxadj = ctrl->wspace.auxcore; - memcpy(auxadj, adjncy, iend*sizeof(idxtype)); - for (i=0; i= 0 && cadjncy[jj] != cnvtxs) { - for (jj=0; jj= 0 && cadjncy[jj] == cnvtxs) { /* This 2nd check is needed for non-adjacent matchings */ - cadjwgtsum[cnvtxs] -= cadjwgt[jj]; - cadjncy[jj] = cadjncy[--nedges]; - cadjwgt[jj] = cadjwgt[nedges]; - } - } - - ASSERTP(cadjwgtsum[cnvtxs] == idxsum(nedges, cadjwgt), ("%d %d %d %d %d\n", cnvtxs, cadjwgtsum[cnvtxs], idxsum(nedges, cadjwgt), adjwgtsum[u], adjwgtsum[v])); - - for (j=0; jnedges = cnedges; - - ReAdjustMemory(graph, cgraph, dovsize); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ContractTmr)); - - idxwspacefree(ctrl, mask+1); - -} - - -/************************************************************************* -* This function creates the coarser graph -**************************************************************************/ -void CreateCoarseGraphNoMask(CtrlType *ctrl, GraphType *graph, idxtype cnvtxs, idxtype *match, idxtype *perm) -{ - idxtype i, j, k, m, istart, iend, nvtxs, nedges, ncon, cnedges, v, u, dovsize; - idxtype *xadj, *vwgt, *vsize, *adjncy, *adjwgt, *adjwgtsum, *auxadj; - idxtype *cmap, *htable; - idxtype *cxadj, *cvwgt, *cvsize, *cadjncy, *cadjwgt, *cadjwgtsum; - float *nvwgt, *cnvwgt; - GraphType *cgraph; - - dovsize = (ctrl->optype == OP_KVMETIS ? 1 : 0); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ContractTmr)); - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - vwgt = graph->vwgt; - vsize = graph->vsize; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - cmap = graph->cmap; - - - /* Initialize the coarser graph */ - cgraph = SetUpCoarseGraph(graph, cnvtxs, dovsize); - cxadj = cgraph->xadj; - cvwgt = cgraph->vwgt; - cvsize = cgraph->vsize; - cnvwgt = cgraph->nvwgt; - cadjwgtsum = cgraph->adjwgtsum; - cadjncy = cgraph->adjncy; - cadjwgt = cgraph->adjwgt; - - - htable = idxset(cnvtxs, -1, idxwspacemalloc(ctrl, cnvtxs)); - - iend = xadj[nvtxs]; - auxadj = ctrl->wspace.auxcore; - memcpy(auxadj, adjncy, iend*sizeof(idxtype)); - for (i=0; inedges = cnedges; - - ReAdjustMemory(graph, cgraph, dovsize); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ContractTmr)); - - idxwspacefree(ctrl, cnvtxs); -} - - -/************************************************************************* -* This function creates the coarser graph -**************************************************************************/ -void CreateCoarseGraph_NVW(CtrlType *ctrl, GraphType *graph, idxtype cnvtxs, idxtype *match, idxtype *perm) -{ - idxtype i, j, jj, k, kk, l, m, istart, iend, nvtxs, nedges, ncon, cnedges, v, u, mask; - idxtype *xadj, *adjncy, *adjwgtsum, *auxadj; - idxtype *cmap, *htable; - idxtype *cxadj, *cvwgt, *cadjncy, *cadjwgt, *cadjwgtsum; - float *nvwgt, *cnvwgt; - GraphType *cgraph; - - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ContractTmr)); - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgtsum = graph->adjwgtsum; - cmap = graph->cmap; - - /* Initialize the coarser graph */ - cgraph = SetUpCoarseGraph(graph, cnvtxs, 0); - cxadj = cgraph->xadj; - cvwgt = cgraph->vwgt; - cnvwgt = cgraph->nvwgt; - cadjwgtsum = cgraph->adjwgtsum; - cadjncy = cgraph->adjncy; - cadjwgt = cgraph->adjwgt; - - - iend = xadj[nvtxs]; - auxadj = ctrl->wspace.auxcore; - memcpy(auxadj, adjncy, iend*sizeof(idxtype)); - for (i=0; i= 0 && cadjncy[jj] != cnvtxs) { - for (jj=0; jj= 0 && cadjncy[jj] == cnvtxs) { /* This 2nd check is needed for non-adjacent matchings */ - cadjwgtsum[cnvtxs] -= cadjwgt[jj]; - cadjncy[jj] = cadjncy[--nedges]; - cadjwgt[jj] = cadjwgt[nedges]; - } - } - - ASSERTP(cadjwgtsum[cnvtxs] == idxsum(nedges, cadjwgt), ("%d %d %d %d %d\n", cnvtxs, cadjwgtsum[cnvtxs], idxsum(nedges, cadjwgt), adjwgtsum[u], adjwgtsum[v])); - - for (j=0; jnedges = cnedges; - - ReAdjustMemory(graph, cgraph, 0); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ContractTmr)); - - idxwspacefree(ctrl, mask+1); - -} - - -/************************************************************************* -* Setup the various arrays for the coarse graph -**************************************************************************/ -GraphType *SetUpCoarseGraph(GraphType *graph, idxtype cnvtxs, idxtype dovsize) -{ - GraphType *cgraph; - - cgraph = CreateGraph(); - - cgraph->nvtxs = cnvtxs; - cgraph->ncon = graph->ncon; - - cgraph->finer = graph; - graph->coarser = cgraph; - - - /* Allocate memory for the coarser graph */ - cgraph->xadj = idxmalloc(cnvtxs+1, "SetUpCoarseGraph: xadj"); - cgraph->adjwgtsum = idxmalloc(cnvtxs, "SetUpCoarseGraph: adjwgtsum"); - cgraph->cmap = idxmalloc(cnvtxs, "SetUpCoarseGraph: cmap"); - cgraph->adjncy = idxmalloc(graph->nedges, "SetUpCoarseGraph: adjncy"); - cgraph->adjwgt = idxmalloc(graph->nedges, "SetUpCoarseGraph: adjwgt"); - - if (graph->ncon == 1) - cgraph->vwgt = idxmalloc(cnvtxs, "SetUpCoarseGraph: vwgt"); - else - cgraph->nvwgt = gk_fmalloc(graph->ncon*cnvtxs, "SetUpCoarseGraph: nvwgt"); - - if (dovsize) - cgraph->vsize = idxmalloc(cnvtxs, "SetUpCoarseGraph: vsize"); - - return cgraph; -} - - - -/************************************************************************* -* This function re-adjusts the amount of memory that was allocated if -* it will lead to significant savings -**************************************************************************/ -void ReAdjustMemory(GraphType *graph, GraphType *cgraph, idxtype dovsize) -{ - if (cgraph->nedges > 10000 && cgraph->nedges < 0.8*graph->nedges) { - cgraph->adjncy = idxrealloc(cgraph->adjncy, cgraph->nedges, "ReAdjustMemory: adjncy"); - cgraph->adjwgt = idxrealloc(cgraph->adjwgt, cgraph->nedges, "ReAdjustMemory: adjwgt"); - } -} diff --git a/src/metis/checkgraph.c b/src/metis/checkgraph.c deleted file mode 100644 index 0f0993a..0000000 --- a/src/metis/checkgraph.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * checkgraph.c - * - * This file contains routines related to I/O - * - * Started 8/28/94 - * George - * - */ - -#include - - - -/************************************************************************* -* This function checks if a graph is valid -**************************************************************************/ -idxtype CheckGraph(GraphType *graph) -{ - idxtype i, j, k, l; - idxtype nvtxs, ncon, err=0; - idxtype minedge, maxedge, minewgt, maxewgt; - float minvwgt[MAXNCON], maxvwgt[MAXNCON]; - idxtype *xadj, *adjncy, *adjwgt, *htable; - float *nvwgt, ntvwgts[MAXNCON]; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - htable = idxsmalloc(nvtxs, 0, "htable"); - - if (ncon > 1) { - for (j=0; j 1) { - for (j=0; j maxvwgt[j]) ? nvwgt[i*ncon+j] : maxvwgt[j]; - } - } - - for (j=xadj[i]; j maxedge) ? k : maxedge; - minewgt = (adjwgt[j] < minewgt) ? adjwgt[j] : minewgt; - maxewgt = (adjwgt[j] > maxewgt) ? adjwgt[j] : maxewgt; - - if (i == k) { - mprintf("Vertex %D contains a self-loop (i.e., diagonal entry in the matrix)!\n", i); - err++; - } - else { - for (l=xadj[k]; l 1) { - for (j=0; j 0.0001) { - mprintf("Normalized vwgts don't sum to one. Weight %D = %.8f.\n", j, ntvwgts[j]); - err++; - } - } - } - -/* - mprintf("errs: %D, adjncy: [%D %D], adjwgt: [%D %D]\n", - err, minedge, maxedge, minewgt, maxewgt); - if (ncon > 1) { - for (j=0; j 0) { - mprintf("A total of %D errors exist in the input file. Correct them, and run again!\n", err); - } - - gk_free((void **)&htable, LTERM); - return (err == 0 ? 1 : 0); -} - diff --git a/src/metis/cmetis.c b/src/metis/cmetis.c deleted file mode 100644 index a6e0cea..0000000 --- a/src/metis/cmetis.c +++ /dev/null @@ -1,1161 +0,0 @@ -/* - * Copyright 2003, Regents of the University of Minnesota - * - * kmetis.c - * - * This file contains the top level routines for the contact-friendly partitioning - * algorithm CMETIS. - * - * Started 4/3/03 - * George - * - */ - -#include - -#define DEPSILON 1.0e-012 - -#define _PRINTSTAT - -/************************************************************************* -* This function is the entry point for KMETIS -**************************************************************************/ -void *METIS_PartGraphForContact(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, - double *xyzcoords, idxtype *sflag, idxtype *numflag, idxtype *nparts, - idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i, j, ii, dim, ncon, wgtflag, mcnumflag, nnodes, nlnodes, nclean, naclean, ndirty, maxdepth, rwgtflag, rnumflag; - idxtype *mcvwgt, *dtpart, *marker, *leafpart; - idxtype *adjwgt; - float rubvec[2], lbvec[2]; - GraphType graph, *cgraph; - ContactInfoType *cinfo; - DKeyValueType *xyzcand[3]; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - /*--------------------------------------------------------------------- - * Allocate memory for the contact info type - *---------------------------------------------------------------------*/ - cinfo = (ContactInfoType *)gk_malloc(sizeof(ContactInfoType), "METIS_PartGraphForContact: cinfo"); - cinfo->leafptr = idxsmalloc(*nvtxs+1, 0, "METIS_PartGraphForContact: leafptr"); - cinfo->leafind = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: leafind"); - cinfo->leafwgt = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: leafwgt"); - cinfo->part = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: part"); - leafpart = cinfo->leafpart = idxmalloc(*nvtxs, "METIS_PartGraphForContact: leafpart"); - cinfo->dtree = (DTreeNodeType *)gk_malloc(sizeof(DTreeNodeType)*(*nvtxs), "METIS_PartGraphForContact: cinfo->dtree"); - cinfo->nvtxs = *nvtxs; - - /*--------------------------------------------------------------------- - * Compute the initial k-way partitioning - *---------------------------------------------------------------------*/ - mcvwgt = idxsmalloc(2*(*nvtxs), 0, "METIS_PartGraphForContact: mcvwgt"); - for (i=0; i<*nvtxs; i++) { - mcvwgt[2*i+0] = 1; - mcvwgt[2*i+1] = (sflag[i] == 0 ? 0 : 1); - } - - adjwgt = idxmalloc(xadj[*nvtxs], "METIS_PartGraphForContact: adjwgt"); - for (i=0; i<*nvtxs; i++) { - for (j=xadj[i]; jdtree, leafpart, dtpart, - &nclean, &naclean, &ndirty, &maxdepth, marker); - - mprintf("NNodes: %5D, NLNodes: %5D, NClean: %5D, NAClean: %5D, NDirty: %5D, MaxDepth: %3D\n", nnodes, nlnodes, nclean, naclean, ndirty, maxdepth); - - - /*--------------------------------------------------------------------- - * Create the tree-induced coarse graph and refine it - *---------------------------------------------------------------------*/ - cgraph = CreatePartitionGraphForContact(*nvtxs, xadj, adjncy, mcvwgt, adjwgt, nlnodes, leafpart); - - for (i=0; i<*nvtxs; i++) - part[leafpart[i]] = dtpart[i]; - - ComputePartitionBalance(cgraph, *nparts, part, lbvec); - mprintf(" %D-way Edge-Cut: %7D, Balance: %5.2f %5.2f\n", *nparts, ComputeCut(cgraph, part), lbvec[0], lbvec[1]); - - - rwgtflag = 3; - rnumflag = 0; - METIS_mCRefineGraphKway(&(cgraph->nvtxs), &ncon, cgraph->xadj, cgraph->adjncy, cgraph->vwgt, - cgraph->adjwgt, &rwgtflag, &rnumflag, nparts, rubvec, options, edgecut, - part); - - ComputePartitionBalance(cgraph, *nparts, part, lbvec); - mprintf(" %D-way Edge-Cut: %7D, Balance: %5.2f %5.2f\n", *nparts, ComputeCut(cgraph, part), lbvec[0], lbvec[1]); - - - /*--------------------------------------------------------------------- - * Use that to compute the partition of the original graph - *---------------------------------------------------------------------*/ - idxcopy(cgraph->nvtxs, part, dtpart); - for (i=0; i<*nvtxs; i++) - part[i] = dtpart[leafpart[i]]; - - ComputePartitionBalance(&graph, *nparts, part, lbvec); - idxset(*nvtxs, 1, graph.vwgt); - mprintf(" %D-way Edge-Cut: %7D, Volume: %7D, Balance: %5.2f %5.2f\n", *nparts, - ComputeCut(&graph, part), ComputeVolume(&graph, part), lbvec[0], lbvec[1]); - - - /*--------------------------------------------------------------------- - * Induce the final decission tree - *---------------------------------------------------------------------*/ - nnodes = nlnodes = nclean = naclean = ndirty = maxdepth = 0; - InduceDecissionTree(*nvtxs, xyzcand, sflag, *nparts, part, - *nvtxs/((40)*(*nparts)), 1, 1.00, - &nnodes, &nlnodes, cinfo->dtree, leafpart, dtpart, - &nclean, &naclean, &ndirty, &maxdepth, marker); - - mprintf("NNodes: %5D, NLNodes: %5D, NClean: %5D, NAClean: %5D, NDirty: %5D, MaxDepth: %3D\n", nnodes, nlnodes, nclean, naclean, ndirty, maxdepth); - - - /*--------------------------------------------------------------------- - * Populate the remaining fields of the cinfo data structure - *---------------------------------------------------------------------*/ - cinfo->nnodes = nnodes; - cinfo->nleafs = nlnodes; - idxcopy(*nvtxs, part, cinfo->part); - - BuildDTLeafContents(cinfo, sflag); - - CheckDTree(*nvtxs, xyzcoords, part, cinfo); - - gk_free((void **)&mcvwgt, &dtpart, &xyzcand[0], &xyzcand[1], &xyzcand[2], &marker, &adjwgt, LTERM); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); - - return (void *)cinfo; -} - - -/************************************************************************* -* This function is the entry point for KMETIS -**************************************************************************/ -void METIS_UpdateContactInfo(void *raw_cinfo, idxtype *nvtxs, double *xyzcoords, idxtype *sflag) -{ - idxtype i, root, nchanges; - ContactInfoType *cinfo; - DTreeNodeType *dtree; - - cinfo = (ContactInfoType *)raw_cinfo; - dtree = cinfo->dtree; - - if (cinfo->nvtxs != *nvtxs) - errexit("The provided number of vertices do not match the initial information: %d %d\n", *nvtxs, cinfo->nvtxs); - - /* Go and reset the nsvtxs fields of all the nodes */ - for (i=0; innodes; i++) { - dtree[i].nvtxs = 0; - dtree[i].nsvtxs = 0; - } - - /* Go and traverse each surface node and see where it gets assigned (We only focus on surface nodes. Right???) */ - for (nchanges=0, i=0; i<*nvtxs; i++) { - if (1 || sflag[i]) { - for (root=0; dtree[root].leafid == -1;) - root = (xyzcoords[3*i+dtree[root].dim] <= dtree[root].value ? dtree[root].left : dtree[root].right); - - if (cinfo->leafpart[i] != dtree[root].leafid && sflag[i]) - nchanges++; - - cinfo->leafpart[i] = dtree[root].leafid; - dtree[root].nvtxs++; - if (sflag[i]) - dtree[root].nsvtxs++; - } - } - - mprintf("NChanges: %D\n", nchanges); - - BuildDTLeafContents(cinfo, sflag); - -return; -for (i=0; innodes; i++) { - if (dtree[i].leafid != -1) - mprintf("%4D %4D %4D %4D\n", dtree[i].nvtxs, dtree[i].nsvtxs, dtree[i].leafid, dtree[i].partid); -} -} - - -/************************************************************************* -* This function is the entry point for KMETIS -**************************************************************************/ -void *METIS_SetupContact0(idxtype *nvtxs, double *xyzcoords, idxtype *sflag, - idxtype *nparts, idxtype *part) -{ - idxtype i, j, ii, dim, ncontacts, wgtflag, mcnumflag, nnodes, nlnodes, nclean, naclean, ndirty, maxdepth, rwgtflag, rnumflag; - idxtype *mcvwgt, *dtpart, *marker, *leafpart, *csflag, *cpart; - idxtype *adjwgt; - GraphType graph, *cgraph; - ContactInfoType *cinfo; - DKeyValueType *xyzcand[3]; - - - /*--------------------------------------------------------------------- - * Allocate memory for the contact info type - *---------------------------------------------------------------------*/ - cinfo = (ContactInfoType *)gk_malloc(sizeof(ContactInfoType), "METIS_PartGraphForContact: cinfo"); - cinfo->leafptr = idxsmalloc(*nvtxs+1, 0, "METIS_PartGraphForContact: leafptr"); - cinfo->leafind = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: leafind"); - cinfo->leafwgt = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: leafwgt"); - cinfo->part = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: part"); - leafpart = cinfo->leafpart = idxmalloc(*nvtxs, "METIS_PartGraphForContact: leafpart"); - cinfo->dtree = (DTreeNodeType *)gk_malloc(sizeof(DTreeNodeType)*(*nvtxs), "METIS_PartGraphForContact: cinfo->dtree"); - cinfo->nvtxs = *nvtxs; - - - /*--------------------------------------------------------------------- - * Induce the decission tree - *---------------------------------------------------------------------*/ - dtpart = idxmalloc(*nvtxs, "METIS_PartGraphForContact: dtpart"); - marker = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: marker"); - - for (dim=0; dim<3; dim++) - xyzcand[dim] = (DKeyValueType *)gk_malloc(sizeof(DKeyValueType)*(*nvtxs), "METIS_PartGraphForContact: xyzcand[dim]"); - for (ncontacts=0, i=0; i<*nvtxs; i++) { - if (sflag[i]) { - for (dim=0; dim<3; dim++) { - xyzcand[dim][ncontacts].key = xyzcoords[3*i+dim]; - xyzcand[dim][ncontacts].val = i; - } - ncontacts++; - } - } - for (dim=0; dim<3; dim++) - idkeysort(ncontacts, xyzcand[dim]); - - - nnodes = nlnodes = nclean = naclean = ndirty = maxdepth = 0; - InduceDecissionTree(ncontacts, xyzcand, sflag, *nparts, part, ncontacts, 1, 1.00, - &nnodes, &nlnodes, cinfo->dtree, leafpart, dtpart, &nclean, - &naclean, &ndirty, &maxdepth, marker); - - mprintf("NNodes: %5D, NLNodes: %5D, NClean: %5D, NAClean: %5D, NDirty: %5D, MaxDepth: %3D\n", nnodes, nlnodes, nclean, naclean, ndirty, maxdepth); - - - /*--------------------------------------------------------------------- - * Populate the remaining fields of the cinfo data structure - *---------------------------------------------------------------------*/ - cinfo->nnodes = nnodes; - cinfo->nleafs = nlnodes; - idxcopy(*nvtxs, part, cinfo->part); - - BuildDTLeafContents(cinfo, sflag); - - CheckDTreeSurface(*nvtxs, xyzcoords, part, cinfo, sflag); - - gk_free((void **)&dtpart, &xyzcand[0], &xyzcand[1], &xyzcand[2], &marker, LTERM); - -/* -for (i=0; idtree[i].leafid, cinfo->dtree[i].nsvtxs); -*/ - - return (void *)cinfo; -} - - -/************************************************************************* -* This function is the entry point for KMETIS -**************************************************************************/ -void *METIS_SetupContact(idxtype *nvtxs, double *xyzcoords, idxtype *sflag, - idxtype *nparts, idxtype *part) -{ - idxtype i, j, ii, dim, ncontacts, wgtflag, mcnumflag, nnodes, nlnodes, nclean, naclean, ndirty, maxdepth, rwgtflag, rnumflag; - idxtype *mcvwgt, *dtpart, *marker, *leafpart, *csflag, *cpart; - idxtype *adjwgt; - GraphType graph, *cgraph; - ContactInfoType *cinfo; - DKeyValueType *xyzcand[3]; - - - /*--------------------------------------------------------------------- - * Allocate memory for the contact info type - *---------------------------------------------------------------------*/ - cinfo = (ContactInfoType *)gk_malloc(sizeof(ContactInfoType), "METIS_PartGraphForContact: cinfo"); - cinfo->leafptr = idxsmalloc(*nvtxs+1, 0, "METIS_PartGraphForContact: leafptr"); - cinfo->leafind = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: leafind"); - cinfo->leafwgt = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: leafwgt"); - cinfo->part = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: part"); - leafpart = cinfo->leafpart = idxmalloc(*nvtxs, "METIS_PartGraphForContact: leafpart"); - cinfo->dtree = (DTreeNodeType *)gk_malloc(sizeof(DTreeNodeType)*(*nvtxs), "METIS_PartGraphForContact: cinfo->dtree"); - cinfo->nvtxs = *nvtxs; - - - /*--------------------------------------------------------------------- - * Induce the decission tree - *---------------------------------------------------------------------*/ - dtpart = idxmalloc(*nvtxs, "METIS_PartGraphForContact: dtpart"); - marker = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: marker"); - - for (dim=0; dim<3; dim++) { - xyzcand[dim] = (DKeyValueType *)gk_malloc(sizeof(DKeyValueType)*(*nvtxs), "METIS_PartGraphForContact: xyzcand[dim]"); - for (i=0; i<*nvtxs; i++) { - xyzcand[dim][i].key = xyzcoords[3*i+dim]; - xyzcand[dim][i].val = i; - } - idkeysort(*nvtxs, xyzcand[dim]); - } - - - nnodes = nlnodes = nclean = naclean = ndirty = maxdepth = 0; - InduceDecissionTree(*nvtxs, xyzcand, sflag, *nparts, part, - *nvtxs, 1, 1.00, - &nnodes, &nlnodes, cinfo->dtree, leafpart, dtpart, - &nclean, &naclean, &ndirty, &maxdepth, marker); - - mprintf("NNodes: %5D, NLNodes: %5D, NClean: %5D, NAClean: %5D, NDirty: %5D, MaxDepth: %3D\n", nnodes, nlnodes, nclean, naclean, ndirty, maxdepth); - - - /*--------------------------------------------------------------------- - * Populate the remaining fields of the cinfo data structure - *---------------------------------------------------------------------*/ - cinfo->nnodes = nnodes; - cinfo->nleafs = nlnodes; - idxcopy(*nvtxs, part, cinfo->part); - - BuildDTLeafContents(cinfo, sflag); - - CheckDTree(*nvtxs, xyzcoords, part, cinfo); - - gk_free((void **)&dtpart, &xyzcand[0], &xyzcand[1], &xyzcand[2], &marker, LTERM); - -/* -for (i=0; idtree[i].leafid, cinfo->dtree[i].nsvtxs); -*/ - - return (void *)cinfo; -} - - -/************************************************************************* -* This function is the entry point for detecting contacts between -* bounding boxes and surface nodes -**************************************************************************/ -void METIS_FindContacts(void *raw_cinfo, idxtype *nboxes, double *boxcoords, idxtype *nparts, - idxtype **r_cntptr, idxtype **r_cntind) -{ - idxtype i, ncnts, tncnts, maxtncnts; - idxtype *cntptr, *cntind, *auxcntind, *stack, *marker; - ContactInfoType *cinfo; - - cinfo = (ContactInfoType *)raw_cinfo; - - maxtncnts = 6*(*nboxes); - cntptr = idxsmalloc(*nboxes+1, 0, "METIS_FindContacts: cntptr"); - cntind = idxmalloc(maxtncnts, "METIS_FindContacts: cntind"); - auxcntind = idxmalloc(*nparts, "METIS_FindContacts: auxcntind"); - stack = idxmalloc(cinfo->nnodes, "METIS_FindContacts: stack"); - marker = idxsmalloc(*nparts, 0, "METIS_FindContacts: marker"); - - - /* Go through each box and determine its contacting partitions */ - for (tncnts=0, i=0; i<*nboxes; i++) { - ncnts = FindBoxContacts(cinfo, boxcoords+i*6, stack, auxcntind, marker); - - if (ncnts == 0) - mprintf("CSearchError: Box has no contacts!\n"); - - if (ncnts + tncnts >= maxtncnts) { - maxtncnts += (tncnts+ncnts)*(*nboxes-i)/i; - if ((cntind = (idxtype *)realloc(cntind, maxtncnts*sizeof(idxtype))) == NULL) - errexit("Realloc failed! of %d words!\n", maxtncnts); - } - cntptr[i] = ncnts; - idxcopy(ncnts, auxcntind, cntind+tncnts); - tncnts += ncnts; - } - MAKECSR(i, *nboxes, cntptr); - - *r_cntptr = cntptr; - *r_cntind = cntind; - - gk_free((void **)&auxcntind, &stack, &marker, LTERM); - -} - - -/************************************************************************* -* This function is the entry point for KMETIS -**************************************************************************/ -void METIS_FreeContactInfo(void *raw_cinfo) -{ - ContactInfoType *cinfo; - - cinfo = (ContactInfoType *)raw_cinfo; - - gk_free((void **)&(cinfo->leafptr), &(cinfo->leafind), &(cinfo->leafwgt), &(cinfo->part), &(cinfo->leafpart), &(cinfo->dtree), &cinfo, LTERM); -} - - - -/****************************************************************************** -* This function creates a coarse graph corresponding to the partitioning vector -*******************************************************************************/ -GraphType *CreatePartitionGraphForContact(idxtype nvtxs, idxtype *xadj, idxtype *adjncy, - idxtype *vwgt, idxtype *adjwgt, idxtype cnvtxs, idxtype *part) -{ - idxtype i, ii, j, jj, k, cnedges; - idxtype *cxadj, *cadjncy, *cvwgt, *cadjwgt; - idxtype *ptr, *ind, *marker; - GraphType *cgraph; - - ptr = idxsmalloc(cnvtxs+1, 0, "CreatePartitionGraph: ptr"); - ind = idxmalloc(nvtxs, "CreatePartitionGraph: ind"); - marker = idxsmalloc(cnvtxs, -1, "CreatePartitionGraph: marker"); - - cgraph = CreateGraph(); - - cgraph->ncon = 2; - cgraph->nvtxs = cnvtxs; - cxadj = cgraph->xadj = idxsmalloc(cnvtxs+1, 0, "CreatePartitionGraph: cxadj"); - cadjncy = cgraph->adjncy = idxmalloc(xadj[nvtxs], "CreatePartitionGraph: cadjncy"); - cvwgt = cgraph->vwgt = idxmalloc(2*cnvtxs, "CreatePartitionGraph: cvwgt"); - cadjwgt = cgraph->adjwgt = idxmalloc(xadj[nvtxs], "CreatePartitionGraph: cadjwgt"); - - - for (i=0; i 0 ? 1 : 0); - //mprintf("LEAF3: %5D %5D Skipping small node!\n", nvtxs, k); - - *r_ndirty += nvtxs*k; - isleaf = 1; - } else if (nvtxs < maxnvtxs && tpwgts[pid] >= (int)(minfrac*nvtxs)) { /* Determine if mostly one class */ - //mprintf("LEAF2: %5D %5D %4D Almost pure node!\n", nvtxs, tpwgts[idxargmax(nparts, tpwgts)], idxargmax(nparts, tpwgts)); - *r_naclean += nvtxs; - isleaf = 1; - } else { /* Check if all coordinates are the same */ - for (dim=0; dim<3; dim++) { - for (i=1; i DEPSILON) - break; - } - if (i != nvtxs) - break; - } - - if (dim == 3) { /* All coordinates matched! Treat it as a dirty node! */ - for (k=0, i=0; i 0 ? 1 : 0); - mprintf("LEAF4: %5D %5D Skipping same coord-nodes! (%D %D)\n", nvtxs, k, isclean, part[xyzcand[0][0].val]); - - *r_ndirty += nvtxs*k; - isleaf = 1; - } - } - - if (isleaf) { - for (i=0; i DEPSILON) { - scores[dim] = sqrt(sum2[0])+sqrt(sum2[1]); - points[dim] = (xyzcand[dim][i].key+xyzcand[dim][i+1].key)/2.0; - lnvtxs[dim] = nleft; - break; - } - } - -#ifdef PRINTSTAT - if (i == nvtxs-1) - mprintf("DTree: Initial Scan Along dim %D failed!\n", dim); -#endif - - - /* Continue with the rest */ - for (i++; i= nvtxs/2) { - if (fabs(xyzcand[dim][i].key - xyzcand[dim][i+1].key) < DEPSILON) - continue; - - scores[dim] = xyzcand[dim][nvtxs-1].key - xyzcand[dim][0].key; /* Use the axis span as the score */ - points[dim] = (xyzcand[dim][i].key+xyzcand[dim][i+1].key)/2.0; - lnvtxs[dim] = nleft; - break; - } - } - else { - if (newscore > scores[dim]) { - if (fabs(xyzcand[dim][i].key - xyzcand[dim][i+1].key) < DEPSILON) - continue; - - scores[dim] = newscore; - points[dim] = (xyzcand[dim][i].key+xyzcand[dim][i+1].key)/2.0; - lnvtxs[dim] = nleft; - } - } - - //mprintf("%5D %f %f %f\n", nleft, newscore, sum2[0], sum2[1]); - } - -#ifdef PRINTSTAT - /* Print some Stats */ - if (scores[dim] >= 0) { - mprintf("Dim: %3D, Score: %f, Point: %f [%5D %5D] [%f %f]\n", dim, scores[dim], - points[dim], lnvtxs[dim], nvtxs-lnvtxs[dim], xyzcand[dim][0].key, xyzcand[dim][nvtxs-1].key); - idxcopy(nparts, tpwgts, pwgts[1]); - idxset(nparts, 0, pwgts[0]); - for (i=0; i 0) - mprintf("%5D => %5D %5D\n", j, pwgts[0][j], pwgts[1][j]); - } -#endif - } - - /* Determine the best overall score */ - bestdim = 0; - bestscore = scores[0]; - bestpoint = points[0]; - for (dim=1; dim<3; dim++) { - if (scores[dim] > bestscore) { - bestscore = scores[dim]; - bestpoint = points[dim]; - bestdim = dim; - } - } - - if (bestscore <= 0.0) - errexit("Major Failure... Non-seperable! %4d nodes. Needs to be fixed!\n", nvtxs); - - - dtree[mynodeID].dim = bestdim; - dtree[mynodeID].value = bestpoint; - - //mprintf("BestDim: %D!\n", bestdim); - - - /*----------------------------------------------------------------------- - * Ok, now go and do the split - *-----------------------------------------------------------------------*/ - nleft = lnvtxs[bestdim]; - nright = nvtxs - nleft; - - for (dim=0; dim<3; dim++) { - lxyzcand[dim] = (DKeyValueType *)gk_malloc(sizeof(DKeyValueType)*nleft, "InduceDecissionTree: lxyzcand[dim]"); - rxyzcand[dim] = (DKeyValueType *)gk_malloc(sizeof(DKeyValueType)*nright, "InduceDecissionTree: rxyzcand[dim]"); - } - - /* Mark the left vertices */ - for (i=0; invtxs; - nleafs = cinfo->nleafs; - part = cinfo->part; - leafpart = cinfo->leafpart; - leafptr = cinfo->leafptr; - leafind = cinfo->leafind; - leafwgt = cinfo->leafwgt; - - cand = (KeyValueType *)gk_malloc(sizeof(KeyValueType)*nvtxs, "BuildDTLeafContents: cand"); - - for (ncontacts=0, i=0; i 1) { - mprintf("%4D, ", i); - for (j=leafptr[i]; j ", i); - for (j=leafptr[i]; jleafptr; - leafind = cinfo->leafind; - dtree = cinfo->dtree; - -//mprintf("%e %e %e %e %e %e\n", coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); - - stack[0] = 0; - k = 1; - ncnts = 0; - while (k > 0) { - root = stack[--k]; - - /* See if you hit a leaf */ - if ((leafid = dtree[root].leafid) != -1) { -//mprintf("Got to a leaf.... %D %D %D\n", leafid, dtree[root].nsvtxs, leafptr[leafid+1]-leafptr[leafid]); - - if (dtree[root].nsvtxs > 0) { - /* Add unique processor IDs. */ - for (j=leafptr[leafid]; j ", dtree[root].dim, dtree[root].value, dtree[root].left, dtree[root].right); - - if (coords[dtree[root].dim] <= dtree[root].value) { /* min <= value */ - stack[k++] = dtree[root].left; -//mprintf(" %D", stack[k-1]); - } - - if (coords[3+dtree[root].dim] >= dtree[root].value) { /* max >= value */ - stack[k++] = dtree[root].right; -//mprintf(" %D", stack[k-1]); - } - -//mprintf("\n"); - - } - - for (i=0; ileafptr; - leafind = cinfo->leafind; - dtree = cinfo->dtree; - - for (i=0; ileafpart[i] != dtree[root].leafid) - mprintf("DTError! %4D %4D %4D %4D %4D\n", i, cinfo->leafpart[i], dtree[root].leafid, part[i], leafind[leafptr[dtree[root].leafid]]); - } - -} - - -/*********************************************************************************** -* This function checks the DT to see if it properly "classifies" all points -************************************************************************************/ -void CheckDTreeSurface(idxtype nvtxs, double *xyzcoords, idxtype *part, ContactInfoType *cinfo, idxtype *sflag) -{ - idxtype i, j, k, root; - idxtype *leafptr, *leafind; - DTreeNodeType *dtree; - - leafptr = cinfo->leafptr; - leafind = cinfo->leafind; - dtree = cinfo->dtree; - - for (i=0; ileafpart[i] != dtree[root].leafid) - mprintf("SDTError! %4D %4D %4D %4D %4D\n", i, cinfo->leafpart[i], dtree[root].leafid, part[i], leafind[leafptr[dtree[root].leafid]]); - } - -} - - - - - - - -/************************************************************************* -* This function is the entry point for KMETIS -**************************************************************************/ -void *METIS_PartSurfForContactRCB(idxtype *nvtxs, double *xyzcoords, idxtype *sflag, - idxtype *nparts, idxtype *part, idxtype *bestdims) -{ - idxtype i, j, nsurf, dim, ncon, nnodes, nlnodes; - idxtype *marker, *spart; - ContactInfoType *cinfo; - DKeyValueType *xyzcand[3]; - double *myxyzcoords; - - - /*--------------------------------------------------------------------- - * Allocate memory for the contact info type - *---------------------------------------------------------------------*/ - cinfo = (ContactInfoType *)gk_malloc(sizeof(ContactInfoType), "METIS_PartGraphForContact: cinfo"); - cinfo->leafptr = idxsmalloc(*nvtxs+1, 0, "METIS_PartGraphForContact: leafptr"); - cinfo->leafind = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: leafind"); - cinfo->leafwgt = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: leafwgt"); - cinfo->part = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: part"); - cinfo->leafpart = idxmalloc(*nvtxs, "METIS_PartGraphForContact: leafpart"); - cinfo->dtree = (DTreeNodeType *)gk_malloc(sizeof(DTreeNodeType)*(*nvtxs), "METIS_PartGraphForContact: cinfo->dtree"); - - /*--------------------------------------------------------------------- - * Induce the decission tree - *---------------------------------------------------------------------*/ - myxyzcoords = gk_dmalloc(3*(*nvtxs), "METIS_PartSurfForContactRCB: myxyzcoords"); - marker = idxsmalloc(*nvtxs, 0, "METIS_PartGraphForContact: marker"); - - for (dim=0; dim<3; dim++) { - xyzcand[dim] = (DKeyValueType *)gk_malloc(sizeof(DKeyValueType)*(*nvtxs), "METIS_PartGraphForContact: xyzcand[dim]"); - - for (nsurf=0, i=0; i<*nvtxs; i++) { - if (sflag[i]) { - myxyzcoords[3*nsurf+dim] = xyzcoords[3*i+dim]; - xyzcand[dim][nsurf].key = xyzcoords[3*i+dim]; - xyzcand[dim][nsurf].val = nsurf++; - } - } - idkeysort(nsurf, xyzcand[dim]); - } - - spart = idxsmalloc(nsurf, 0, "METIS_PartGraphForContact: spart"); - - nnodes = nlnodes = 0; - InduceRCBTree(nsurf, xyzcand, 0, *nparts, &nnodes, &nlnodes, cinfo->dtree, cinfo->leafpart, spart, marker, bestdims); - - mprintf("NNodes: %5D, NLNodes: %5D\n", nnodes, nlnodes); - - /* Project the partition back to the original space */ - for (nsurf=0, i=0; i<*nvtxs; i++) - part[i] = (sflag[i] ? spart[nsurf++] : -1); - - - /*--------------------------------------------------------------------- - * Populate the remaining fields of the cinfo data structure - *---------------------------------------------------------------------*/ - cinfo->nvtxs = nsurf; - cinfo->nnodes = nnodes; - cinfo->nleafs = nlnodes; - idxcopy(nsurf, spart, cinfo->part); - - idxset(nsurf, 1, marker); - - BuildDTLeafContents(cinfo, marker); - - CheckDTree(nsurf, myxyzcoords, spart, cinfo); - - gk_free((void **)&xyzcand[0], &xyzcand[1], &xyzcand[2], &myxyzcoords, &marker, &spart, LTERM); - - for (i=0; innodes; i++) - bestdims[i] = cinfo->dtree[i].dim; - - return (void *)cinfo; -} - - - -/************************************************************************* -* This function induces a DT that satisfied the given size requirements -**************************************************************************/ -idxtype InduceRCBTree(idxtype nvtxs, DKeyValueType **xyzcand, idxtype firstPID, idxtype nparts, - idxtype *r_nnodes, idxtype *r_nlnodes, DTreeNodeType *dtree, idxtype *leafpart, - idxtype *part, idxtype *marker, idxtype *oldBestDims) -{ - idxtype i, nr, nl, j, k, mynodeID, dim, bestdim, lnvtxs, rnvtxs, lnparts, rnparts; - DKeyValueType *lxyzcand[3], *rxyzcand[3]; - double bestpoint; - - - mynodeID = (*r_nnodes)++; - dtree[mynodeID].nvtxs = nvtxs; - dtree[mynodeID].nsvtxs = nvtxs; - dtree[mynodeID].leafid = -1; - - - /*----------------------------------------------------------------------- - * Check the exit conditions - *-----------------------------------------------------------------------*/ - if (nparts == 1) { - //mprintf("Pid:%D, Size:%D\n", firstPID, nvtxs); - for (i=0; i DEPSILON) - break; - } - lnvtxs++; - rnvtxs = nvtxs - lnvtxs; - } - - if (rnvtxs <= 0) { - if (bestdim != -1) - mprintf("Finding a dimension for %D points...\n", nvtxs); - - lnparts = nparts/2; - rnparts = nparts - lnparts; - lnvtxs = nvtxs*lnparts/nparts; - - for (bestdim=0, i=1; i<3; i++) - bestdim = (xyzcand[i][nvtxs-1].key - xyzcand[i][0].key > xyzcand[bestdim][nvtxs-1].key - xyzcand[bestdim][0].key ? i : bestdim); - - for (; lnvtxs DEPSILON) - break; - } - lnvtxs++; - rnvtxs = nvtxs - lnvtxs; - } - - - dtree[mynodeID].dim = bestdim; - dtree[mynodeID].value = bestpoint = (xyzcand[bestdim][lnvtxs-1].key+xyzcand[bestdim][lnvtxs].key)/2; - - /* Print some Stats */ - //mprintf("Dim: %3D, Point: %f [%5D %5D] [%3D %3D]\n", bestdim, bestpoint, lnvtxs, rnvtxs, lnparts, rnparts); - - /*----------------------------------------------------------------------- - * Ok, now go and do the split - *-----------------------------------------------------------------------*/ - for (dim=0; dim<3; dim++) { - lxyzcand[dim] = (DKeyValueType *)gk_malloc(sizeof(DKeyValueType)*lnvtxs, "InduceDecissionTree: lxyzcand[dim]"); - rxyzcand[dim] = (DKeyValueType *)gk_malloc(sizeof(DKeyValueType)*rnvtxs, "InduceDecissionTree: rxyzcand[dim]"); - } - - /* Mark the left vertices */ - for (i=0; i - - -/************************************************************************* -* This function takes a graph and creates a sequence of coarser graphs -**************************************************************************/ -GraphType *Coarsen2Way(CtrlType *ctrl, GraphType *graph) -{ - idxtype clevel; - GraphType *cgraph; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->CoarsenTmr)); - - cgraph = graph; - - /* The following is ahack to allow the multiple bisections to go through with correct - coarsening */ - if (ctrl->CType > 20) { - clevel = 1; - ctrl->CType -= 20; - } - else - clevel = 0; - - do { - IFSET(ctrl->dbglvl, DBG_COARSEN, mprintf("%6D %7D %7D [%D] [%D %D]\n", - cgraph->nvtxs, cgraph->nedges/2, idxsum(cgraph->nvtxs, cgraph->adjwgtsum, 1)/2, - ctrl->CoarsenTo, ctrl->maxvwgt, - (cgraph->vwgt ? idxsum(cgraph->nvtxs, cgraph->vwgt, 1) : cgraph->nvtxs))); - - if (cgraph->adjwgt) { - switch (ctrl->CType) { - case MTYPE_RM: - Match_RM(ctrl, cgraph); - break; - case MTYPE_HEM: - if (clevel < 1 || cgraph->nedges == 0) - Match_RM(ctrl, cgraph); - else - Match_HEM(ctrl, cgraph); - break; - case MTYPE_SHEM: - if (clevel < 1 || cgraph->nedges == 0) - Match_RM(ctrl, cgraph); - else - Match_SHEM(ctrl, cgraph); - break; - case MTYPE_SHEMKWAY: - if (cgraph->nedges == 0) - Match_RM(ctrl, cgraph); - else - Match_SHEM(ctrl, cgraph); - break; - default: - errexit("Unknown CType: %d\n", ctrl->CType); - } - } - else { - Match_RM_NVW(ctrl, cgraph); - } - - cgraph = cgraph->coarser; - clevel++; - - } while (cgraph->nvtxs > ctrl->CoarsenTo && cgraph->nvtxs < COARSEN_FRACTION2*cgraph->finer->nvtxs && cgraph->nedges > cgraph->nvtxs/2); - - IFSET(ctrl->dbglvl, DBG_COARSEN, mprintf("%6D %7D %7D [%D] [%D %D]\n", - cgraph->nvtxs, cgraph->nedges/2, idxsum(cgraph->nvtxs, cgraph->adjwgtsum, 1)/2, - ctrl->CoarsenTo, ctrl->maxvwgt, - (cgraph->vwgt ? idxsum(cgraph->nvtxs, cgraph->vwgt, 1) : cgraph->nvtxs))); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->CoarsenTmr)); - - return cgraph; -} - diff --git a/src/metis/compress.c b/src/metis/compress.c deleted file mode 100644 index 860b9d6..0000000 --- a/src/metis/compress.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * compress.c - * - * This file contains code for compressing nodes with identical adjacency - * structure and for prunning dense columns - * - * Started 9/17/97 - * George - */ - -#include - -/************************************************************************* -* This function compresses a graph by merging identical vertices -* The compression should lead to at least 10% reduction. -**************************************************************************/ -void CompressGraph(CtrlType *ctrl, GraphType *graph, idxtype nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *cptr, idxtype *cind) -{ - idxtype i, ii, iii, j, jj, k, l, cnvtxs, cnedges; - idxtype *cxadj, *cadjncy, *cvwgt, *mark, *map; - KeyValueType *keys; - - mark = idxsmalloc(nvtxs, -1, "CompressGraph: mark"); - map = idxsmalloc(nvtxs, -1, "CompressGraph: map"); - keys = (KeyValueType *)gk_malloc(nvtxs*sizeof(KeyValueType), "CompressGraph: keys"); - - /* Compute a key for each adjacency list */ - for (i=0; i= COMPRESSION_FRACTION*nvtxs) { - graph->nvtxs = nvtxs; - graph->nedges = xadj[nvtxs]; - graph->ncon = 1; - graph->xadj = xadj; - graph->free_xadj = 0; - graph->adjncy = adjncy; - graph->free_adjncy = 0; - - graph->vwgt = idxmalloc(nvtxs, "CompressGraph: vwgt"); - graph->adjwgtsum = idxmalloc(nvtxs, "CompressGraph: adjwgtsum"); - graph->cmap = idxmalloc(nvtxs, "CompressGraph: cmap"); - graph->adjwgt = idxmalloc(graph->nedges, "CompressGraph: adjwgt"); - - idxset(nvtxs, 1, graph->vwgt); - idxset(graph->nedges, 1, graph->adjwgt); - for (i=0; iadjwgtsum[i] = xadj[i+1]-xadj[i]; - - graph->label = idxmalloc(nvtxs, "CompressGraph: label"); - for (i=0; ilabel[i] = i; - } - else { /* Ok, form the compressed graph */ - cnedges = 0; - for (i=0; ixadj = idxmalloc(cnvtxs+1, "CompressGraph: xadj"); - cvwgt = graph->vwgt = idxmalloc(cnvtxs, "CompressGraph: vwgt"); - graph->adjwgtsum = idxmalloc(cnvtxs, "CompressGraph: adjwgtsum"); - graph->cmap = idxmalloc(cnvtxs, "CompressGraph: cmap"); - cadjncy = graph->adjncy = idxmalloc(cnedges, "CompressGraph: adjncy"); - graph->adjwgt = idxmalloc(cnedges, "CompressGraph: adjwgt"); - - /* Now go and compress the graph */ - idxset(nvtxs, -1, mark); - l = cxadj[0] = 0; - for (i=0; invtxs = cnvtxs; - graph->nedges = l; - graph->ncon = 1; - - idxset(graph->nedges, 1, graph->adjwgt); - for (i=0; iadjwgtsum[i] = cxadj[i+1]-cxadj[i]; - - graph->label = idxmalloc(cnvtxs, "CompressGraph: label"); - for (i=0; ilabel[i] = i; - - } - - gk_free((void **)&keys, &map, &mark, LTERM); -} - - - -/************************************************************************* -* This function prunes all the vertices in a graph with degree greater -* than factor*average -**************************************************************************/ -void PruneGraph(CtrlType *ctrl, GraphType *graph, idxtype nvtxs, idxtype *xadj, - idxtype *adjncy, idxtype *iperm, float factor) -{ - idxtype i, j, k, l, nlarge, pnvtxs, pnedges; - idxtype *pxadj, *padjncy, *padjwgt; - idxtype *perm; - - perm = idxmalloc(nvtxs, "PruneGraph: perm"); - - factor = factor*xadj[nvtxs]/nvtxs; - - pnvtxs = pnedges = nlarge = 0; - for (i=0; invtxs = nvtxs; - graph->nedges = xadj[nvtxs]; - graph->ncon = 1; - graph->xadj = xadj; - graph->free_xadj = 0; - graph->adjncy = adjncy; - graph->free_adjncy = 0; - - graph->vwgt = idxmalloc(nvtxs, "PruneGraph: vwgt"); - graph->adjwgtsum = idxmalloc(nvtxs, "PruneGraph: adjwgtsum"); - graph->cmap = idxmalloc(nvtxs, "PruneGraph: cmap"); - graph->adjwgt = idxmalloc(graph->nedges, "PruneGraph: adjwgt"); - - idxset(nvtxs, 1, graph->vwgt); - idxset(graph->nedges, 1, graph->adjwgt); - for (i=0; iadjwgtsum[i] = xadj[i+1]-xadj[i]; - - graph->label = idxmalloc(nvtxs, "CompressGraph: label"); - for (i=0; ilabel[i] = i; - } - else { /* Prune the graph */ - /* Allocate memory for the prunned graph*/ - pxadj = graph->xadj = idxmalloc(pnvtxs+1, "PruneGraph: xadj"); - graph->vwgt = idxmalloc(pnvtxs, "PruneGraph: vwgt"); - graph->adjwgtsum = idxmalloc(pnvtxs, "PruneGraph: adjwgtsum"); - graph->cmap = idxmalloc(pnvtxs, "PruneGraph: cmap"); - padjncy = graph->adjncy = idxmalloc(pnedges, "PruneGraph: adjncy"); - graph->adjwgt = idxmalloc(pnedges, "PruneGraph: adjwgt"); - - pxadj[0] = pnedges = l = 0; - for (i=0; invtxs = pnvtxs; - graph->nedges = pnedges; - graph->ncon = 1; - - idxset(pnvtxs, 1, graph->vwgt); - idxset(pnedges, 1, graph->adjwgt); - for (i=0; iadjwgtsum[i] = pxadj[i+1]-pxadj[i]; - - graph->label = idxmalloc(pnvtxs, "CompressGraph: label"); - for (i=0; ilabel[i] = i; - } - - gk_free((void **)&perm, LTERM); - -} - - - - - - - - - diff --git a/src/metis/debug.c b/src/metis/debug.c deleted file mode 100644 index 8f48e14..0000000 --- a/src/metis/debug.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * debug.c - * - * This file contains code that performs self debuging - * - * Started 7/24/97 - * George - * - */ - -#include - -/************************************************************************* -* This function computes the cut given the graph and a where vector -**************************************************************************/ -idxtype ComputeCut(GraphType *graph, idxtype *where) -{ - idxtype i, j, cut; - - if (graph->adjwgt == NULL) { - for (cut=0, i=0; invtxs; i++) { - for (j=graph->xadj[i]; jxadj[i+1]; j++) - if (where[i] != where[graph->adjncy[j]]) - cut++; - } - } - else { - for (cut=0, i=0; invtxs; i++) { - for (j=graph->xadj[i]; jxadj[i+1]; j++) - if (where[i] != where[graph->adjncy[j]]) - cut += graph->adjwgt[j]; - } - } - - return cut/2; -} - - -/************************************************************************* -* This function computes the cut given the graph and a where vector -**************************************************************************/ -idxtype ComputeMaxCut(GraphType *graph, idxtype nparts, idxtype *where) -{ - idxtype i, j, maxcut; - idxtype *cuts; - - cuts = idxsmalloc(nparts, 0, "ComputeMaxCut: cuts"); - - if (graph->adjwgt == NULL) { - for (i=0; invtxs; i++) { - for (j=graph->xadj[i]; jxadj[i+1]; j++) - if (where[i] != where[graph->adjncy[j]]) - cuts[where[i]]++; - } - } - else { - for (i=0; invtxs; i++) { - for (j=graph->xadj[i]; jxadj[i+1]; j++) - if (where[i] != where[graph->adjncy[j]]) - cuts[where[i]] += graph->adjwgt[j]; - } - } - - maxcut = cuts[idxargmax(nparts, cuts)]; - - mprintf("%D => %D\n", idxargmax(nparts, cuts), maxcut); - - gk_free((void **)&cuts, LTERM); - - return maxcut; -} - - -/************************************************************************* -* This function checks whether or not the boundary information is correct -**************************************************************************/ -idxtype CheckBnd(GraphType *graph) -{ - idxtype i, j, nvtxs, nbnd; - idxtype *xadj, *adjncy, *where, *bndptr, *bndind; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - where = graph->where; - bndptr = graph->bndptr; - bndind = graph->bndind; - - for (nbnd=0, i=0; inbnd, ("%d %d\n", nbnd, graph->nbnd)); - - return 1; -} - - - -/************************************************************************* -* This function checks whether or not the boundary information is correct -**************************************************************************/ -idxtype CheckBnd2(GraphType *graph) -{ - idxtype i, j, nvtxs, nbnd, id, ed; - idxtype *xadj, *adjncy, *where, *bndptr, *bndind; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - where = graph->where; - bndptr = graph->bndptr; - bndind = graph->bndind; - - for (nbnd=0, i=0; iadjwgt[j]; - else - id += graph->adjwgt[j]; - } - if (ed - id >= 0 && xadj[i] < xadj[i+1]) { - nbnd++; - ASSERTP(bndptr[i] != -1, ("%d %d %d\n", i, id, ed)); - ASSERT(bndind[bndptr[i]] == i); - } - } - - ASSERTP(nbnd == graph->nbnd, ("%d %d\n", nbnd, graph->nbnd)); - - return 1; -} - -/************************************************************************* -* This function checks whether or not the boundary information is correct -**************************************************************************/ -idxtype CheckNodeBnd(GraphType *graph, idxtype onbnd) -{ - idxtype i, j, nvtxs, nbnd; - idxtype *xadj, *adjncy, *where, *bndptr, *bndind; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - where = graph->where; - bndptr = graph->bndptr; - bndind = graph->bndind; - - for (nbnd=0, i=0; indegrees; i++) { - for (j=i+1; jndegrees; j++) - ASSERTP(rinfo->edegrees[i].pid != rinfo->edegrees[j].pid, ("%d %d %d %d\n", i, j, rinfo->edegrees[i].pid, rinfo->edegrees[j].pid)); - } - - return 1; -} - - - -/************************************************************************* -* This function checks the correctness of the NodeFM data structures -**************************************************************************/ -idxtype CheckNodePartitionParams(GraphType *graph) -{ - idxtype i, j, k, l, nvtxs, me, other; - idxtype *xadj, *adjncy, *adjwgt, *vwgt, *where; - idxtype edegrees[2], pwgts[3]; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - - /*------------------------------------------------------------ - / Compute now the separator external degrees - /------------------------------------------------------------*/ - pwgts[0] = pwgts[1] = pwgts[2] = 0; - for (i=0; inrinfo[i].edegrees[0] || edegrees[1] != graph->nrinfo[i].edegrees[1]) { - mprintf("Something wrong with edegrees: %D %D %D %D %D\n", i, edegrees[0], edegrees[1], graph->nrinfo[i].edegrees[0], graph->nrinfo[i].edegrees[1]); - return 0; - } - } - } - - if (pwgts[0] != graph->pwgts[0] || pwgts[1] != graph->pwgts[1] || pwgts[2] != graph->pwgts[2]) - mprintf("Something wrong with part-weights: %D %D %D %D %D %D\n", pwgts[0], pwgts[1], pwgts[2], graph->pwgts[0], graph->pwgts[1], graph->pwgts[2]); - - return 1; -} - - -/************************************************************************* -* This function checks if the separator is indeed a separator -**************************************************************************/ -idxtype IsSeparable(GraphType *graph) -{ - idxtype i, j, nvtxs, other; - idxtype *xadj, *adjncy, *where; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - where = graph->where; - - for (i=0; i - -/************************************************************************* -* This function computes how much memory will be required by the various -* routines in METIS -**************************************************************************/ -void METIS_EstimateMemory(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *optype, idxtype *nbytes) -{ - idxtype i, j, k, nedges, nlevels; - float vfraction, efraction, vmult, emult; - idxtype coresize, gdata, rdata; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - nedges = xadj[*nvtxs]; - - InitRandom(-1); - EstimateCFraction(*nvtxs, xadj, adjncy, &vfraction, &efraction); - - /* Estimate the amount of memory for coresize */ - if (*optype == 2) - coresize = nedges; - else - coresize = 0; - coresize += nedges + 11*(*nvtxs) + 4*1024 + 2*(NEG_GAINSPAN+PLUS_GAINSPAN+1)*(sizeof(ListNodeType *)/sizeof(idxtype)); - coresize += 2*(*nvtxs); /* add some more fore other vectors */ - - gdata = nedges; /* Agk_fsume that the user does not pass weights */ - - nlevels = (int)(log(100.0/(*nvtxs))/log(vfraction) + .5); - vmult = 0.5 + (1.0 - pow(vfraction, nlevels))/(1.0 - vfraction); - emult = 1.0 + (1.0 - pow(efraction, nlevels+1))/(1.0 - efraction); - - gdata += vmult*4*(*nvtxs) + emult*2*nedges; - if ((vmult-1.0)*4*(*nvtxs) + (emult-1.0)*2*nedges < 5*(*nvtxs)) - rdata = 0; - else - rdata = 5*(*nvtxs); - - *nbytes = sizeof(idxtype)*(coresize+gdata+rdata+(*nvtxs)); - - if (*numflag == 1) - Change2FNumbering2(*nvtxs, xadj, adjncy); -} - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void EstimateCFraction(idxtype nvtxs, idxtype *xadj, idxtype *adjncy, float *vfraction, float *efraction) -{ - idxtype i, ii, j, cnvtxs, cnedges, maxidx; - idxtype *match, *cmap, *perm; - - cmap = idxmalloc(nvtxs, "cmap"); - match = idxsmalloc(nvtxs, UNMATCHED, "match"); - perm = idxmalloc(nvtxs, "perm"); - RandomPermute(nvtxs, perm, 1); - - cnvtxs = 0; - for (ii=0; ii - - -/************************************************************************* -* This function performs an edge-based FM refinement -**************************************************************************/ -void FM_2WayEdgeRefine(CtrlType *ctrl, GraphType *graph, idxtype *tpwgts, idxtype npasses) -{ - idxtype i, ii, j, k, kwgt, nvtxs, nbnd, nswaps, from, to, pass, me, limit, tmp; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind, *pwgts; - idxtype *moved, *swaps, *perm; - PQueueType parts[2]; - idxtype higain, oldgain, mincut, mindiff, origdiff, initcut, newcut, mincutorder, avgvwgt; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - id = graph->id; - ed = graph->ed; - pwgts = graph->pwgts; - bndptr = graph->bndptr; - bndind = graph->bndind; - - moved = idxwspacemalloc(ctrl, nvtxs); - swaps = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - - limit = amin(amax(0.01*nvtxs, 15), 100); - avgvwgt = amin((pwgts[0]+pwgts[1])/20, 2*(pwgts[0]+pwgts[1])/nvtxs); - - tmp = graph->adjwgtsum[idxargmax(nvtxs, graph->adjwgtsum)]; - PQueueInit(ctrl, &parts[0], nvtxs, tmp); - PQueueInit(ctrl, &parts[1], nvtxs, tmp); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D] T[%6D %6D], Nv-Nb[%6D %6D]. ICut: %6D\n", - pwgts[0], pwgts[1], tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); - - origdiff = idxtype_abs(tpwgts[0]-pwgts[0]); - idxset(nvtxs, -1, moved); - for (pass=0; passmincut; - mindiff = idxtype_abs(tpwgts[0]-pwgts[0]); - - ASSERT(ComputeCut(graph, where) == graph->mincut); - ASSERT(CheckBnd(graph)); - - /* Insert boundary nodes in the priority queues */ - nbnd = graph->nbnd; - RandomPermute(nbnd, perm, 1); - for (ii=0; ii 0 || id[bndind[i]] == 0); - ASSERT(bndptr[bndind[i]] != -1); - PQueueInsert(&parts[where[bndind[i]]], bndind[i], ed[bndind[i]]-id[bndind[i]]); - } - - for (nswaps=0; nswaps limit) { /* We hit the limit, undo last move */ - newcut += (ed[higain]-id[higain]); - INC_DEC(pwgts[from], pwgts[to], vwgt[higain]); - break; - } - - where[higain] = to; - moved[higain] = nswaps; - swaps[nswaps] = higain; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, - mprintf("Moved %6D from %D. [%3D %3D] %5D [%4D %4D]\n", higain, from, ed[higain]-id[higain], vwgt[higain], newcut, pwgts[0], pwgts[1])); - - /************************************************************** - * Update the id[i]/ed[i] values of the affected nodes - ***************************************************************/ - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - - for (j=xadj[higain]; j 0) { /* It will now become a boundary vertex */ - BNDInsert(nbnd, bndind, bndptr, k); - if (moved[k] == -1) - PQueueInsert(&parts[where[k]], k, ed[k]-id[k]); - } - } - } - - } - - - /**************************************************************** - * Roll back computations - *****************************************************************/ - for (i=0; imincutorder; nswaps--) { - higain = swaps[nswaps]; - - to = where[higain] = (where[higain]+1)%2; - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - else if (ed[higain] > 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - INC_DEC(pwgts[to], pwgts[(to+1)%2], vwgt[higain]); - for (j=xadj[higain]; j 0) - BNDInsert(nbnd, bndind, bndptr, k); - } - } - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\tMinimum cut: %6D at %5D, PWGTS: [%6D %6D], NBND: %6D\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd)); - - graph->mincut = mincut; - graph->nbnd = nbnd; - - if (mincutorder == -1 || mincut == initcut) - break; - } - - PQueueFree(ctrl, &parts[0]); - PQueueFree(ctrl, &parts[1]); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - -} - - diff --git a/src/metis/fortran.c b/src/metis/fortran.c deleted file mode 100644 index 4d85379..0000000 --- a/src/metis/fortran.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * fortran.c - * - * This file contains code for the fortran to C interface - * - * Started 8/19/97 - * George - * - */ - -#include - - -/************************************************************************* -* This function changes the numbering to start from 0 instead of 1 -**************************************************************************/ -void Change2CNumbering(idxtype nvtxs, idxtype *xadj, idxtype *adjncy) -{ - idxtype i, nedges; - - for (i=0; i<=nvtxs; i++) - xadj[i]--; - - nedges = xadj[nvtxs]; - for (i=0; i - - -void METIS_PARTGRAPHRECURSIVE(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_PartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} -void metis_partgraphrecursive(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_PartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} -void metis_partgraphrecursive_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_PartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} -void metis_partgraphrecursive__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_PartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} - - -void METIS_WPARTGRAPHRECURSIVE(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part); -} -void metis_wpartgraphrecursive(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part); -} -void metis_wpartgraphrecursive_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part); -} -void metis_wpartgraphrecursive__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part); -} - - - -void METIS_PARTGRAPHKWAY(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_PartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} -void metis_partgraphkway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_PartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} -void metis_partgraphkway_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_PartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} -void metis_partgraphkway__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_PartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} - - - -void METIS_WPARTGRAPHKWAY(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part); -} -void metis_wpartgraphkway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part); -} -void metis_wpartgraphkway_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part); -} -void metis_wpartgraphkway__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part); -} - - - -void METIS_EDGEND(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_EdgeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm); -} -void metis_edgend(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_EdgeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm); -} -void metis_edgend_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_EdgeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm); -} -void metis_edgend__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_EdgeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm); -} - - - -void METIS_NODEND(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_NodeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm); -} -void metis_nodend(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_NodeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm); -} -void metis_nodend_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_NodeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm); -} -void metis_nodend__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_NodeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm); -} - - - -void METIS_NODEWND(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_NodeWND(nvtxs, xadj, adjncy, vwgt, numflag, options, perm, iperm); -} -void metis_nodewnd(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_NodeWND(nvtxs, xadj, adjncy, vwgt, numflag, options, perm, iperm); -} -void metis_nodewnd_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_NodeWND(nvtxs, xadj, adjncy, vwgt, numflag, options, perm, iperm); -} -void metis_nodewnd__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *numflag, idxtype *options, idxtype *perm, idxtype *iperm) -{ - METIS_NodeWND(nvtxs, xadj, adjncy, vwgt, numflag, options, perm, iperm); -} - - -#ifdef XXXX -void METIS_PARTMESHNODAL(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart) -{ - METIS_PartMeshNodal(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart); -} -void metis_partmeshnodal(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart) -{ - METIS_PartMeshNodal(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart); -} -void metis_partmeshnodal_(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart) -{ - METIS_PartMeshNodal(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart); -} -void metis_partmeshnodal__(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart) -{ - METIS_PartMeshNodal(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart); -} - - -void METIS_PARTMESHDUAL(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart) -{ - METIS_PartMeshDual(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart, 0, NULL); -} -void metis_partmeshdual(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart) -{ - METIS_PartMeshDual(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart, 0, NULL); -} -void metis_partmeshdual_(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart) -{ - METIS_PartMeshDual(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart, 0, NULL); -} -void metis_partmeshdual__(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart) -{ - METIS_PartMeshDual(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart, 0, NULL); -} - - -void METIS_MESHTONODAL(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - METIS_MeshToNodal(ne, nn, elmnts, etype, numflag, dxadj, dadjncy); -} -void metis_meshtonodal(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - METIS_MeshToNodal(ne, nn, elmnts, etype, numflag, dxadj, dadjncy); -} -void metis_meshtonodal_(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - METIS_MeshToNodal(ne, nn, elmnts, etype, numflag, dxadj, dadjncy); -} -void metis_meshtonodal__(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - METIS_MeshToNodal(ne, nn, elmnts, etype, numflag, dxadj, dadjncy); -} - - -void METIS_MESHTODUAL(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - METIS_MeshToDual(ne, nn, elmnts, etype, numflag, dxadj, dadjncy); -} -void metis_meshtodual(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - METIS_MeshToDual(ne, nn, elmnts, etype, numflag, dxadj, dadjncy); -} -void metis_meshtodual_(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - METIS_MeshToDual(ne, nn, elmnts, etype, numflag, dxadj, dadjncy); -} -void metis_meshtodual__(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - METIS_MeshToDual(ne, nn, elmnts, etype, numflag, dxadj, dadjncy); -} -#endif - -void METIS_ESTIMATEMEMORY(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *optype, idxtype *nbytes) -{ - METIS_EstimateMemory(nvtxs, xadj, adjncy, numflag, optype, nbytes); -} -void metis_estimatememory(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *optype, idxtype *nbytes) -{ - METIS_EstimateMemory(nvtxs, xadj, adjncy, numflag, optype, nbytes); -} -void metis_estimatememory_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *optype, idxtype *nbytes) -{ - METIS_EstimateMemory(nvtxs, xadj, adjncy, numflag, optype, nbytes); -} -void metis_estimatememory__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *optype, idxtype *nbytes) -{ - METIS_EstimateMemory(nvtxs, xadj, adjncy, numflag, optype, nbytes); -} - - - -void METIS_MCPARTGRAPHRECURSIVE(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_mCPartGraphRecursive(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} -void metis_mcpartgraphrecursive(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_mCPartGraphRecursive(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} -void metis_mcpartgraphrecursive_(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_mCPartGraphRecursive(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} -void metis_mcpartgraphrecursive__(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_mCPartGraphRecursive(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); -} - - -void METIS_MCPARTGRAPHKWAY(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *rubvec, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_mCPartGraphKway(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, rubvec, options, edgecut, part); -} -void metis_mcpartgraphkway(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *rubvec, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_mCPartGraphKway(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, rubvec, options, edgecut, part); -} -void metis_mcpartgraphkway_(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *rubvec, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_mCPartGraphKway(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, rubvec, options, edgecut, part); -} -void metis_mcpartgraphkway__(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *rubvec, idxtype *options, idxtype *edgecut, idxtype *part) -{ - METIS_mCPartGraphKway(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, rubvec, options, edgecut, part); -} - - -void METIS_PARTGRAPHVKWAY(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *volume, idxtype *part) -{ - METIS_PartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, options, volume, part); -} -void metis_partgraphvkaway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *volume, idxtype *part) -{ - METIS_PartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, options, volume, part); -} -void metis_partgraphvkaway_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *volume, idxtype *part) -{ - METIS_PartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, options, volume, part); -} -void metis_partgraphvkaway__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, idxtype *volume, idxtype *part) -{ - METIS_PartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, options, volume, part); -} - -void METIS_WPARTGRAPHVKWAY(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *volume, idxtype *part) -{ - METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, tpwgts, options, volume, part); -} -void metis_wpartgraphvkaway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *volume, idxtype *part) -{ - METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, tpwgts, options, volume, part); -} -void metis_wpartgraphvkaway_(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *volume, idxtype *part) -{ - METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, tpwgts, options, volume, part); -} -void metis_wpartgraphvkaway__(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, idxtype *options, idxtype *volume, idxtype *part) -{ - METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, tpwgts, options, volume, part); -} - - - diff --git a/src/metis/graph.c b/src/metis/graph.c deleted file mode 100644 index 72a6cc4..0000000 --- a/src/metis/graph.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * graph.c - * - * This file contains functions that deal with setting up the graphs - * for METIS. - * - * Started 7/25/97 - * George - * - */ - -#include - -/************************************************************************* -* This function sets up the graph from the user input -**************************************************************************/ -void SetUpGraph(GraphType *graph, idxtype OpType, idxtype nvtxs, idxtype ncon, - idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, idxtype wgtflag) -{ - idxtype i, j, k, sum; - float *nvwgt; - idxtype tvwgt[MAXNCON]; - - - InitGraph(graph); - - graph->nvtxs = nvtxs; - graph->nedges = xadj[nvtxs]; - graph->ncon = ncon; - - graph->xadj = xadj; - graph->free_xadj = 0; - - graph->adjncy = adjncy; - graph->free_adjncy = 0; - - /* setup the vertex weights */ - if (ncon == 1) { /* We are in the non mC mode */ - if ((wgtflag&2) == 0) { - vwgt = graph->vwgt = idxsmalloc(nvtxs, 1, "SetUpGraph: vwgt"); - } - else { - graph->vwgt = vwgt; - graph->free_vwgt = 0; - } - } - else { /* Set up the graph in MOC mode */ - for (i=0; invwgt = gk_fmalloc(ncon*nvtxs, "SetUpGraph: nvwgt"); - - for (i=0; iadjwgt = idxsmalloc(graph->nedges, 1, "SetUpGraph: adjwgt"); - } - else { - graph->adjwgt = adjwgt; - graph->free_adjwgt = 0; - } - - /* Compute the initial values of the adjwgtsum */ - graph->adjwgtsum = idxmalloc(nvtxs, "SetUpGraph: adjwgtsum"); - - for (i=0; iadjwgtsum[i] = sum; - } - - graph->cmap = idxmalloc(nvtxs, "SetUpGraph: cmap"); - - - if (OpType != OP_KMETIS && OpType != OP_KVMETIS) { - graph->label = idxmalloc(nvtxs, "SetUpGraph: label"); - - for (i=0; ilabel[i] = i; - } - -} - - - - -/************************************************************************* -* This function sets up the graph from the user input. The difference -* from the previous routine is that the vertex weights came already in -* a normalized fashion. -**************************************************************************/ -void SetUpGraph2(GraphType *graph, idxtype nvtxs, idxtype ncon, idxtype *xadj, - idxtype *adjncy, float *nvwgt, idxtype *adjwgt) -{ - idxtype i, j, sum; - - InitGraph(graph); - - graph->nvtxs = nvtxs; - graph->nedges = xadj[nvtxs]; - graph->ncon = ncon; - - graph->xadj = xadj; - graph->free_xadj = 0; - - graph->adjncy = adjncy; - graph->free_adjncy = 0; - - graph->adjwgt = adjwgt; - graph->free_adjwgt = 0; - - graph->nvwgt = gk_fmalloc(nvtxs*ncon, "SetUpGraph2: graph->nvwgt"); - gk_fcopy(nvtxs*ncon, nvwgt, graph->nvwgt); - - - /* Compute the initial values of the adjwgtsum */ - graph->adjwgtsum = idxmalloc(nvtxs, "SetUpGraph2: adjwgtsum"); - for (i=0; iadjwgtsum[i] = sum; - } - - graph->cmap = idxmalloc(nvtxs, "SetUpGraph2: cmap"); - - graph->label = idxmalloc(nvtxs, "SetUpGraph: label"); - for (i=0; ilabel[i] = i; - -} - - -/************************************************************************* -* This function sets up the graph from the user input -**************************************************************************/ -void VolSetUpGraph(GraphType *graph, idxtype OpType, idxtype nvtxs, idxtype ncon, idxtype *xadj, - idxtype *adjncy, idxtype *vwgt, idxtype *vsize, idxtype wgtflag) -{ - idxtype i, j, k, sum; - idxtype *adjwgt; - float *nvwgt; - idxtype tvwgt[MAXNCON]; - - InitGraph(graph); - - graph->nvtxs = nvtxs; - graph->nedges = xadj[nvtxs]; - graph->ncon = ncon; - - graph->xadj = xadj; - graph->free_xadj = 0; - - graph->adjncy = adjncy; - graph->free_adjncy = 0; - - /* Setup the vwgt/vwgt */ - if (ncon == 1) { /* We are in the non mC mode */ - if ((wgtflag&2) == 0) { - vwgt = graph->vwgt = idxsmalloc(nvtxs, 1, "VolSetUpGraph: vwgt"); - } - else { - graph->vwgt = vwgt; - graph->free_vwgt = 0; - } - } - else { /* Set up the graph in MOC mode */ - /* Create the normalized vertex weights along each constraint */ - for (i=0; invwgt = gk_fmalloc(ncon*nvtxs, "SetUpGraph: nvwgt"); - - for (i=0; ivsize = idxsmalloc(nvtxs, 1, "VolSetUpGraph: vsize"); - } - else { - graph->vsize = vsize; - graph->free_vsize = 0; - } - - /* Allocate memory for edge weights and initialize them to the sum of the vsize */ - adjwgt = graph->adjwgt = idxmalloc(graph->nedges, "VolSetUpGraph: adjwgt"); - for (i=0; iadjwgtsum = idxmalloc(nvtxs, "VolSetUpGraph: adjwgtsum"); - for (i=0; iadjwgtsum[i] = sum; - } - - graph->cmap = idxmalloc(nvtxs, "VolSetUpGraph: cmap"); - - - if (OpType != OP_KVMETIS) { - graph->label = idxmalloc(nvtxs, "SetUpGraph: label"); - - for (i=0; ilabel[i] = i; - } - -} - - -/************************************************************************* -* This function randomly permutes the adjacency lists of a graph -**************************************************************************/ -void RandomizeGraph(GraphType *graph) -{ - idxtype i, j, k, l, tmp, nvtxs; - idxtype *xadj, *adjncy, *adjwgt; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - for (i=0; invtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - where = graph->where; - - touched = idxsmalloc(nvtxs, 0, "IsConnected: touched"); - queue = idxmalloc(nvtxs, "IsConnected: queue"); - cptr = idxmalloc(nvtxs+1, "IsConnected: cptr"); - - nleft = 0; - for (i=0; i 1 && report) { - mprintf("The graph has %D connected components in partition %D:\t", ncmps, pid); - for (i=0; ivwgt[queue[j]]; - mprintf("[%5D %5D] ", cptr[i+1]-cptr[i], wgt); - /* - if (cptr[i+1]-cptr[i] == 1) - mprintf("[%D %D] ", queue[cptr[i]], xadj[queue[cptr[i]]+1]-xadj[queue[cptr[i]]]); - */ - } - mprintf("\n"); - } - - gk_free((void **)&touched, &queue, &cptr, LTERM); - - return (ncmps == 1 ? 1 : 0); -} - - -/************************************************************************* -* This function checks whether a graph is contigous or not -**************************************************************************/ -idxtype IsConnected(CtrlType *ctrl, GraphType *graph, idxtype report) -{ - idxtype i, j, k, nvtxs, first, last; - idxtype *xadj, *adjncy, *touched, *queue; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - - touched = idxsmalloc(nvtxs, 0, "IsConnected: touched"); - queue = idxmalloc(nvtxs, "IsConnected: queue"); - - touched[0] = 1; - queue[0] = 0; - first = 0; last = 1; - - while (first < last) { - i = queue[first++]; - for (j=xadj[i]; jnvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - where = graph->where; - - touched = idxsmalloc(nvtxs, 0, "IsConnected: touched"); - queue = idxmalloc(nvtxs, "IsConnected: queue"); - cptr = idxmalloc(nvtxs+1, "IsConnected: cptr"); - - nleft = nvtxs; - touched[0] = 1; - queue[0] = 0; - first = 0; last = 1; - - cptr[0] = 0; /* This actually points to queue */ - ncmps = 0; - while (first != nleft) { - if (first == last) { /* Find another starting vertex */ - cptr[++ncmps] = first; - for (i=0; i 1 && report) { - mprintf("%D connected components:\t", ncmps); - for (i=0; i 200) - mprintf("[%5D] ", cptr[i+1]-cptr[i]); - } - mprintf("\n"); - } - - gk_free((void **)&touched, &queue, &cptr, LTERM); - - return (ncmps == 1 ? 1 : 0); -} - - -/************************************************************************* -* This function returns the number of connected components in cptr,cind -* The separator of the graph is used to split it and then find its components. -**************************************************************************/ -idxtype FindComponents(CtrlType *ctrl, GraphType *graph, idxtype *cptr, idxtype *cind) -{ - idxtype i, j, k, nvtxs, first, last, nleft, ncmps, wgt; - idxtype *xadj, *adjncy, *where, *touched, *queue; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - where = graph->where; - - touched = idxsmalloc(nvtxs, 0, "IsConnected: queue"); - - for (i=0; inbnd; i++) - touched[graph->bndind[i]] = 1; - - queue = cind; - - nleft = 0; - for (i=0; i + +typedef __int32 int32_t; +typedef __int64 int64_t; +#define PRId32 "I32d" +#define PRId64 "I64d" +#define SCNd32 "ld" +#define SCNd64 "I64d" +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#else +#include +#endif +#endif + + +/*------------------------------------------------------------------------ +* Setup the basic datatypes +*-------------------------------------------------------------------------*/ +#if IDXTYPEWIDTH == 32 + typedef int32_t idx_t; + + #define IDX_MAX INT32_MAX + #define IDX_MIN INT32_MIN + + #define SCIDX SCNd32 + #define PRIDX PRId32 + + #define strtoidx strtol + #define iabs abs +#elif IDXTYPEWIDTH == 64 + typedef int64_t idx_t; + + #define IDX_MAX INT64_MAX + #define IDX_MIN INT64_MIN + + #define SCIDX SCNd64 + #define PRIDX PRId64 + +#ifdef COMPILER_MSC + #define strtoidx _strtoi64 +#else + #define strtoidx strtoll +#endif + #define iabs labs +#else + #error "Incorrect user-supplied value fo IDXTYPEWIDTH" +#endif + + +#if REALTYPEWIDTH == 32 + typedef float real_t; + + #define SCREAL "f" + #define PRREAL "f" + #define REAL_MAX FLT_MAX + #define REAL_MIN FLT_MIN + #define REAL_EPSILON FLT_EPSILON + + #define rabs fabsf + #define REALEQ(x,y) ((rabs((x)-(y)) <= FLT_EPSILON)) + +#ifdef COMPILER_MSC + #define strtoreal (float)strtod +#else + #define strtoreal strtof +#endif +#elif REALTYPEWIDTH == 64 + typedef double real_t; + + #define SCREAL "lf" + #define PRREAL "lf" + #define REAL_MAX DBL_MAX + #define REAL_MIN DBL_MIN + #define REAL_EPSILON DBL_EPSILON + + #define rabs fabs + #define REALEQ(x,y) ((rabs((x)-(y)) <= DBL_EPSILON)) + + #define strtoreal strtod +#else + #error "Incorrect user-supplied value for REALTYPEWIDTH" +#endif + + +/*------------------------------------------------------------------------ +* Constant definitions +*-------------------------------------------------------------------------*/ +/* Metis's version number */ +#define METIS_VER_MAJOR 5 +#define METIS_VER_MINOR 1 +#define METIS_VER_SUBMINOR 0 + +/* The maximum length of the options[] array */ +#define METIS_NOPTIONS 40 + + + +/*------------------------------------------------------------------------ +* Function prototypes +*-------------------------------------------------------------------------*/ + +#ifdef _WINDLL +#define METIS_API(type) __declspec(dllexport) type __cdecl +#elif defined(__cdecl) +#define METIS_API(type) type __cdecl +#else +#define METIS_API(type) type +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + +METIS_API(int) METIS_PartGraphRecursive(idx_t *nvtxs, idx_t *ncon, idx_t *xadj, + idx_t *adjncy, idx_t *vwgt, idx_t *vsize, idx_t *adjwgt, + idx_t *nparts, real_t *tpwgts, real_t *ubvec, idx_t *options, + idx_t *edgecut, idx_t *part); + +METIS_API(int) METIS_PartGraphKway(idx_t *nvtxs, idx_t *ncon, idx_t *xadj, + idx_t *adjncy, idx_t *vwgt, idx_t *vsize, idx_t *adjwgt, + idx_t *nparts, real_t *tpwgts, real_t *ubvec, idx_t *options, + idx_t *edgecut, idx_t *part); + +METIS_API(int) METIS_MeshToDual(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, + idx_t *ncommon, idx_t *numflag, idx_t **r_xadj, idx_t **r_adjncy); + +METIS_API(int) METIS_MeshToNodal(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, + idx_t *numflag, idx_t **r_xadj, idx_t **r_adjncy); + +METIS_API(int) METIS_PartMeshNodal(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, + idx_t *vwgt, idx_t *vsize, idx_t *nparts, real_t *tpwgts, + idx_t *options, idx_t *objval, idx_t *epart, idx_t *npart); + +METIS_API(int) METIS_PartMeshDual(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, + idx_t *vwgt, idx_t *vsize, idx_t *ncommon, idx_t *nparts, + real_t *tpwgts, idx_t *options, idx_t *objval, idx_t *epart, + idx_t *npart); + +METIS_API(int) METIS_NodeND(idx_t *nvtxs, idx_t *xadj, idx_t *adjncy, idx_t *vwgt, + idx_t *options, idx_t *perm, idx_t *iperm); + +METIS_API(int) METIS_Free(void *ptr); + +METIS_API(int) METIS_SetDefaultOptions(idx_t *options); + + +/* These functions are used by ParMETIS */ + +METIS_API(int) METIS_NodeNDP(idx_t nvtxs, idx_t *xadj, idx_t *adjncy, idx_t *vwgt, + idx_t npes, idx_t *options, idx_t *perm, idx_t *iperm, + idx_t *sizes); + +METIS_API(int) METIS_ComputeVertexSeparator(idx_t *nvtxs, idx_t *xadj, idx_t *adjncy, + idx_t *vwgt, idx_t *options, idx_t *sepsize, idx_t *part); + +METIS_API(int) METIS_NodeRefine(idx_t nvtxs, idx_t *xadj, idx_t *vwgt, idx_t *adjncy, + idx_t *where, idx_t *hmarker, real_t ubfactor); + + +#ifdef __cplusplus +} +#endif + + + +/*------------------------------------------------------------------------ +* Enum type definitions +*-------------------------------------------------------------------------*/ +/*! Return codes */ +typedef enum { + METIS_OK = 1, /*!< Returned normally */ + METIS_ERROR_INPUT = -2, /*!< Returned due to erroneous inputs and/or options */ + METIS_ERROR_MEMORY = -3, /*!< Returned due to insufficient memory */ + METIS_ERROR = -4 /*!< Some other errors */ +} rstatus_et; + + +/*! Operation type codes */ +typedef enum { + METIS_OP_PMETIS, + METIS_OP_KMETIS, + METIS_OP_OMETIS +} moptype_et; + + +/*! Options codes (i.e., options[]) */ +typedef enum { + METIS_OPTION_PTYPE, + METIS_OPTION_OBJTYPE, + METIS_OPTION_CTYPE, + METIS_OPTION_IPTYPE, + METIS_OPTION_RTYPE, + METIS_OPTION_DBGLVL, + METIS_OPTION_NITER, + METIS_OPTION_NCUTS, + METIS_OPTION_SEED, + METIS_OPTION_NO2HOP, + METIS_OPTION_MINCONN, + METIS_OPTION_CONTIG, + METIS_OPTION_COMPRESS, + METIS_OPTION_CCORDER, + METIS_OPTION_PFACTOR, + METIS_OPTION_NSEPS, + METIS_OPTION_UFACTOR, + METIS_OPTION_NUMBERING, + + /* Used for command-line parameter purposes */ + METIS_OPTION_HELP, + METIS_OPTION_TPWGTS, + METIS_OPTION_NCOMMON, + METIS_OPTION_NOOUTPUT, + METIS_OPTION_BALANCE, + METIS_OPTION_GTYPE, + METIS_OPTION_UBVEC +} moptions_et; + + +/*! Partitioning Schemes */ +typedef enum { + METIS_PTYPE_RB, + METIS_PTYPE_KWAY +} mptype_et; + +/*! Graph types for meshes */ +typedef enum { + METIS_GTYPE_DUAL, + METIS_GTYPE_NODAL +} mgtype_et; + +/*! Coarsening Schemes */ +typedef enum { + METIS_CTYPE_RM, + METIS_CTYPE_SHEM +} mctype_et; + +/*! Initial partitioning schemes */ +typedef enum { + METIS_IPTYPE_GROW, + METIS_IPTYPE_RANDOM, + METIS_IPTYPE_EDGE, + METIS_IPTYPE_NODE, + METIS_IPTYPE_METISRB +} miptype_et; + + +/*! Refinement schemes */ +typedef enum { + METIS_RTYPE_FM, + METIS_RTYPE_GREEDY, + METIS_RTYPE_SEP2SIDED, + METIS_RTYPE_SEP1SIDED +} mrtype_et; + + +/*! Debug Levels */ +typedef enum { + METIS_DBG_INFO = 1, /*!< Shows various diagnostic messages */ + METIS_DBG_TIME = 2, /*!< Perform timing analysis */ + METIS_DBG_COARSEN = 4, /*!< Show the coarsening progress */ + METIS_DBG_REFINE = 8, /*!< Show the refinement progress */ + METIS_DBG_IPART = 16, /*!< Show info on initial partitioning */ + METIS_DBG_MOVEINFO = 32, /*!< Show info on vertex moves during refinement */ + METIS_DBG_SEPINFO = 64, /*!< Show info on vertex moves during sep refinement */ + METIS_DBG_CONNINFO = 128, /*!< Show info on minimization of subdomain connectivity */ + METIS_DBG_CONTIGINFO = 256, /*!< Show info on elimination of connected components */ + METIS_DBG_MEMORY = 2048, /*!< Show info related to wspace allocation */ +} mdbglvl_et; + + +/* Types of objectives */ +typedef enum { + METIS_OBJTYPE_CUT, + METIS_OBJTYPE_VOL, + METIS_OBJTYPE_NODE +} mobjtype_et; + + + +#endif /* _METIS_H_ */ diff --git a/src/metis/initpart.c b/src/metis/initpart.c deleted file mode 100644 index 8c54c8a..0000000 --- a/src/metis/initpart.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * initpart.c - * - * This file contains code that performs the initial partition of the - * coarsest graph - * - * Started 7/23/97 - * George - * - */ - -#include - -/************************************************************************* -* This function computes the initial bisection of the coarsest graph -**************************************************************************/ -void Init2WayPartition(CtrlType *ctrl, GraphType *graph, idxtype *tpwgts, float ubfactor) -{ - idxtype dbglvl; - - dbglvl = ctrl->dbglvl; - IFSET(ctrl->dbglvl, DBG_REFINE, ctrl->dbglvl -= DBG_REFINE); - IFSET(ctrl->dbglvl, DBG_MOVEINFO, ctrl->dbglvl -= DBG_MOVEINFO); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); - - switch (ctrl->IType) { - case ITYPE_GGPKL: - if (graph->nedges == 0) - RandomBisection(ctrl, graph, tpwgts, ubfactor); - else - GrowBisection(ctrl, graph, tpwgts, ubfactor); - break; - case ITYPE_RANDOM: - RandomBisection(ctrl, graph, tpwgts, ubfactor); - break; - default: - errexit("Unknown initial partition type: %d\n", ctrl->IType); - } - - IFSET(ctrl->dbglvl, DBG_IPART, mprintf("Initial Cut: %D\n", graph->mincut)); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); - ctrl->dbglvl = dbglvl; - -/* - IsConnectedSubdomain(ctrl, graph, 0); - IsConnectedSubdomain(ctrl, graph, 1); -*/ -} - -/************************************************************************* -* This function computes the initial bisection of the coarsest graph -**************************************************************************/ -void InitSeparator(CtrlType *ctrl, GraphType *graph, float ubfactor) -{ - idxtype dbglvl; - - dbglvl = ctrl->dbglvl; - IFSET(ctrl->dbglvl, DBG_REFINE, ctrl->dbglvl -= DBG_REFINE); - IFSET(ctrl->dbglvl, DBG_MOVEINFO, ctrl->dbglvl -= DBG_MOVEINFO); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); - - GrowBisectionNode(ctrl, graph, ubfactor); - Compute2WayNodePartitionParams(ctrl, graph); - - IFSET(ctrl->dbglvl, DBG_IPART, mprintf("Initial Sep: %D\n", graph->mincut)); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); - - ctrl->dbglvl = dbglvl; - -} - - - -/************************************************************************* -* This function takes a graph and produces a bisection by using a region -* growing algorithm. The resulting partition is returned in -* graph->where -**************************************************************************/ -void GrowBisection(CtrlType *ctrl, GraphType *graph, idxtype *tpwgts, float ubfactor) -{ - idxtype i, j, k, nvtxs, drain, nleft, first, last, pwgts[2], minpwgt[2], - maxpwgt[2], from, bestcut, icut, mincut, me, pass, nbfs, inbfs; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where; - idxtype *queue, *touched, *gain, *bestwhere; - - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - Allocate2WayPartitionMemory(ctrl, graph); - where = graph->where; - - bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere"); - queue = idxmalloc(nvtxs, "BisectGraph: queue"); - touched = idxmalloc(nvtxs, "BisectGraph: touched"); - - ASSERTP(tpwgts[0]+tpwgts[1] == idxsum(nvtxs, vwgt, 1), ("%d %d\n", tpwgts[0]+tpwgts[1], idxsum(nvtxs, vwgt, 1))); - - maxpwgt[0] = ubfactor*tpwgts[0]; - maxpwgt[1] = ubfactor*tpwgts[1]; - minpwgt[0] = (1.0/ubfactor)*tpwgts[0]; - minpwgt[1] = (1.0/ubfactor)*tpwgts[1]; - - nbfs = (nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS); - for (inbfs=0; inbfs 0 && pwgts[1]-vwgt[i] < minpwgt[1]) { - drain = 1; - continue; - } - - where[i] = 0; - INC_DEC(pwgts[0], pwgts[1], vwgt[i]); - if (pwgts[1] <= maxpwgt[1]) - break; - - drain = 0; - for (j=xadj[i]; jnvtxs, pwgts[0], pwgts[1], graph->pwgts[0], graph->pwgts[1], graph->mincut); */ - - Balance2Way(ctrl, graph, tpwgts, ubfactor); - /*mprintf("BPART: [%5D %5D] %5D\n", graph->pwgts[0], graph->pwgts[1], graph->mincut);*/ - - FM_2WayEdgeRefine(ctrl, graph, tpwgts, 4); - /*mprintf("RPART: [%5D %5D] %5D\n", graph->pwgts[0], graph->pwgts[1], graph->mincut);*/ - - if (inbfs == 0 || bestcut > graph->mincut) { - bestcut = graph->mincut; - idxcopy(nvtxs, where, bestwhere); - if (bestcut == 0) - break; - } - } - - graph->mincut = bestcut; - idxcopy(nvtxs, bestwhere, where); - - gk_free((void **)&bestwhere, &queue, &touched, LTERM); -} - - - - -/************************************************************************* -* This function takes a graph and produces a bisection by using a region -* growing algorithm. The resulting partition is returned in -* graph->where -**************************************************************************/ -void GrowBisectionNode(CtrlType *ctrl, GraphType *graph, float ubfactor) -{ - idxtype i, j, k, nvtxs, drain, nleft, first, last, pwgts[2], tpwgts[2], - minpwgt[2], maxpwgt[2], from, bestcut, icut, mincut, me, pass, - nbfs, inbfs; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where, *bndind; - idxtype *queue, *touched, *gain, *bestwhere; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere"); - queue = idxmalloc(nvtxs, "BisectGraph: queue"); - touched = idxmalloc(nvtxs, "BisectGraph: touched"); - - tpwgts[0] = idxsum(nvtxs, vwgt, 1); - tpwgts[1] = tpwgts[0]/2; - tpwgts[0] -= tpwgts[1]; - - maxpwgt[0] = ubfactor*tpwgts[0]; - maxpwgt[1] = ubfactor*tpwgts[1]; - minpwgt[0] = (1.0/ubfactor)*tpwgts[0]; - minpwgt[1] = (1.0/ubfactor)*tpwgts[1]; - - /* Allocate refinement memory. Allocate sufficient memory for both edge and node */ - graph->pwgts = idxmalloc(3, "GrowBisectionNode: pwgts"); - graph->where = idxmalloc(nvtxs, "GrowBisectionNode: where"); - graph->bndptr = idxmalloc(nvtxs, "GrowBisectionNode: bndptr"); - graph->bndind = idxmalloc(nvtxs, "GrowBisectionNode: bndind"); - graph->id = idxmalloc(nvtxs, "GrowBisectionNode: id"); - graph->ed = idxmalloc(nvtxs, "GrowBisectionNode: ed"); - graph->nrinfo = (NRInfoType *)gk_malloc(nvtxs*sizeof(NRInfoType), "GrowBisectionNode: nrinfo"); - - - where = graph->where; - bndind = graph->bndind; - - nbfs = (nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS) + 1; - for (inbfs=0; inbfs= 1) { - for (;;) { - if (first == last) { /* Empty. Disconnected graph! */ - if (nleft == 0 || drain) - break; - - k = RandomInRange(nleft); - for (i=0; inbnd; i++) - where[bndind[i]] = 2; - - Compute2WayNodePartitionParams(ctrl, graph); - FM_2WayNodeRefine(ctrl, graph, ubfactor, 6); - - /* mprintf("ISep: [%D %D %D] %D\n", graph->pwgts[0], graph->pwgts[1], graph->pwgts[2], bestcut); */ - - if (inbfs == 0 || bestcut > graph->mincut) { - bestcut = graph->mincut; - idxcopy(nvtxs, where, bestwhere); - } - } - - graph->mincut = bestcut; - idxcopy(nvtxs, bestwhere, where); - - Compute2WayNodePartitionParams(ctrl, graph); - - gk_free((void **)&bestwhere, &queue, &touched, LTERM); -} - - -/************************************************************************* -* This function takes a graph and produces a bisection by using a region -* growing algorithm. The resulting partition is returned in -* graph->where -**************************************************************************/ -void RandomBisection(CtrlType *ctrl, GraphType *graph, idxtype *tpwgts, float ubfactor) -{ - idxtype i, ii, j, k, nvtxs, pwgts[2], minpwgt[2], maxpwgt[2], from, bestcut, - icut, mincut, me, pass, nbfs, inbfs; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where; - idxtype *perm, *bestwhere; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - Allocate2WayPartitionMemory(ctrl, graph); - where = graph->where; - - bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere"); - perm = idxmalloc(nvtxs, "BisectGraph: queue"); - - ASSERTP(tpwgts[0]+tpwgts[1] == idxsum(nvtxs, vwgt, 1), ("%d %d\n", tpwgts[0]+tpwgts[1], idxsum(nvtxs, vwgt, 1))); - - maxpwgt[0] = ubfactor*tpwgts[0]; - maxpwgt[1] = ubfactor*tpwgts[1]; - minpwgt[0] = (1.0/ubfactor)*tpwgts[0]; - minpwgt[1] = (1.0/ubfactor)*tpwgts[1]; - - nbfs = (nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS); - for (inbfs=0; inbfs minpwgt[0]) - break; - } - } - } - - /************************************************************* - * Do some partition refinement - **************************************************************/ - Compute2WayPartitionParams(ctrl, graph); - /* mprintf("IPART: %3D [%5D %5D] [%5D %5D] %5D\n", graph->nvtxs, pwgts[0], pwgts[1], graph->pwgts[0], graph->pwgts[1], graph->mincut); */ - - Balance2Way(ctrl, graph, tpwgts, ubfactor); - /* mprintf("BPART: [%5D %5D] %5D\n", graph->pwgts[0], graph->pwgts[1], graph->mincut); */ - - FM_2WayEdgeRefine(ctrl, graph, tpwgts, 4); - /* mprintf("RPART: [%5D %5D] %5D\n", graph->pwgts[0], graph->pwgts[1], graph->mincut); */ - - if (inbfs==0 || bestcut > graph->mincut) { - bestcut = graph->mincut; - idxcopy(nvtxs, where, bestwhere); - if (bestcut == 0) - break; - } - } - - graph->mincut = bestcut; - idxcopy(nvtxs, bestwhere, where); - - gk_free((void **)&bestwhere, &perm, LTERM); -} - - - - diff --git a/src/metis/kmetis.c b/src/metis/kmetis.c deleted file mode 100644 index 6f6d1ff..0000000 --- a/src/metis/kmetis.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * kmetis.c - * - * This file contains the top level routines for the multilevel k-way partitioning - * algorithm KMETIS. - * - * Started 7/28/97 - * George - * - */ - -#include - - -/************************************************************************* -* This function is the entry point for KMETIS -**************************************************************************/ -void METIS_PartGraphKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i; - float *tpwgts; - - tpwgts = gk_fmalloc(*nparts, "KMETIS: tpwgts"); - for (i=0; i<*nparts; i++) - tpwgts[i] = 1.0/(1.0*(*nparts)); - - METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, - tpwgts, options, edgecut, part); - - gk_free((void **)&tpwgts, LTERM); -} - - -/************************************************************************* -* This function is the entry point for KWMETIS -**************************************************************************/ -void METIS_WPartGraphKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - SetUpGraph(&graph, OP_KMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, *wgtflag); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = KMETIS_CTYPE; - ctrl.IType = KMETIS_ITYPE; - ctrl.RType = KMETIS_RTYPE; - ctrl.dbglvl = KMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_KMETIS; - ctrl.CoarsenTo = amax((*nvtxs)/(40*gk_log2(*nparts)), 20*(*nparts)); - ctrl.maxvwgt = 1.5*((graph.vwgt ? idxsum(*nvtxs, graph.vwgt, 1) : (*nvtxs))/ctrl.CoarsenTo); - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MlevelKWayPartitioning(&ctrl, &graph, *nparts, part, tpwgts, 1.03); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); -} - - -/************************************************************************* -* This function takes a graph and produces a bisection of it -**************************************************************************/ -idxtype MlevelKWayPartitioning(CtrlType *ctrl, GraphType *graph, idxtype nparts, idxtype *part, float *tpwgts, float ubfactor) -{ - idxtype i, j, nvtxs, tvwgt, tpwgts2[2]; - GraphType *cgraph; - idxtype wgtflag=3, numflag=0, options[10], edgecut; - - cgraph = Coarsen2Way(ctrl, graph); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); - AllocateKWayPartitionMemory(ctrl, cgraph, nparts); - - options[0] = 1; - options[OPTION_CTYPE] = MTYPE_SHEMKWAY; - options[OPTION_ITYPE] = ITYPE_GGPKL; - options[OPTION_RTYPE] = RTYPE_FM; - options[OPTION_DBGLVL] = 0; - - METIS_WPartGraphRecursive(&cgraph->nvtxs, cgraph->xadj, cgraph->adjncy, cgraph->vwgt, - cgraph->adjwgt, &wgtflag, &numflag, &nparts, tpwgts, options, - &edgecut, cgraph->where); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); - IFSET(ctrl->dbglvl, DBG_IPART, mprintf("Initial %D-way partitioning cut: %D\n", nparts, edgecut)); - - IFSET(ctrl->dbglvl, DBG_KWAYPINFO, ComputePartitionInfo(cgraph, nparts, cgraph->where)); - - RefineKWay(ctrl, graph, cgraph, nparts, tpwgts, ubfactor); - - idxcopy(graph->nvtxs, graph->where, part); - - FreeGraph(graph, 0); - - return graph->mincut; - -} - diff --git a/src/metis/kvmetis.c b/src/metis/kvmetis.c deleted file mode 100644 index 686ebc0..0000000 --- a/src/metis/kvmetis.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * kvmetis.c - * - * This file contains the top level routines for the multilevel k-way partitioning - * algorithm KMETIS. - * - * Started 7/28/97 - * George - * - * $Id: kvmetis.c,v 1.3 2002/08/10 06:57:50 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function is the entry point for KMETIS -**************************************************************************/ -void METIS_PartGraphVKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - idxtype *options, idxtype *volume, idxtype *part) -{ - idxtype i; - float *tpwgts; - - tpwgts = gk_fmalloc(*nparts, "KMETIS: tpwgts"); - for (i=0; i<*nparts; i++) - tpwgts[i] = 1.0/(1.0*(*nparts)); - - METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, - tpwgts, options, volume, part); - - gk_free((void **)&tpwgts, LTERM); -} - - -/************************************************************************* -* This function is the entry point for KWMETIS -**************************************************************************/ -void METIS_WPartGraphVKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - float *tpwgts, idxtype *options, idxtype *volume, idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - VolSetUpGraph(&graph, OP_KVMETIS, *nvtxs, 1, xadj, adjncy, vwgt, vsize, *wgtflag); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = KVMETIS_CTYPE; - ctrl.IType = KVMETIS_ITYPE; - ctrl.RType = KVMETIS_RTYPE; - ctrl.dbglvl = KVMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_KVMETIS; - ctrl.CoarsenTo = amax((*nvtxs)/(40*gk_log2(*nparts)), 20*(*nparts)); - ctrl.maxvwgt = 1.5*((graph.vwgt ? idxsum(*nvtxs, graph.vwgt, 1) : (*nvtxs))/ctrl.CoarsenTo); - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *volume = MlevelVolKWayPartitioning(&ctrl, &graph, *nparts, part, tpwgts, 1.03); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); -} - - -/************************************************************************* -* This function takes a graph and produces a bisection of it -**************************************************************************/ -idxtype MlevelVolKWayPartitioning(CtrlType *ctrl, GraphType *graph, idxtype nparts, idxtype *part, - float *tpwgts, float ubfactor) -{ - idxtype i, j, nvtxs, tvwgt, tpwgts2[2]; - GraphType *cgraph; - idxtype wgtflag=3, numflag=0, options[10], edgecut; - - cgraph = Coarsen2Way(ctrl, graph); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); - AllocateVolKWayPartitionMemory(ctrl, cgraph, nparts); - - options[0] = 1; - options[OPTION_CTYPE] = MTYPE_SHEMKWAY; - options[OPTION_ITYPE] = ITYPE_GGPKL; - options[OPTION_RTYPE] = RTYPE_FM; - options[OPTION_DBGLVL] = 0; - - METIS_WPartGraphRecursive(&cgraph->nvtxs, cgraph->xadj, cgraph->adjncy, cgraph->vwgt, - cgraph->adjwgt, &wgtflag, &numflag, &nparts, tpwgts, options, - &edgecut, cgraph->where); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); - IFSET(ctrl->dbglvl, DBG_IPART, mprintf("Initial %D-way partitioning cut: %D\n", nparts, edgecut)); - - IFSET(ctrl->dbglvl, DBG_KWAYPINFO, ComputePartitionInfo(cgraph, nparts, cgraph->where)); - - RefineVolKWay(ctrl, graph, cgraph, nparts, tpwgts, ubfactor); - - idxcopy(graph->nvtxs, graph->where, part); - - FreeGraph(graph, 0); - - return graph->minvol; - -} - diff --git a/src/metis/kwayfm.c b/src/metis/kwayfm.c deleted file mode 100644 index 0ccc2e3..0000000 --- a/src/metis/kwayfm.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * kwayfm.c - * - * This file contains code that implements the multilevel k-way refinement - * - * Started 7/28/97 - * George - * - * $Id: kwayfm.c,v 1.2 2002/08/10 06:29:31 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void Random_KWayEdgeRefine(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, float ubfactor, idxtype npasses, idxtype ffactor) -{ - idxtype i, ii, iii, j, jj, k, l, pass, nvtxs, nmoves, nbnd, tvwgt, myndegrees; - idxtype from, me, to, oldcut, vwgt, gain; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts; - EDegreeType *myedegrees; - RInfoType *myrinfo; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndptr = graph->bndptr; - bndind = graph->bndind; - - where = graph->where; - pwgts = graph->pwgts; - - /* Setup the weight intervals of the various subdomains */ - minwgt = idxwspacemalloc(ctrl, nparts); - maxwgt = idxwspacemalloc(ctrl, nparts); - itpwgts = idxwspacemalloc(ctrl, nparts); - tvwgt = idxsum(nparts, pwgts, 1); - ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt, 1)); - - for (i=0; idbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D]-[%6D %6D], Balance: %5.3f, Nv-Nb[%6D %6D]. Cut: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], minwgt[0], maxwgt[0], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd, - graph->mincut)); - - for (pass=0; passmincut); - - oldcut = graph->mincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (nmoves=iii=0; iiinbnd; iii++) { - ii = perm[iii]; - if (ii >= nbnd) - continue; - i = bndind[ii]; - - myrinfo = graph->rinfo+i; - - if (myrinfo->ed >= myrinfo->id) { /* Total ED is too high */ - from = where[i]; - vwgt = graph->vwgt[i]; - - if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from]) - continue; /* This cannot be moved! */ - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - j = myrinfo->id; - for (k=0; kid. Allow good nodes to move */ - if (pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain && gain >= 0) - break; - } - if (k == myndegrees) - continue; /* break out if you did not find a candidate */ - - for (j=k+1; j myedegrees[k].ed && pwgts[to]+vwgt <= maxwgt[to]) || - (myedegrees[j].ed == myedegrees[k].ed && - itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid])) - k = j; - } - - to = myedegrees[k].pid; - - j = 0; - if (myedegrees[k].ed-myrinfo->id > 0) - j = 1; - else if (myedegrees[k].ed-myrinfo->id == 0) { - if ((iii&7) == 0 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from]) - j = 1; - } - if (j == 0) - continue; - - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - graph->mincut -= myedegrees[k].ed-myrinfo->id; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D to %3D. Gain: %4D. Cut: %6D\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut)); - - /* Update where, weight, and ID/ED information of the vertex you moved */ - where[i] = to; - INC_DEC(pwgts[to], pwgts[from], vwgt); - myrinfo->ed += myrinfo->id-myedegrees[k].ed; - SWAP(myrinfo->id, myedegrees[k].ed, j); - if (myedegrees[k].ed == 0) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].pid = from; - - if (myrinfo->ed-myrinfo->id < 0) - BNDDelete(nbnd, bndind, bndptr, i); - - /* Update the degrees of adjacent vertices */ - for (j=xadj[i]; jrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - ASSERT(CheckRInfo(myrinfo)); - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1) - BNDInsert(nbnd, bndind, bndptr, ii); - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1) - BNDDelete(nbnd, bndind, bndptr, ii); - } - - /* Remove contribution from the .ed of 'from' */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ed == adjwgt[j]) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].ed -= adjwgt[j]; - break; - } - } - } - - /* Add contribution to the .ed of 'to' */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - - ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]); - ASSERT(CheckRInfo(myrinfo)); - - } - nmoves++; - } - } - - graph->nbnd = nbnd; - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\t[%6D %6D], Balance: %5.3f, Nb: %6D. Nmoves: %5D, Cut: %6D, Vol: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, ComputeVolume(graph, where))); - - if (graph->mincut == oldcut) - break; - } - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); -} - - - - - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void Greedy_KWayEdgeRefine(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, float ubfactor, idxtype npasses) -{ - idxtype i, ii, iii, j, jj, k, l, pass, nvtxs, nbnd, tvwgt, myndegrees, oldgain, gain; - idxtype from, me, to, oldcut, vwgt; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *moved, *itpwgts; - EDegreeType *myedegrees; - RInfoType *myrinfo; - PQueueType queue; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndind = graph->bndind; - bndptr = graph->bndptr; - - where = graph->where; - pwgts = graph->pwgts; - - /* Setup the weight intervals of the various subdomains */ - minwgt = idxwspacemalloc(ctrl, nparts); - maxwgt = idxwspacemalloc(ctrl, nparts); - itpwgts = idxwspacemalloc(ctrl, nparts); - tvwgt = idxsum(nparts, pwgts, 1); - ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt, 1)); - - for (i=0; iadjwgtsum[idxargmax(nvtxs, graph->adjwgtsum)]); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D]-[%6D %6D], Balance: %5.3f, Nv-Nb[%6D %6D]. Cut: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], minwgt[0], maxwgt[0], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd, - graph->mincut)); - - for (pass=0; passmincut); - - PQueueReset(&queue); - idxset(nvtxs, -1, moved); - - oldcut = graph->mincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (ii=0; iirinfo[i].ed - graph->rinfo[i].id); - moved[i] = 2; - } - - for (iii=0;;iii++) { - if ((i = PQueueGetMax(&queue)) == -1) - break; - moved[i] = 1; - - myrinfo = graph->rinfo+i; - from = where[i]; - vwgt = graph->vwgt[i]; - - if (pwgts[from]-vwgt < minwgt[from]) - continue; /* This cannot be moved! */ - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - j = myrinfo->id; - for (k=0; kid. Allow good nodes to move */ - if (pwgts[to]+vwgt <= maxwgt[to]+gain && gain >= 0) - break; - } - if (k == myndegrees) - continue; /* break out if you did not find a candidate */ - - for (j=k+1; j myedegrees[k].ed && pwgts[to]+vwgt <= maxwgt[to]) || - (myedegrees[j].ed == myedegrees[k].ed && - itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid])) - k = j; - } - - to = myedegrees[k].pid; - - j = 0; - if (myedegrees[k].ed-myrinfo->id > 0) - j = 1; - else if (myedegrees[k].ed-myrinfo->id == 0) { - if ((iii&7) == 0 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from]) - j = 1; - } - if (j == 0) - continue; - - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - graph->mincut -= myedegrees[k].ed-myrinfo->id; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D to %3D. Gain: %4D. Cut: %6D\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut)); - - /* Update where, weight, and ID/ED information of the vertex you moved */ - where[i] = to; - INC_DEC(pwgts[to], pwgts[from], vwgt); - myrinfo->ed += myrinfo->id-myedegrees[k].ed; - SWAP(myrinfo->id, myedegrees[k].ed, j); - if (myedegrees[k].ed == 0) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].pid = from; - - if (myrinfo->ed < myrinfo->id) - BNDDelete(nbnd, bndind, bndptr, i); - - /* Update the degrees of adjacent vertices */ - for (j=xadj[i]; jrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - ASSERT(CheckRInfo(myrinfo)); - - oldgain = (myrinfo->ed-myrinfo->id); - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1) - BNDInsert(nbnd, bndind, bndptr, ii); - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1) - BNDDelete(nbnd, bndind, bndptr, ii); - } - - /* Remove contribution from the .ed of 'from' */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ed == adjwgt[j]) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].ed -= adjwgt[j]; - break; - } - } - } - - /* Add contribution to the .ed of 'to' */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - - /* Update the queue */ - if (me == to || me == from) { - gain = myrinfo->ed-myrinfo->id; - if (moved[ii] == 2) { - if (gain >= 0) - PQueueUpdate(&queue, ii, oldgain, gain); - else { - PQueueDelete(&queue, ii, oldgain); - moved[ii] = -1; - } - } - else if (moved[ii] == -1 && gain >= 0) { - PQueueInsert(&queue, ii, gain); - moved[ii] = 2; - } - } - - ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]); - ASSERT(CheckRInfo(myrinfo)); - - } - } - - graph->nbnd = nbnd; - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\t[%6D %6D], Balance: %5.3f, Nb: %6D. Cut: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nbnd, graph->mincut)); - - if (graph->mincut == oldcut) - break; - } - - PQueueFree(ctrl, &queue); - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - -} - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void Greedy_KWayEdgeBalance(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, float ubfactor, idxtype npasses) -{ - idxtype i, ii, iii, j, jj, k, l, pass, nvtxs, nbnd, tvwgt, myndegrees, oldgain, gain, nmoves; - idxtype from, me, to, oldcut, vwgt; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *moved, *itpwgts; - EDegreeType *myedegrees; - RInfoType *myrinfo; - PQueueType queue; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndind = graph->bndind; - bndptr = graph->bndptr; - - where = graph->where; - pwgts = graph->pwgts; - - /* Setup the weight intervals of the various subdomains */ - minwgt = idxwspacemalloc(ctrl, nparts); - maxwgt = idxwspacemalloc(ctrl, nparts); - itpwgts = idxwspacemalloc(ctrl, nparts); - tvwgt = idxsum(nparts, pwgts, 1); - ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt, 1)); - - for (i=0; iadjwgtsum[idxargmax(nvtxs, graph->adjwgtsum)]); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D]-[%6D %6D], Balance: %5.3f, Nv-Nb[%6D %6D]. Cut: %6D [B]\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], minwgt[0], maxwgt[0], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd, - graph->mincut)); - - for (pass=0; passmincut); - - /* Check to see if things are out of balance, given the tolerance */ - for (i=0; i maxwgt[i]) - break; - } - if (i == nparts) /* Things are balanced. Return right away */ - break; - - PQueueReset(&queue); - idxset(nvtxs, -1, moved); - - oldcut = graph->mincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (ii=0; iirinfo[i].ed - graph->rinfo[i].id); - moved[i] = 2; - } - - nmoves = 0; - for (;;) { - if ((i = PQueueGetMax(&queue)) == -1) - break; - moved[i] = 1; - - myrinfo = graph->rinfo+i; - from = where[i]; - vwgt = graph->vwgt[i]; - - if (pwgts[from]-vwgt < minwgt[from]) - continue; /* This cannot be moved! */ - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - for (k=0; k minwgt[to] && myedegrees[k].ed-myrinfo->id < 0) - continue; - - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - graph->mincut -= myedegrees[k].ed-myrinfo->id; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D to %3D. Gain: %4D. Cut: %6D\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut)); - - /* Update where, weight, and ID/ED information of the vertex you moved */ - where[i] = to; - INC_DEC(pwgts[to], pwgts[from], vwgt); - myrinfo->ed += myrinfo->id-myedegrees[k].ed; - SWAP(myrinfo->id, myedegrees[k].ed, j); - if (myedegrees[k].ed == 0) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].pid = from; - - if (myrinfo->ed == 0) - BNDDelete(nbnd, bndind, bndptr, i); - - /* Update the degrees of adjacent vertices */ - for (j=xadj[i]; jrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - ASSERT(CheckRInfo(myrinfo)); - - oldgain = (myrinfo->ed-myrinfo->id); - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - - if (myrinfo->ed > 0 && bndptr[ii] == -1) - BNDInsert(nbnd, bndind, bndptr, ii); - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - - if (myrinfo->ed == 0 && bndptr[ii] != -1) - BNDDelete(nbnd, bndind, bndptr, ii); - } - - /* Remove contribution from the .ed of 'from' */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ed == adjwgt[j]) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].ed -= adjwgt[j]; - break; - } - } - } - - /* Add contribution to the .ed of 'to' */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - - /* Update the queue */ - if (me == to || me == from) { - gain = myrinfo->ed-myrinfo->id; - if (moved[ii] == 2) { - if (myrinfo->ed > 0) - PQueueUpdate(&queue, ii, oldgain, gain); - else { - PQueueDelete(&queue, ii, oldgain); - moved[ii] = -1; - } - } - else if (moved[ii] == -1 && myrinfo->ed > 0) { - PQueueInsert(&queue, ii, gain); - moved[ii] = 2; - } - } - - ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]); - ASSERT(CheckRInfo(myrinfo)); - } - nmoves++; - } - - graph->nbnd = nbnd; - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\t[%6D %6D], Balance: %5.3f, Nb: %6D. Nmoves: %5D, Cut: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut)); - } - - PQueueFree(ctrl, &queue); - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - -} - diff --git a/src/metis/kwayrefine.c b/src/metis/kwayrefine.c deleted file mode 100644 index 1fd5ea3..0000000 --- a/src/metis/kwayrefine.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * kwayrefine.c - * - * This file contains the driving routines for multilevel k-way refinement - * - * Started 7/28/97 - * George - * - * $Id: kwayrefine.c,v 1.5 2003/04/01 05:09:37 karypis Exp $ - */ - -#include - - -/************************************************************************* -* This function is the entry point of refinement -**************************************************************************/ -void RefineKWay(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, idxtype nparts, float *tpwgts, float ubfactor) -{ - idxtype i, nlevels, mustfree=0; - GraphType *ptr; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); - - /* Compute the parameters of the coarsest graph */ - ComputeKWayPartitionParams(ctrl, graph, nparts); - - - /* Take care any non-contiguity */ - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->AuxTmr1)); - if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) { - EliminateComponents(ctrl, graph, nparts, tpwgts, 1.25); - EliminateSubDomainEdges(ctrl, graph, nparts, tpwgts); - EliminateComponents(ctrl, graph, nparts, tpwgts, 1.25); - } - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->AuxTmr1)); - - /* Determine how many levels are there */ - for (ptr=graph, nlevels=0; ptr!=orggraph; ptr=ptr->finer, nlevels++); - - for (i=0; ;i++) { - /* PrintSubDomainGraph(graph, nparts, graph->where); */ - if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN && (i == nlevels/2 || i == nlevels/2+1)) - EliminateSubDomainEdges(ctrl, graph, nparts, tpwgts); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->RefTmr)); - - if (2*i >= nlevels && !IsBalanced(graph->pwgts, nparts, tpwgts, 1.04*ubfactor)) { - ComputeKWayBalanceBoundary(ctrl, graph, nparts); - if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) - Greedy_KWayEdgeBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 1); - else - Greedy_KWayEdgeBalance(ctrl, graph, nparts, tpwgts, ubfactor, 1); - ComputeKWayBoundary(ctrl, graph, nparts); - } - - switch (ctrl->RType) { - case RTYPE_KWAYRANDOM: - Random_KWayEdgeRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 1); - break; - case RTYPE_KWAYGREEDY: - Greedy_KWayEdgeRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10); - break; - case RTYPE_KWAYRANDOM_MCONN: - Random_KWayEdgeRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 1); - break; - } - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); - - if (graph == orggraph) - break; - - graph = graph->finer; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); - if (graph->vwgt == NULL) { - graph->vwgt = idxsmalloc(graph->nvtxs, 1, "RefineKWay: graph->vwgt"); - graph->adjwgt = idxsmalloc(graph->nedges, 1, "RefineKWay: graph->adjwgt"); - mustfree = 1; - } - ProjectKWayPartition(ctrl, graph, nparts); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); - } - - if (!IsBalanced(graph->pwgts, nparts, tpwgts, ubfactor)) { - ComputeKWayBalanceBoundary(ctrl, graph, nparts); - if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) { - Greedy_KWayEdgeBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 8); - Random_KWayEdgeRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); - } - else { - Greedy_KWayEdgeBalance(ctrl, graph, nparts, tpwgts, ubfactor, 8); - Random_KWayEdgeRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); - } - } - - /* Take care any trivial non-contiguity */ - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->AuxTmr2)); - EliminateComponents(ctrl, graph, nparts, tpwgts, ubfactor); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->AuxTmr2)); - - if (mustfree) - gk_free((void **)&graph->vwgt, &graph->adjwgt, LTERM); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); -} - - -/************************************************************************* -* This function is the entry point of refinement -**************************************************************************/ -void RefineKWayRefinement(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, idxtype nparts, float *tpwgts, float ubfactor) -{ - idxtype i, nlevels, mustfree=0; - GraphType *ptr; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); - - /* Compute the parameters of the coarsest graph */ - ComputeKWayPartitionParams(ctrl, graph, nparts); - - -#ifdef XXXX - /* Take care any non-contiguity */ - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->AuxTmr1)); - if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) { - EliminateComponents(ctrl, graph, nparts, tpwgts, 1.25); - EliminateSubDomainEdges(ctrl, graph, nparts, tpwgts); - EliminateComponents(ctrl, graph, nparts, tpwgts, 1.25); - } - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->AuxTmr1)); -#endif - - /* Determine how many levels are there */ - for (ptr=graph, nlevels=0; ptr!=orggraph; ptr=ptr->finer, nlevels++); - - for (i=0; ;i++) { -#ifdef XXXX - /* PrintSubDomainGraph(graph, nparts, graph->where); */ - if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN && (i == nlevels/2 || i == nlevels/2+1)) - EliminateSubDomainEdges(ctrl, graph, nparts, tpwgts); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->RefTmr)); -#endif - - if (2*i >= nlevels && !IsBalanced(graph->pwgts, nparts, tpwgts, 1.04*ubfactor)) { - ComputeKWayBalanceBoundary(ctrl, graph, nparts); - if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) - Greedy_KWayEdgeBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 1); - else - Greedy_KWayEdgeBalance(ctrl, graph, nparts, tpwgts, ubfactor, 1); - ComputeKWayBoundary(ctrl, graph, nparts); - } - - switch (ctrl->RType) { - case RTYPE_KWAYRANDOM: - Random_KWayEdgeRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 1); - break; - case RTYPE_KWAYGREEDY: - Greedy_KWayEdgeRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10); - break; - case RTYPE_KWAYRANDOM_MCONN: - Random_KWayEdgeRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 1); - break; - } - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); - - if (graph == orggraph) - break; - - graph = graph->finer; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); - if (graph->vwgt == NULL) { - graph->vwgt = idxsmalloc(graph->nvtxs, 1, "RefineKWay: graph->vwgt"); - graph->adjwgt = idxsmalloc(graph->nedges, 1, "RefineKWay: graph->adjwgt"); - mustfree = 1; - } - ProjectKWayPartition(ctrl, graph, nparts); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); - } - - if (!IsBalanced(graph->pwgts, nparts, tpwgts, ubfactor)) { - ComputeKWayBalanceBoundary(ctrl, graph, nparts); - if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) { - Greedy_KWayEdgeBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 8); - Random_KWayEdgeRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); - } - else { - Greedy_KWayEdgeBalance(ctrl, graph, nparts, tpwgts, ubfactor, 8); - Random_KWayEdgeRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); - } - } - - /* Take care any trivial non-contiguity */ - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->AuxTmr2)); - EliminateComponents(ctrl, graph, nparts, tpwgts, ubfactor); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->AuxTmr2)); - - if (mustfree) - gk_free((void **)&graph->vwgt, &graph->adjwgt, LTERM); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); -} - -/************************************************************************* -* This function allocates memory for k-way edge refinement -**************************************************************************/ -void AllocateKWayPartitionMemory(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype nvtxs; - - nvtxs = graph->nvtxs; - - graph->pwgts = idxmalloc(nparts, "AllocateKWayPartitionMemory: pwgts"); - graph->where = idxmalloc(nvtxs, "AllocateKWayPartitionMemory: pwgts"); - graph->bndptr = idxmalloc(nvtxs, "AllocateKWayPartitionMemory: pwgts"); - graph->bndind = idxmalloc(nvtxs, "AllocateKWayPartitionMemory: pwgts"); - graph->rinfo = (RInfoType *)gk_malloc(nvtxs*sizeof(RInfoType), "AllocateKWayPartitionMemory: rinfo"); - -} - - -/************************************************************************* -* This function computes the initial id/ed -**************************************************************************/ -void ComputeKWayPartitionParams(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, j, k, l, nvtxs, nbnd, mincut, me, other; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *pwgts, *where, *bndind, *bndptr; - RInfoType *rinfo, *myrinfo; - EDegreeType *myedegrees; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - pwgts = idxset(nparts, 0, graph->pwgts); - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - rinfo = graph->rinfo; - - - /*------------------------------------------------------------ - / Compute now the id/ed degrees - /------------------------------------------------------------*/ - ctrl->wspace.cdegree = 0; - nbnd = mincut = 0; - for (i=0; iid = myrinfo->ed = myrinfo->ndegrees = 0; - myrinfo->edegrees = NULL; - - for (j=xadj[i]; jed += adjwgt[j]; - } - myrinfo->id = graph->adjwgtsum[i] - myrinfo->ed; - - if (myrinfo->ed > 0) - mincut += myrinfo->ed; - - if (myrinfo->ed-myrinfo->id >= 0) - BNDInsert(nbnd, bndind, bndptr, i); - - /* Time to compute the particular external degrees */ - if (myrinfo->ed > 0) { - myedegrees = myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[i+1]-xadj[i]; - - for (j=xadj[i]; jndegrees; k++) { - if (myedegrees[k].pid == other) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = other; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - } - - ASSERT(myrinfo->ndegrees <= xadj[i+1]-xadj[i]); - } - } - - graph->mincut = mincut/2; - graph->nbnd = nbnd; - -} - - - -/************************************************************************* -* This function projects a partition, and at the same time computes the -* parameters for refinement. -**************************************************************************/ -void ProjectKWayPartition(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, j, k, nvtxs, nbnd, me, other, istart, iend, ndegrees; - idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum; - idxtype *cmap, *where, *bndptr, *bndind; - idxtype *cwhere; - GraphType *cgraph; - RInfoType *crinfo, *rinfo, *myrinfo; - EDegreeType *myedegrees; - idxtype *htable; - - cgraph = graph->coarser; - cwhere = cgraph->where; - crinfo = cgraph->rinfo; - - nvtxs = graph->nvtxs; - cmap = graph->cmap; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - - AllocateKWayPartitionMemory(ctrl, graph, nparts); - where = graph->where; - rinfo = graph->rinfo; - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - - /* Go through and project partition and compute id/ed for the nodes */ - for (i=0; iwspace.cdegree = 0; - for (nbnd=0, i=0; iid = myrinfo->ed = myrinfo->ndegrees = 0; - myrinfo->edegrees = NULL; - - myrinfo->id = adjwgtsum[i]; - - if (cmap[i] > 0) { /* If it is an interface node. Note cmap[i] = crinfo[cmap[i]].ed */ - istart = xadj[i]; - iend = xadj[i+1]; - - myedegrees = myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += iend-istart; - - ndegrees = 0; - for (j=istart; jed += adjwgt[j]; - if ((k = htable[other]) == -1) { - htable[other] = ndegrees; - myedegrees[ndegrees].pid = other; - myedegrees[ndegrees++].ed = adjwgt[j]; - } - else { - myedegrees[k].ed += adjwgt[j]; - } - } - } - myrinfo->id -= myrinfo->ed; - - /* Remove space for edegrees if it was interior */ - if (myrinfo->ed == 0) { - myrinfo->edegrees = NULL; - ctrl->wspace.cdegree -= iend-istart; - } - else { - if (myrinfo->ed-myrinfo->id >= 0) - BNDInsert(nbnd, bndind, bndptr, i); - - myrinfo->ndegrees = ndegrees; - - for (j=0; jpwgts, graph->pwgts); - graph->mincut = cgraph->mincut; - graph->nbnd = nbnd; - - FreeGraph(graph->coarser, 1); - graph->coarser = NULL; - - idxwspacefree(ctrl, nparts); - - ASSERT(CheckBnd2(graph)); - -} - - - -/************************************************************************* -* This function checks if the partition weights are within the balance -* contraints -**************************************************************************/ -idxtype IsBalanced(idxtype *pwgts, idxtype nparts, float *tpwgts, float ubfactor) -{ - idxtype i, j, tvwgt; - - tvwgt = idxsum(nparts, pwgts, 1); - for (i=0; i tpwgts[i]*tvwgt*(ubfactor+0.005)) - return 0; - } - - return 1; -} - - -/************************************************************************* -* This function computes the boundary definition for balancing -**************************************************************************/ -void ComputeKWayBoundary(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, nvtxs, nbnd; - idxtype *bndind, *bndptr; - - nvtxs = graph->nvtxs; - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - - - /*------------------------------------------------------------ - / Compute the new boundary - /------------------------------------------------------------*/ - nbnd = 0; - for (i=0; irinfo[i].ed-graph->rinfo[i].id >= 0) - BNDInsert(nbnd, bndind, bndptr, i); - } - - graph->nbnd = nbnd; -} - -/************************************************************************* -* This function computes the boundary definition for balancing -**************************************************************************/ -void ComputeKWayBalanceBoundary(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, nvtxs, nbnd; - idxtype *bndind, *bndptr; - - nvtxs = graph->nvtxs; - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - - - /*------------------------------------------------------------ - / Compute the new boundary - /------------------------------------------------------------*/ - nbnd = 0; - for (i=0; irinfo[i].ed > 0) - BNDInsert(nbnd, bndind, bndptr, i); - } - - graph->nbnd = nbnd; -} - diff --git a/src/metis/kwayvolfm.c b/src/metis/kwayvolfm.c deleted file mode 100644 index a2cfea4..0000000 --- a/src/metis/kwayvolfm.c +++ /dev/null @@ -1,1778 +0,0 @@ -/* - * kwayvolfm.c - * - * This file contains code that implements the multilevel k-way refinement - * - * Started 7/8/98 - * George - * - * $Id: kwayvolfm.c,v 1.3 2003/07/31 06:04:52 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void Random_KWayVolRefine(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, - float ubfactor, idxtype npasses, idxtype ffactor) -{ - idxtype i, ii, iii, j, jj, k, kk, l, u, pass, nvtxs, nmoves, tvwgt, myndegrees, xgain; - idxtype from, me, to, oldcut, oldvol, vwgt; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts, *updind, *marker, *phtable; - VEDegreeType *myedegrees; - VRInfoType *myrinfo; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndptr = graph->bndptr; - bndind = graph->bndind; - - where = graph->where; - pwgts = graph->pwgts; - - /* Setup the weight intervals of the various subdomains */ - minwgt = idxwspacemalloc(ctrl, nparts); - maxwgt = idxwspacemalloc(ctrl, nparts); - itpwgts = idxwspacemalloc(ctrl, nparts); - tvwgt = idxsum(nparts, pwgts, 1); - ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt, 1)); - - updind = idxmalloc(nvtxs, "Random_KWayVolRefine: updind"); - marker = idxsmalloc(nvtxs, 0, "Random_KWayVolRefine: marker"); - phtable = idxsmalloc(nparts, -1, "Random_KWayVolRefine: phtable"); - - for (i=0; idbglvl, DBG_REFINE, - mprintf("VolPart: [%5D %5D]-[%5D %5D], Balance: %3.2f, Nv-Nb[%5D %5D]. Cut: %5D, Vol: %5D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], minwgt[0], maxwgt[0], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd, - graph->mincut, graph->minvol)); - - for (pass=0; passmincut); - - oldcut = graph->mincut; - oldvol = graph->minvol; - - RandomPermute(graph->nbnd, perm, 1); - for (nmoves=iii=0; iiinbnd; iii++) { - ii = perm[iii]; - if (ii >= graph->nbnd) - continue; - i = bndind[ii]; - myrinfo = graph->vrinfo+i; - - if (myrinfo->gv >= 0) { /* Total volume gain is too high */ - from = where[i]; - vwgt = graph->vwgt[i]; - - if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from]) - continue; /* This cannot be moved! */ - - xgain = (myrinfo->id == 0 && myrinfo->ed > 0 ? graph->vsize[i] : 0); - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - for (k=0; k= 0) - break; - } - if (k == myndegrees) - continue; /* break out if you did not find a candidate */ - - for (j=k+1; j maxwgt[to]) - continue; - if (myedegrees[j].gv > myedegrees[k].gv || - (myedegrees[j].gv == myedegrees[k].gv && myedegrees[j].ed > myedegrees[k].ed) || - (myedegrees[j].gv == myedegrees[k].gv && myedegrees[j].ed == myedegrees[k].ed && - itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid])) - k = j; - } - - to = myedegrees[k].pid; - - j = 0; - if (xgain+myedegrees[k].gv > 0 || myedegrees[k].ed-myrinfo->id > 0) - j = 1; - else if (myedegrees[k].ed-myrinfo->id == 0) { - if ((iii&5) == 0 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from]) - j = 1; - } - if (j == 0) - continue; - - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - INC_DEC(pwgts[to], pwgts[from], vwgt); - graph->mincut -= myedegrees[k].ed-myrinfo->id; - graph->minvol -= (xgain+myedegrees[k].gv); - where[i] = to; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D from %3D to %3D. Gain: [%4D %4D]. Cut: %6D, Vol: %6D\n", - i, from, to, xgain+myedegrees[k].gv, myedegrees[k].ed-myrinfo->id, graph->mincut, graph->minvol)); - - KWayVolUpdate(ctrl, graph, i, from, to, marker, phtable, updind); - - nmoves++; - - /* CheckVolKWayPartitionParams(ctrl, graph, nparts); */ - } - } - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\t[%6D %6D], Balance: %5.3f, Nb: %6D. Nmoves: %5D, Cut: %6D, Vol: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, - graph->minvol)); - - if (graph->minvol == oldvol && graph->mincut == oldcut) - break; - } - - gk_free((void **)&marker, &updind, &phtable, LTERM); - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); -} - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void Random_KWayVolRefineMConn(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, - float ubfactor, idxtype npasses, idxtype ffactor) -{ - idxtype i, ii, iii, j, jj, k, kk, l, u, pass, nvtxs, nmoves, tvwgt, myndegrees, xgain; - idxtype from, me, to, oldcut, oldvol, vwgt, nadd, maxndoms; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts, *updind, *marker, *phtable; - idxtype *pmat, *pmatptr, *ndoms; - VEDegreeType *myedegrees; - VRInfoType *myrinfo; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndptr = graph->bndptr; - bndind = graph->bndind; - - where = graph->where; - pwgts = graph->pwgts; - - /* Setup the weight intervals of the various subdomains */ - minwgt = idxwspacemalloc(ctrl, nparts); - maxwgt = idxwspacemalloc(ctrl, nparts); - itpwgts = idxwspacemalloc(ctrl, nparts); - tvwgt = idxsum(nparts, pwgts, 1); - ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt, 1)); - - updind = idxmalloc(nvtxs, "Random_KWayVolRefine: updind"); - marker = idxsmalloc(nvtxs, 0, "Random_KWayVolRefine: marker"); - phtable = idxsmalloc(nparts, -1, "Random_KWayVolRefine: phtable"); - - pmat = ctrl->wspace.pmat; - ndoms = idxwspacemalloc(ctrl, nparts); - - ComputeVolSubDomainGraph(graph, nparts, pmat, ndoms); - - for (i=0; idbglvl, DBG_REFINE, - mprintf("VolPart: [%5D %5D]-[%5D %5D], Balance: %3.2f, Nv-Nb[%5D %5D]. Cut: %5D, Vol: %5D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], minwgt[0], maxwgt[0], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd, - graph->mincut, graph->minvol)); - - for (pass=0; passmincut); - - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - - oldcut = graph->mincut; - oldvol = graph->minvol; - - RandomPermute(graph->nbnd, perm, 1); - for (nmoves=iii=0; iiinbnd; iii++) { - ii = perm[iii]; - if (ii >= graph->nbnd) - continue; - i = bndind[ii]; - myrinfo = graph->vrinfo+i; - - if (myrinfo->gv >= 0) { /* Total volume gain is too high */ - from = where[i]; - vwgt = graph->vwgt[i]; - - if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from]) - continue; /* This cannot be moved! */ - - xgain = (myrinfo->id == 0 && myrinfo->ed > 0 ? graph->vsize[i] : 0); - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - /* Determine the valid domains */ - for (j=0; j maxndoms-1) { - phtable[to] = 0; - nadd = maxndoms; - break; - } - nadd++; - } - } - if (ndoms[to]+nadd > maxndoms) - phtable[to] = 0; - if (nadd == 0) - phtable[to] = 2; - } - - for (k=0; k= 0) - break; - } - if (k == myndegrees) - continue; /* break out if you did not find a candidate */ - - for (j=k+1; j maxwgt[to]) - continue; - if (myedegrees[j].gv > myedegrees[k].gv || - (myedegrees[j].gv == myedegrees[k].gv && myedegrees[j].ed > myedegrees[k].ed) || - (myedegrees[j].gv == myedegrees[k].gv && myedegrees[j].ed == myedegrees[k].ed && - itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid])) - k = j; - } - - to = myedegrees[k].pid; - - j = 0; - if (xgain+myedegrees[k].gv > 0 || myedegrees[k].ed-myrinfo->id > 0) - j = 1; - else if (myedegrees[k].ed-myrinfo->id == 0) { - if ((iii&5) == 0 || phtable[myedegrees[k].pid] == 2 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from]) - j = 1; - } - - if (j == 0) - continue; - - for (j=0; jmincut -= myedegrees[k].ed-myrinfo->id; - graph->minvol -= (xgain+myedegrees[k].gv); - where[i] = to; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D from %3D to %3D. Gain: [%4D %4D]. Cut: %6D, Vol: %6D\n", - i, from, to, xgain+myedegrees[k].gv, myedegrees[k].ed-myrinfo->id, graph->mincut, graph->minvol)); - - /* Update pmat to reflect the move of 'i' */ - pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed); - pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed); - if (pmat[from*nparts+to] == 0) { - ndoms[from]--; - if (ndoms[from]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - if (pmat[to*nparts+from] == 0) { - ndoms[to]--; - if (ndoms[to]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - - for (j=xadj[i]; j maxndoms) { - mprintf("You just increased the maxndoms: %D %D\n", ndoms[me], maxndoms); - maxndoms = ndoms[me]; - } - } - if (pmat[to*nparts+me] == 0) { - ndoms[to]++; - if (ndoms[to] > maxndoms) { - mprintf("You just increased the maxndoms: %D %D\n", ndoms[to], maxndoms); - maxndoms = ndoms[to]; - } - } - pmat[me*nparts+to] += adjwgt[j]; - pmat[to*nparts+me] += adjwgt[j]; - } - } - - KWayVolUpdate(ctrl, graph, i, from, to, marker, phtable, updind); - - nmoves++; - - /* CheckVolKWayPartitionParams(ctrl, graph, nparts); */ - } - } - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\t[%6D %6D], Balance: %5.3f, Nb: %6D. Nmoves: %5D, Cut: %6D, Vol: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, - graph->minvol)); - - if (graph->minvol == oldvol && graph->mincut == oldcut) - break; - } - - gk_free((void **)&marker, &updind, &phtable, LTERM); - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); -} - - - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void Greedy_KWayVolBalance(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, - float ubfactor, idxtype npasses) -{ - idxtype i, ii, iii, j, jj, k, kk, l, u, pass, nvtxs, nmoves, tvwgt, myndegrees, xgain; - idxtype from, me, to, vwgt, gain; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *pwgts, *perm, *moved, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts, *updind, *marker, *phtable; - VEDegreeType *myedegrees; - VRInfoType *myrinfo; - PQueueType queue; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndptr = graph->bndptr; - bndind = graph->bndind; - - where = graph->where; - pwgts = graph->pwgts; - - /* Setup the weight intervals of the various subdomains */ - minwgt = idxwspacemalloc(ctrl, nparts); - maxwgt = idxwspacemalloc(ctrl, nparts); - itpwgts = idxwspacemalloc(ctrl, nparts); - tvwgt = idxsum(nparts, pwgts, 1); - ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt, 1)); - - updind = idxmalloc(nvtxs, "Random_KWayVolRefine: updind"); - marker = idxsmalloc(nvtxs, 0, "Random_KWayVolRefine: marker"); - phtable = idxsmalloc(nparts, -1, "Random_KWayVolRefine: phtable"); - - for (i=0; iadjwgtsum[idxargmax(nvtxs, graph->adjwgtsum)]); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("VolPart: [%5D %5D]-[%5D %5D], Balance: %3.2f, Nv-Nb[%5D %5D]. Cut: %5D, Vol: %5D [B]\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], minwgt[0], maxwgt[0], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd, - graph->mincut, graph->minvol)); - - - for (pass=0; passmincut); - /* Check to see if things are out of balance, given the tolerance */ - for (i=0; i maxwgt[i]) - break; - } - if (i == nparts) /* Things are balanced. Return right away */ - break; - - PQueueReset(&queue); - idxset(nvtxs, -1, moved); - - RandomPermute(graph->nbnd, perm, 1); - for (ii=0; iinbnd; ii++) { - i = bndind[perm[ii]]; - PQueueInsert(&queue, i, graph->vrinfo[i].gv); - moved[i] = 2; - } - - for (nmoves=0;;) { - if ((i = PQueueGetMax(&queue)) == -1) - break; - moved[i] = 1; - - myrinfo = graph->vrinfo+i; - from = where[i]; - vwgt = graph->vwgt[i]; - - if (pwgts[from]-vwgt < minwgt[from]) - continue; /* This cannot be moved! */ - - xgain = (myrinfo->id == 0 && myrinfo->ed > 0 ? graph->vsize[i] : 0); - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - for (k=0; k minwgt[to] && - (xgain+myedegrees[k].gv < 0 || - (xgain+myedegrees[k].gv == 0 && myedegrees[k].ed-myrinfo->id < 0)) - ) - continue; - - - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - INC_DEC(pwgts[to], pwgts[from], vwgt); - graph->mincut -= myedegrees[k].ed-myrinfo->id; - graph->minvol -= (xgain+myedegrees[k].gv); - where[i] = to; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D from %3D to %3D. Gain: [%4D %4D]. Cut: %6D, Vol: %6D\n", - i, from, to, xgain+myedegrees[k].gv, myedegrees[k].ed-myrinfo->id, graph->mincut, graph->minvol)); - - KWayVolUpdate(ctrl, graph, i, from, to, marker, phtable, updind); - - nmoves++; - - /*CheckVolKWayPartitionParams(ctrl, graph, nparts); */ - } - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\t[%6D %6D], Balance: %5.3f, Nb: %6D. Nmoves: %5D, Cut: %6D, Vol: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, - graph->minvol)); - - } - - gk_free((void **)&marker, &updind, &phtable, LTERM); - - PQueueFree(ctrl, &queue); - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void Greedy_KWayVolBalanceMConn(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, - float ubfactor, idxtype npasses) -{ - idxtype i, ii, iii, j, jj, k, kk, l, u, pass, nvtxs, nmoves, tvwgt, myndegrees, xgain; - idxtype from, me, to, vwgt, gain, maxndoms, nadd; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *pwgts, *perm, *moved, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts, *updind, *marker, *phtable; - idxtype *pmat, *pmatptr, *ndoms; - VEDegreeType *myedegrees; - VRInfoType *myrinfo; - PQueueType queue; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndptr = graph->bndptr; - bndind = graph->bndind; - - where = graph->where; - pwgts = graph->pwgts; - - /* Setup the weight intervals of the various subdomains */ - minwgt = idxwspacemalloc(ctrl, nparts); - maxwgt = idxwspacemalloc(ctrl, nparts); - itpwgts = idxwspacemalloc(ctrl, nparts); - tvwgt = idxsum(nparts, pwgts, 1); - ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt, 1)); - - updind = idxmalloc(nvtxs, "Random_KWayVolRefine: updind"); - marker = idxsmalloc(nvtxs, 0, "Random_KWayVolRefine: marker"); - phtable = idxsmalloc(nparts, -1, "Random_KWayVolRefine: phtable"); - - pmat = ctrl->wspace.pmat; - ndoms = idxwspacemalloc(ctrl, nparts); - - ComputeVolSubDomainGraph(graph, nparts, pmat, ndoms); - - for (i=0; iadjwgtsum[idxargmax(nvtxs, graph->adjwgtsum)]); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("VolPart: [%5D %5D]-[%5D %5D], Balance: %3.2f, Nv-Nb[%5D %5D]. Cut: %5D, Vol: %5D [B]\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], minwgt[0], maxwgt[0], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd, - graph->mincut, graph->minvol)); - - - for (pass=0; passmincut); - /* Check to see if things are out of balance, given the tolerance */ - for (i=0; i maxwgt[i]) - break; - } - if (i == nparts) /* Things are balanced. Return right away */ - break; - - PQueueReset(&queue); - idxset(nvtxs, -1, moved); - - RandomPermute(graph->nbnd, perm, 1); - for (ii=0; iinbnd; ii++) { - i = bndind[perm[ii]]; - PQueueInsert(&queue, i, graph->vrinfo[i].gv); - moved[i] = 2; - } - - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - - for (nmoves=0;;) { - if ((i = PQueueGetMax(&queue)) == -1) - break; - moved[i] = 1; - - myrinfo = graph->vrinfo+i; - from = where[i]; - vwgt = graph->vwgt[i]; - - if (pwgts[from]-vwgt < minwgt[from]) - continue; /* This cannot be moved! */ - - xgain = (myrinfo->id == 0 && myrinfo->ed > 0 ? graph->vsize[i] : 0); - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - /* Determine the valid domains */ - for (j=0; j maxndoms-1) { - phtable[to] = 0; - nadd = maxndoms; - break; - } - nadd++; - } - } - if (ndoms[to]+nadd > maxndoms) - phtable[to] = 0; - } - - for (k=0; k minwgt[to] && - (xgain+myedegrees[k].gv < 0 || - (xgain+myedegrees[k].gv == 0 && myedegrees[k].ed-myrinfo->id < 0)) - ) - continue; - - - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - INC_DEC(pwgts[to], pwgts[from], vwgt); - graph->mincut -= myedegrees[k].ed-myrinfo->id; - graph->minvol -= (xgain+myedegrees[k].gv); - where[i] = to; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D from %3D to %3D. Gain: [%4D %4D]. Cut: %6D, Vol: %6D\n", - i, from, to, xgain+myedegrees[k].gv, myedegrees[k].ed-myrinfo->id, graph->mincut, graph->minvol)); - - /* Update pmat to reflect the move of 'i' */ - pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed); - pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed); - if (pmat[from*nparts+to] == 0) { - ndoms[from]--; - if (ndoms[from]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - if (pmat[to*nparts+from] == 0) { - ndoms[to]--; - if (ndoms[to]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - - for (j=xadj[i]; j maxndoms) { - mprintf("You just increased the maxndoms: %D %D\n", ndoms[me], maxndoms); - maxndoms = ndoms[me]; - } - } - if (pmat[to*nparts+me] == 0) { - ndoms[to]++; - if (ndoms[to] > maxndoms) { - mprintf("You just increased the maxndoms: %D %D\n", ndoms[to], maxndoms); - maxndoms = ndoms[to]; - } - } - pmat[me*nparts+to] += adjwgt[j]; - pmat[to*nparts+me] += adjwgt[j]; - } - } - - KWayVolUpdate(ctrl, graph, i, from, to, marker, phtable, updind); - - nmoves++; - - /*CheckVolKWayPartitionParams(ctrl, graph, nparts); */ - } - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\t[%6D %6D], Balance: %5.3f, Nb: %6D. Nmoves: %5D, Cut: %6D, Vol: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, - graph->minvol)); - - } - - gk_free((void **)&marker, &updind, &phtable, LTERM); - - PQueueFree(ctrl, &queue); - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - - -/************************************************************************* -* This function updates the edge and volume gains as a result of moving -* v from 'from' to 'to'. -* The working arrays marker and phtable are agk_fsumed to be initialized to -* -1, and they left to -1 upon return -**************************************************************************/ -void KWayVolUpdate(CtrlType *ctrl, GraphType *graph, idxtype v, idxtype from, idxtype to, - idxtype *marker, idxtype *phtable, idxtype *updind) -{ - idxtype ii, iii, j, jj, k, kk, l, u, nupd, other, me, myidx; - idxtype *xadj, *vsize, *adjncy, *adjwgt, *where; - VEDegreeType *myedegrees, *oedegrees; - VRInfoType *myrinfo, *orinfo; - - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - vsize = graph->vsize; - where = graph->where; - - myrinfo = graph->vrinfo+v; - myedegrees = myrinfo->edegrees; - - - /*====================================================================== - * Remove the contributions on the gain made by 'v'. - *=====================================================================*/ - for (k=0; kndegrees; k++) - phtable[myedegrees[k].pid] = k; - phtable[from] = k; - - myidx = phtable[to]; /* Keep track of the index in myedegrees of the 'to' domain */ - - for (j=xadj[v]; jvrinfo+ii; - oedegrees = orinfo->edegrees; - - if (other == from) { - for (k=0; kndegrees; k++) { - if (phtable[oedegrees[k].pid] == -1) - oedegrees[k].gv += vsize[v]; - } - } - else { - ASSERT(phtable[other] != -1); - - if (myedegrees[phtable[other]].ned > 1) { - for (k=0; kndegrees; k++) { - if (phtable[oedegrees[k].pid] == -1) - oedegrees[k].gv += vsize[v]; - } - } - else { /* There is only one connection */ - for (k=0; kndegrees; k++) { - if (phtable[oedegrees[k].pid] != -1) - oedegrees[k].gv -= vsize[v]; - } - } - } - } - - for (k=0; kndegrees; k++) - phtable[myedegrees[k].pid] = -1; - phtable[from] = -1; - - - /*====================================================================== - * Update the id/ed of vertex 'v' - *=====================================================================*/ - myrinfo->ed += myrinfo->id-myedegrees[myidx].ed; - SWAP(myrinfo->id, myedegrees[myidx].ed, j); - SWAP(myrinfo->nid, myedegrees[myidx].ned, j); - if (myedegrees[myidx].ed == 0) - myedegrees[myidx] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[myidx].pid = from; - - /*====================================================================== - * Update the degrees of adjacent vertices and their volume gains - *=====================================================================*/ - marker[v] = 1; - updind[0] = v; - nupd = 1; - for (j=xadj[v]; jvrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.vedegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - myrinfo->nid--; - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - myrinfo->nid++; - } - - /* Remove the edgeweight from the 'pid == from' entry of the vertex */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ned == 1) { - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - marker[ii] = 1; /* You do a complete .gv calculation */ - - /* All vertices adjacent to 'ii' need to be updated */ - for (jj=xadj[ii]; jjvrinfo+u; - oedegrees = orinfo->edegrees; - - for (kk=0; kkndegrees; kk++) { - if (oedegrees[kk].pid == from) { - oedegrees[kk].gv -= vsize[ii]; - break; - } - } - } - } - else { - myedegrees[k].ed -= adjwgt[j]; - myedegrees[k].ned--; - - /* Update the gv due to single 'ii' connection to 'from' */ - if (myedegrees[k].ned == 1) { - /* find the vertex 'u' that 'ii' was connected into 'from' */ - for (jj=xadj[ii]; jjvrinfo+u; - oedegrees = orinfo->edegrees; - - if (other == from) { - for (kk=0; kkndegrees; kk++) - oedegrees[kk].gv += vsize[ii]; - break; - } - } - } - } - - break; - } - } - } - - /* Add the edgeweight to the 'pid == to' entry of the vertex */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - myedegrees[k].ned++; - - /* Update the gv due to non-single 'ii' connection to 'to' */ - if (myedegrees[k].ned == 2) { - /* find the vertex 'u' that 'ii' was connected into 'to' */ - for (jj=xadj[ii]; jjvrinfo+u; - oedegrees = orinfo->edegrees; - - if (u != v && other == to) { - for (kk=0; kkndegrees; kk++) - oedegrees[kk].gv -= vsize[ii]; - break; - } - } - } - break; - } - } - - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees].ed = adjwgt[j]; - myedegrees[myrinfo->ndegrees++].ned = 1; - marker[ii] = 1; /* You do a complete .gv calculation */ - - /* All vertices adjacent to 'ii' need to be updated */ - for (jj=xadj[ii]; jjvrinfo+u; - oedegrees = orinfo->edegrees; - - for (kk=0; kkndegrees; kk++) { - if (oedegrees[kk].pid == to) { - oedegrees[kk].gv += vsize[ii]; - if (!marker[u]) { /* Need to update boundary etc */ - marker[u] = 2; - updind[nupd++] = u; - } - break; - } - } - } - } - } - - ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]); - } - - /*====================================================================== - * Add the contributions on the volume gain due to 'v' - *=====================================================================*/ - myrinfo = graph->vrinfo+v; - myedegrees = myrinfo->edegrees; - for (k=0; kndegrees; k++) - phtable[myedegrees[k].pid] = k; - phtable[to] = k; - - for (j=xadj[v]; jvrinfo+ii; - oedegrees = orinfo->edegrees; - - if (other == to) { - for (k=0; kndegrees; k++) { - if (phtable[oedegrees[k].pid] == -1) - oedegrees[k].gv -= vsize[v]; - } - } - else { - ASSERT(phtable[other] != -1); - - if (myedegrees[phtable[other]].ned > 1) { - for (k=0; kndegrees; k++) { - if (phtable[oedegrees[k].pid] == -1) - oedegrees[k].gv -= vsize[v]; - } - } - else { /* There is only one connection */ - for (k=0; kndegrees; k++) { - if (phtable[oedegrees[k].pid] != -1) - oedegrees[k].gv += vsize[v]; - } - } - } - } - for (k=0; kndegrees; k++) - phtable[myedegrees[k].pid] = -1; - phtable[to] = -1; - - - /*====================================================================== - * Recompute the volume information of the 'hard' nodes, and update the - * max volume gain for all the update vertices - *=====================================================================*/ - ComputeKWayVolume(graph, nupd, updind, marker, phtable); - - - /*====================================================================== - * Maintain a consistent boundary - *=====================================================================*/ - for (j=0; jvrinfo+k; - - if ((myrinfo->gv >= 0 || myrinfo->ed-myrinfo->id >= 0) && graph->bndptr[k] == -1) - BNDInsert(graph->nbnd, graph->bndind, graph->bndptr, k); - - if (myrinfo->gv < 0 && myrinfo->ed-myrinfo->id < 0 && graph->bndptr[k] != -1) - BNDDelete(graph->nbnd, graph->bndind, graph->bndptr, k); - } - -} - - - - -/************************************************************************* -* This function computes the initial id/ed -**************************************************************************/ -void ComputeKWayVolume(GraphType *graph, idxtype nupd, idxtype *updind, idxtype *marker, idxtype *phtable) -{ - idxtype ii, iii, i, j, k, kk, l, nvtxs, me, other, pid; - idxtype *xadj, *vsize, *adjncy, *adjwgt, *where; - VRInfoType *rinfo, *myrinfo, *orinfo; - VEDegreeType *myedegrees, *oedegrees; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vsize = graph->vsize; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - rinfo = graph->vrinfo; - - - /*------------------------------------------------------------ - / Compute now the iv/ev degrees - /------------------------------------------------------------*/ - for (iii=0; iiiedegrees; - - if (marker[i] == 1) { /* Only complete gain updates go through */ - for (k=0; kndegrees; k++) - myedegrees[k].gv = 0; - - for (j=xadj[i]; jedegrees; - - for (kk=0; kkndegrees; kk++) - phtable[oedegrees[kk].pid] = kk; - phtable[other] = 1; - - if (me == other) { - /* Find which domains 'i' is connected and 'ii' is not and update their gain */ - for (k=0; kndegrees; k++) { - if (phtable[myedegrees[k].pid] == -1) - myedegrees[k].gv -= vsize[ii]; - } - } - else { - ASSERT(phtable[me] != -1); - - /* I'm the only connection of 'ii' in 'me' */ - if (oedegrees[phtable[me]].ned == 1) { - /* Increase the gains for all the common domains between 'i' and 'ii' */ - for (k=0; kndegrees; k++) { - if (phtable[myedegrees[k].pid] != -1) - myedegrees[k].gv += vsize[ii]; - } - } - else { - /* Find which domains 'i' is connected and 'ii' is not and update their gain */ - for (k=0; kndegrees; k++) { - if (phtable[myedegrees[k].pid] == -1) - myedegrees[k].gv -= vsize[ii]; - } - } - } - - for (kk=0; kkndegrees; kk++) - phtable[oedegrees[kk].pid] = -1; - phtable[other] = -1; - - } - } - - myrinfo->gv = -MAXIDX; - for (k=0; kndegrees; k++) { - if (myedegrees[k].gv > myrinfo->gv) - myrinfo->gv = myedegrees[k].gv; - } - if (myrinfo->ed > 0 && myrinfo->id == 0) - myrinfo->gv += vsize[i]; - - } - -} - - - -/************************************************************************* -* This function computes the total volume -**************************************************************************/ -idxtype ComputeVolume(GraphType *graph, idxtype *where) -{ - idxtype i, j, k, me, nvtxs, nparts, totalv; - idxtype *xadj, *adjncy, *vsize, *marker; - - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vsize = (graph->vsize == NULL ? graph->vwgt : graph->vsize); - - nparts = where[idxargmax(nvtxs, where)]+1; - marker = idxsmalloc(nparts, -1, "ComputeVolume: marker"); - - totalv = 0; - - for (i=0; invtxs; - xadj = graph->xadj; - vsize = graph->vsize; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - rinfo = graph->vrinfo; - - tmpdegrees = (VEDegreeType *)gk_malloc(nparts*sizeof(VEDegreeType), "CheckVolKWayPartitionParams: tmpdegrees"); - - /*------------------------------------------------------------ - / Compute now the iv/ev degrees - /------------------------------------------------------------*/ - for (i=0; iedegrees; - - for (k=0; kndegrees; k++) - tmpdegrees[k] = myedegrees[k]; - - tmprinfo.ndegrees = myrinfo->ndegrees; - tmprinfo.id = myrinfo->id; - tmprinfo.ed = myrinfo->ed; - - myrinfo = &tmprinfo; - myedegrees = tmpdegrees; - - - for (k=0; kndegrees; k++) - myedegrees[k].gv = 0; - - for (j=xadj[i]; jedegrees; - - if (me == other) { - /* Find which domains 'i' is connected and 'ii' is not and update their gain */ - for (k=0; kndegrees; k++) { - pid = myedegrees[k].pid; - for (kk=0; kkndegrees; kk++) { - if (oedegrees[kk].pid == pid) - break; - } - if (kk == orinfo->ndegrees) - myedegrees[k].gv -= vsize[ii]; - } - } - else { - /* Find the orinfo[me].ed and see if I'm the only connection */ - for (k=0; kndegrees; k++) { - if (oedegrees[k].pid == me) - break; - } - - if (oedegrees[k].ned == 1) { /* I'm the only connection of 'ii' in 'me' */ - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == other) { - myedegrees[k].gv += vsize[ii]; - break; - } - } - - /* Increase the gains for all the common domains between 'i' and 'ii' */ - for (k=0; kndegrees; k++) { - if ((pid = myedegrees[k].pid) == other) - continue; - for (kk=0; kkndegrees; kk++) { - if (oedegrees[kk].pid == pid) { - myedegrees[k].gv += vsize[ii]; - break; - } - } - } - - } - else { - /* Find which domains 'i' is connected and 'ii' is not and update their gain */ - for (k=0; kndegrees; k++) { - if ((pid = myedegrees[k].pid) == other) - continue; - for (kk=0; kkndegrees; kk++) { - if (oedegrees[kk].pid == pid) - break; - } - if (kk == orinfo->ndegrees) - myedegrees[k].gv -= vsize[ii]; - } - } - } - } - - myrinfo = rinfo+i; - myedegrees = myrinfo->edegrees; - - for (k=0; kndegrees; k++) { - pid = myedegrees[k].pid; - for (kk=0; kknvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - rinfo = graph->vrinfo; - - idxset(nparts*nparts, 0, pmat); - - for (i=0; i 0) { - me = where[i]; - ndegrees = rinfo[i].ndegrees; - edegrees = rinfo[i].edegrees; - - k = me*nparts; - for (j=0; j 0) - ndoms[i]++; - } - } -} - - - -/************************************************************************* -* This function computes the subdomain graph -**************************************************************************/ -void EliminateVolSubDomainEdges(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts) -{ - idxtype i, ii, j, k, me, other, nvtxs, total, max, avg, totalout, nind, ncand, ncand2, target, target2, nadd; - idxtype min, move, cpwgt, tvwgt; - idxtype *xadj, *adjncy, *vwgt, *adjwgt, *pwgts, *where, *maxpwgt, *pmat, *ndoms, *mypmat, *otherpmat, *ind; - KeyValueType *cand, *cand2; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - adjwgt = graph->adjwgt; - - where = graph->where; - pwgts = idxset(nparts, 0, graph->pwgts); - - maxpwgt = idxwspacemalloc(ctrl, nparts); - ndoms = idxwspacemalloc(ctrl, nparts); - otherpmat = idxwspacemalloc(ctrl, nparts); - ind = idxwspacemalloc(ctrl, nvtxs); - pmat = idxset(nparts*nparts, 0, ctrl->wspace.pmat); - - cand = (KeyValueType *)gk_malloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand"); - cand2 = (KeyValueType *)gk_malloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand"); - - /* Compute the pmat matrix */ - for (i=0; i 0) - k++; - } - ndoms[i] = k; - } - - /* Get into the loop eliminating subdomain connections */ - for (;;) { - total = idxsum(nparts, ndoms, 1); - avg = total/nparts; - max = ndoms[idxargmax(nparts, ndoms)]; - - /* mprintf("Adjacent Subdomain Stats: Total: %3D, Max: %3D, Avg: %3D\n", total, max, avg); */ - - if (max < 1.5*avg) - break; - - me = idxargmax(nparts, ndoms); - mypmat = pmat + me*nparts; - totalout = idxsum(nparts, mypmat, 1); - - /*mprintf("Me: %D, TotalOut: %D,\n", me, totalout);*/ - - /* Sort the connections according to their cut */ - for (ncand2=0, i=0; i 0) { - cand2[ncand2].key = mypmat[i]; - cand2[ncand2++].val = i; - } - } - ikeysort(ncand2, cand2); - - move = 0; - for (min=0; min totalout/(2*ndoms[me])) - break; - - other = cand2[min].val; - - /*mprintf("\tMinOut: %D to %D\n", mypmat[other], other);*/ - - idxset(nparts, 0, otherpmat); - - /* Go and find the vertices in 'other' that are connected in 'me' */ - for (nind=0, i=0; i 0) { - cand[ncand].key = -otherpmat[i]; - cand[ncand++].val = i; - } - } - ikeysort(ncand, cand); - - /* - * Go through and the select the first domain that is common with 'me', and - * does not increase the ndoms[target] higher than my ndoms, subject to the - * maxpwgt constraint. Traversal is done from the mostly connected to the least. - */ - target = target2 = -1; - for (i=0; i 0) { - if (pwgts[k] + cpwgt > maxpwgt[k]) /* Check if balance will go off */ - continue; - - for (j=0; j 0 && ndoms[j] >= ndoms[me]-1 && pmat[nparts*j+k] == 0) - break; - } - if (j == nparts) { /* No bad second level effects */ - for (nadd=0, j=0; j 0 && pmat[nparts*k+j] == 0) - nadd++; - } - - /*mprintf("\t\tto=%D, nadd=%D, %D\n", k, nadd, ndoms[k]);*/ - if (target2 == -1 && ndoms[k]+nadd < ndoms[me]) { - target2 = k; - } - if (nadd == 0) { - target = k; - break; - } - } - } - } - if (target == -1 && target2 != -1) - target = target2; - - if (target == -1) { - /* mprintf("\t\tCould not make the move\n");*/ - continue; - } - - /*mprintf("\t\tMoving to %D\n", target);*/ - - /* Update the partition weights */ - INC_DEC(pwgts[target], pwgts[other], cpwgt); - - /* Set all nind vertices to belong to 'target' */ - for (ii=0; iiwhere and tries to push them around to -* remove some of them -**************************************************************************/ -void EliminateVolComponents(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, float ubfactor) -{ - idxtype i, ii, j, jj, k, me, nvtxs, tvwgt, first, last, nleft, ncmps, cwgt, ncand, other, target, deltawgt; - idxtype *xadj, *adjncy, *vwgt, *adjwgt, *where, *pwgts, *maxpwgt; - idxtype *cpvec, *touched, *perm, *todo, *cind, *cptr, *npcmps; - KeyValueType *cand; - idxtype recompute=0; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - adjwgt = graph->adjwgt; - - where = graph->where; - pwgts = idxset(nparts, 0, graph->pwgts); - - touched = idxset(nvtxs, 0, idxwspacemalloc(ctrl, nvtxs)); - cptr = idxwspacemalloc(ctrl, nvtxs+1); - cind = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - todo = idxwspacemalloc(ctrl, nvtxs); - maxpwgt = idxwspacemalloc(ctrl, nparts); - cpvec = idxwspacemalloc(ctrl, nparts); - npcmps = idxset(nparts, 0, idxwspacemalloc(ctrl, nparts)); - - for (i=0; i 0) { - if (first == last) { /* Find another starting vertex */ - cptr[++ncmps] = first; - ASSERT(touched[todo[0]] == 0); - i = todo[0]; - cind[last++] = i; - touched[i] = 1; - me = where[i]; - npcmps[me]++; - } - - i = cind[first++]; - k = perm[i]; - j = todo[k] = todo[--nleft]; - perm[j] = k; - - for (j=xadj[i]; j nparts) { /* There are more components than processors */ - cand = (KeyValueType *)gk_malloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand"); - - /* First determine the partition sizes and max allowed load imbalance */ - for (i=0; i .30*pwgts[me]) - continue; /* Skip the component if it is over 30% of the weight */ - - for (ncand=0, j=0; j 0) { - cand[ncand].key = -cpvec[j]; - cand[ncand++].val = j; - } - } - if (ncand == 0) - continue; - - ikeysort(ncand, cand); - - target = -1; - for (j=0; jmincut -= cpvec[target]; - recompute = 1; - } - } - - gk_free((void **)&cand, LTERM); - } - - if (recompute) { - idxtype ttlv; - idxtype *marker; - - marker = idxset(nparts, -1, cpvec); - for (ttlv=0, i=0; ivsize[i]; - marker[where[adjncy[j]]] = i; - } - } - } - graph->minvol = ttlv; - } - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs+1); - -} - diff --git a/src/metis/kwayvolrefine.c b/src/metis/kwayvolrefine.c deleted file mode 100644 index 7d9c72d..0000000 --- a/src/metis/kwayvolrefine.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * kwayvolrefine.c - * - * This file contains the driving routines for multilevel k-way refinement - * - * Started 7/28/97 - * George - * - * $Id: kwayvolrefine.c,v 1.2 2002/08/10 06:29:31 karypis Exp $ - */ - -#include - - -/************************************************************************* -* This function is the entry point of refinement -**************************************************************************/ -void RefineVolKWay(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, idxtype nparts, - float *tpwgts, float ubfactor) -{ - idxtype i, nlevels; - GraphType *ptr; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); - - /* Take care any non-contiguity */ - if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) { - ComputeVolKWayPartitionParams(ctrl, graph, nparts); - EliminateVolComponents(ctrl, graph, nparts, tpwgts, 1.25); - EliminateVolSubDomainEdges(ctrl, graph, nparts, tpwgts); - EliminateVolComponents(ctrl, graph, nparts, tpwgts, 1.25); - } - - - /* Determine how many levels are there */ - for (ptr=graph, nlevels=0; ptr!=orggraph; ptr=ptr->finer, nlevels++); - - /* Compute the parameters of the coarsest graph */ - ComputeVolKWayPartitionParams(ctrl, graph, nparts); - - for (i=0; ;i++) { - /*PrintSubDomainGraph(graph, nparts, graph->where);*/ - MALLOC_CHECK(NULL); - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->RefTmr)); - - if (2*i >= nlevels && !IsBalanced(graph->pwgts, nparts, tpwgts, 1.04*ubfactor)) { - ComputeVolKWayBalanceBoundary(ctrl, graph, nparts); - switch (ctrl->RType) { - case RTYPE_KWAYRANDOM: - Greedy_KWayVolBalance(ctrl, graph, nparts, tpwgts, ubfactor, 1); - break; - case RTYPE_KWAYRANDOM_MCONN: - Greedy_KWayVolBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 1); - break; - } - ComputeVolKWayBoundary(ctrl, graph, nparts); - } - - switch (ctrl->RType) { - case RTYPE_KWAYRANDOM: - Random_KWayVolRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); - break; - case RTYPE_KWAYRANDOM_MCONN: - Random_KWayVolRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); - break; - } - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); - - if (graph == orggraph) - break; - - graph = graph->finer; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); - ProjectVolKWayPartition(ctrl, graph, nparts); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); - } - - if (!IsBalanced(graph->pwgts, nparts, tpwgts, ubfactor)) { - ComputeVolKWayBalanceBoundary(ctrl, graph, nparts); - switch (ctrl->RType) { - case RTYPE_KWAYRANDOM: - Greedy_KWayVolBalance(ctrl, graph, nparts, tpwgts, ubfactor, 8); - Random_KWayVolRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); - break; - case RTYPE_KWAYRANDOM_MCONN: - Greedy_KWayVolBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 8); - Random_KWayVolRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); - break; - } - } - - EliminateVolComponents(ctrl, graph, nparts, tpwgts, ubfactor); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); -} - - - -/************************************************************************* -* This function allocates memory for k-way edge refinement -**************************************************************************/ -void AllocateVolKWayPartitionMemory(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype nvtxs; - - nvtxs = graph->nvtxs; - - graph->pwgts = idxmalloc(nparts, "AllocateVolKWayPartitionMemory: pwgts"); - graph->where = idxmalloc(nvtxs, "AllocateVolKWayPartitionMemory: pwgts"); - graph->bndptr = idxmalloc(nvtxs, "AllocateVolKWayPartitionMemory: pwgts"); - graph->bndind = idxmalloc(nvtxs, "AllocateVolKWayPartitionMemory: pwgts"); - - graph->vrinfo = (VRInfoType *)gk_malloc(nvtxs*sizeof(VRInfoType), "AllocateVolKWayPartitionMemory: vrinfo"); - -} - - - -/************************************************************************* -* This function computes the initial id/ed -**************************************************************************/ -void ComputeVolKWayPartitionParams(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, ii, j, k, kk, l, nvtxs, nbnd, mincut, minvol, me, other, pid; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *pwgts, *where; - VRInfoType *rinfo, *myrinfo, *orinfo; - VEDegreeType *myedegrees, *oedegrees; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - pwgts = idxset(nparts, 0, graph->pwgts); - rinfo = graph->vrinfo; - - - /*------------------------------------------------------------ - / Compute now the id/ed degrees - /------------------------------------------------------------*/ - ctrl->wspace.cdegree = 0; - mincut = 0; - for (i=0; iid = myrinfo->ed = myrinfo->nid = myrinfo->ndegrees = 0; - myrinfo->edegrees = NULL; - - for (j=xadj[i]; jid += adjwgt[j]; - myrinfo->nid++; - } - } - myrinfo->ed = graph->adjwgtsum[i] - myrinfo->id; - - mincut += myrinfo->ed; - - /* Time to compute the particular external degrees */ - if (myrinfo->ed > 0) { - myedegrees = myrinfo->edegrees = ctrl->wspace.vedegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[i+1]-xadj[i]; - - for (j=xadj[i]; jndegrees; k++) { - if (myedegrees[k].pid == other) { - myedegrees[k].ed += adjwgt[j]; - myedegrees[k].ned++; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].gv = 0; - myedegrees[myrinfo->ndegrees].pid = other; - myedegrees[myrinfo->ndegrees].ed = adjwgt[j]; - myedegrees[myrinfo->ndegrees++].ned = 1; - } - } - } - - ASSERT(myrinfo->ndegrees <= xadj[i+1]-xadj[i]); - } - } - graph->mincut = mincut/2; - - - ComputeKWayVolGains(ctrl, graph, nparts); - -} - - - -/************************************************************************* -* This function computes the initial id/ed -**************************************************************************/ -void ComputeKWayVolGains(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, ii, j, k, kk, l, nvtxs, me, other, pid, myndegrees; - idxtype *xadj, *vsize, *adjncy, *adjwgt, *where, *bndind, *bndptr, *ophtable; - VRInfoType *rinfo, *myrinfo, *orinfo; - VEDegreeType *myedegrees, *oedegrees; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vsize = graph->vsize; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - rinfo = graph->vrinfo; - - ophtable = idxset(nparts, -1, idxwspacemalloc(ctrl, nparts)); - - /*------------------------------------------------------------ - / Compute now the iv/ev degrees - /------------------------------------------------------------*/ - graph->minvol = graph->nbnd = 0; - for (i=0; igv = -MAXIDX; - - if (myrinfo->ndegrees > 0) { - me = where[i]; - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - graph->minvol += myndegrees*vsize[i]; - - for (j=xadj[i]; jedegrees; - - for (k=0; kndegrees; k++) - ophtable[oedegrees[k].pid] = k; - ophtable[other] = 1; /* this is to simplify coding */ - - if (me == other) { - /* Find which domains 'i' is connected and 'ii' is not and update their gain */ - for (k=0; kndegrees; kk++) - ophtable[oedegrees[kk].pid] = -1; - ophtable[other] = -1; - } - - /* Compute the max vgain */ - for (k=0; k myrinfo->gv) - myrinfo->gv = myedegrees[k].gv; - } - } - - if (myrinfo->ed > 0 && myrinfo->id == 0) - myrinfo->gv += vsize[i]; - - if (myrinfo->gv >= 0 || myrinfo->ed-myrinfo->id >= 0) - BNDInsert(graph->nbnd, bndind, bndptr, i); - } - - idxwspacefree(ctrl, nparts); - -} - - - -/************************************************************************* -* This function projects a partition, and at the same time computes the -* parameters for refinement. -**************************************************************************/ -void ProjectVolKWayPartition(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, j, k, nvtxs, me, other, istart, iend, ndegrees; - idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum; - idxtype *cmap, *where; - idxtype *cwhere; - GraphType *cgraph; - VRInfoType *crinfo, *rinfo, *myrinfo; - VEDegreeType *myedegrees; - idxtype *htable; - - cgraph = graph->coarser; - cwhere = cgraph->where; - crinfo = cgraph->vrinfo; - - nvtxs = graph->nvtxs; - cmap = graph->cmap; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - - AllocateVolKWayPartitionMemory(ctrl, graph, nparts); - where = graph->where; - rinfo = graph->vrinfo; - - /* Go through and project partition and compute id/ed for the nodes */ - for (i=0; iwspace.cdegree = 0; - for (i=0; iid = myrinfo->ed = myrinfo->nid = myrinfo->ndegrees = 0; - myrinfo->edegrees = NULL; - - myrinfo->id = adjwgtsum[i]; - myrinfo->nid = xadj[i+1]-xadj[i]; - - if (cmap[i] > 0) { /* If it is an interface node. Note cmap[i] = crinfo[cmap[i]].ed */ - istart = xadj[i]; - iend = xadj[i+1]; - - myedegrees = myrinfo->edegrees = ctrl->wspace.vedegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += iend-istart; - - ndegrees = 0; - for (j=istart; jed += adjwgt[j]; - myrinfo->nid--; - if ((k = htable[other]) == -1) { - htable[other] = ndegrees; - myedegrees[ndegrees].gv = 0; - myedegrees[ndegrees].pid = other; - myedegrees[ndegrees].ed = adjwgt[j]; - myedegrees[ndegrees++].ned = 1; - } - else { - myedegrees[k].ed += adjwgt[j]; - myedegrees[k].ned++; - } - } - } - myrinfo->id -= myrinfo->ed; - - /* Remove space for edegrees if it was interior */ - if (myrinfo->ed == 0) { - myrinfo->edegrees = NULL; - ctrl->wspace.cdegree -= iend-istart; - } - else { - myrinfo->ndegrees = ndegrees; - - for (j=0; jpwgts, graph->pwgts); - graph->mincut = cgraph->mincut; - - FreeGraph(graph->coarser, 1); - graph->coarser = NULL; - - idxwspacefree(ctrl, nparts); - -} - - - -/************************************************************************* -* This function computes the boundary definition for balancing -**************************************************************************/ -void ComputeVolKWayBoundary(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, nvtxs, nbnd; - idxtype *bndind, *bndptr; - - nvtxs = graph->nvtxs; - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - - - /*------------------------------------------------------------ - / Compute the new boundary - /------------------------------------------------------------*/ - nbnd = 0; - for (i=0; ivrinfo[i].gv >=0 || graph->vrinfo[i].ed-graph->vrinfo[i].id >= 0) - BNDInsert(nbnd, bndind, bndptr, i); - } - - graph->nbnd = nbnd; -} - -/************************************************************************* -* This function computes the boundary definition for balancing -**************************************************************************/ -void ComputeVolKWayBalanceBoundary(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, nvtxs, nbnd; - idxtype *bndind, *bndptr; - - nvtxs = graph->nvtxs; - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - - - /*------------------------------------------------------------ - / Compute the new boundary - /------------------------------------------------------------*/ - nbnd = 0; - for (i=0; ivrinfo[i].ed > 0) - BNDInsert(nbnd, bndind, bndptr, i); - } - - graph->nbnd = nbnd; -} - diff --git a/src/metis/libmetis/CMakeLists.txt b/src/metis/libmetis/CMakeLists.txt new file mode 100644 index 0000000..120e94d --- /dev/null +++ b/src/metis/libmetis/CMakeLists.txt @@ -0,0 +1,16 @@ +# Add this directory for internal users. +include_directories(.) +# Find sources. +file(GLOB metis_sources *.c) +# Build libmetis. +add_library(metis ${METIS_LIBRARY_TYPE} ${GKlib_sources} ${metis_sources}) +if(UNIX) + target_link_libraries(metis m) +endif() + +if(METIS_INSTALL) + install(TARGETS metis + LIBRARY DESTINATION lib + RUNTIME DESTINATION lib + ARCHIVE DESTINATION lib) +endif() diff --git a/src/metis/libmetis/auxapi.c b/src/metis/libmetis/auxapi.c new file mode 100644 index 0000000..8976b4b --- /dev/null +++ b/src/metis/libmetis/auxapi.c @@ -0,0 +1,43 @@ +/** +\file +\brief This file contains various helper API routines for using METIS. + +\date Started 5/12/2011 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version\verbatim $Id: auxapi.c 10409 2011-06-25 16:58:34Z karypis $ \endverbatim +*/ + + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function free memory that was allocated by METIS and retuned + to the application. + + \param ptr points to the memory that was previously allocated by + METIS. +*/ +/*************************************************************************/ +int METIS_Free(void *ptr) +{ + if (ptr != NULL) free(ptr); + return METIS_OK; +} + + +/*************************************************************************/ +/*! This function sets the default values for the options. + + \param options points to an array of size at least METIS_NOPTIONS. +*/ +/*************************************************************************/ +int METIS_SetDefaultOptions(idx_t *options) +{ + iset(METIS_NOPTIONS, -1, options); + + return METIS_OK; +} + + diff --git a/src/metis/libmetis/balance.c b/src/metis/libmetis/balance.c new file mode 100644 index 0000000..3fb0e6e --- /dev/null +++ b/src/metis/libmetis/balance.c @@ -0,0 +1,498 @@ +/*! +\file +\brief Functions for the edge-based balancing + +\date Started 7/23/97 +\author George +\author Copyright 1997-2011, Regents of the University of Minnesota +\version\verbatim $Id: balance.c 10187 2011-06-13 13:46:57Z karypis $ \endverbatim +*/ + +#include "metislib.h" + +/************************************************************************* +* This function is the entry poidx_t of the bisection balancing algorithms. +**************************************************************************/ +void Balance2Way(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts) +{ + if (ComputeLoadImbalanceDiff(graph, 2, ctrl->pijbm, ctrl->ubfactors) <= 0) + return; + + if (graph->ncon == 1) { + /* return right away if the balance is OK */ + if (iabs(ntpwgts[0]*graph->tvwgt[0]-graph->pwgts[0]) < 3*graph->tvwgt[0]/graph->nvtxs) + return; + + if (graph->nbnd > 0) + Bnd2WayBalance(ctrl, graph, ntpwgts); + else + General2WayBalance(ctrl, graph, ntpwgts); + } + else { + McGeneral2WayBalance(ctrl, graph, ntpwgts); + } +} + + +/************************************************************************* +* This function balances two partitions by moving boundary nodes +* from the domain that is overweight to the one that is underweight. +**************************************************************************/ +void Bnd2WayBalance(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts) +{ + idx_t i, ii, j, k, kwgt, nvtxs, nbnd, nswaps, from, to, pass, me, tmp; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind, *pwgts; + idx_t *moved, *perm; + rpq_t *queue; + idx_t higain, mincut, mindiff; + idx_t tpwgts[2]; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + where = graph->where; + id = graph->id; + ed = graph->ed; + pwgts = graph->pwgts; + bndptr = graph->bndptr; + bndind = graph->bndind; + + moved = iwspacemalloc(ctrl, nvtxs); + perm = iwspacemalloc(ctrl, nvtxs); + + /* Determine from which domain you will be moving data */ + tpwgts[0] = graph->tvwgt[0]*ntpwgts[0]; + tpwgts[1] = graph->tvwgt[0] - tpwgts[0]; + mindiff = iabs(tpwgts[0]-pwgts[0]); + from = (pwgts[0] < tpwgts[0] ? 1 : 0); + to = (from+1)%2; + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("Partitions: [%6"PRIDX" %6"PRIDX"] T[%6"PRIDX" %6"PRIDX"], Nv-Nb[%6"PRIDX" %6"PRIDX"]. ICut: %6"PRIDX" [B]\n", + pwgts[0], pwgts[1], tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, + graph->mincut)); + + queue = rpqCreate(nvtxs); + + iset(nvtxs, -1, moved); + + ASSERT(ComputeCut(graph, where) == graph->mincut); + ASSERT(CheckBnd(graph)); + + /* Insert the boundary nodes of the proper partition whose size is OK in the priority queue */ + nbnd = graph->nbnd; + irandArrayPermute(nbnd, perm, nbnd/5, 1); + for (ii=0; ii 0 || id[bndind[i]] == 0); + ASSERT(bndptr[bndind[i]] != -1); + if (where[bndind[i]] == from && vwgt[bndind[i]] <= mindiff) + rpqInsert(queue, bndind[i], ed[bndind[i]]-id[bndind[i]]); + } + + mincut = graph->mincut; + for (nswaps=0; nswaps tpwgts[to]) + break; + + mincut -= (ed[higain]-id[higain]); + INC_DEC(pwgts[to], pwgts[from], vwgt[higain]); + + where[higain] = to; + moved[higain] = nswaps; + + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, + printf("Moved %6"PRIDX" from %"PRIDX". [%3"PRIDX" %3"PRIDX"] %5"PRIDX" [%4"PRIDX" %4"PRIDX"]\n", higain, from, ed[higain]-id[higain], vwgt[higain], mincut, pwgts[0], pwgts[1])); + + /************************************************************** + * Update the id[i]/ed[i] values of the affected nodes + ***************************************************************/ + SWAP(id[higain], ed[higain], tmp); + if (ed[higain] == 0 && xadj[higain] < xadj[higain+1]) + BNDDelete(nbnd, bndind, bndptr, higain); + + for (j=xadj[higain]; j 0) { /* It will now become a boundary vertex */ + BNDInsert(nbnd, bndind, bndptr, k); + if (moved[k] == -1 && where[k] == from && vwgt[k] <= mindiff) + rpqInsert(queue, k, ed[k]-id[k]); + } + } + } + } + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("\tMinimum cut: %6"PRIDX", PWGTS: [%6"PRIDX" %6"PRIDX"], NBND: %6"PRIDX"\n", mincut, pwgts[0], pwgts[1], nbnd)); + + graph->mincut = mincut; + graph->nbnd = nbnd; + + rpqDestroy(queue); + + WCOREPOP; +} + + +/************************************************************************* +* This function balances two partitions by moving the highest gain +* (including negative gain) vertices to the other domain. +* It is used only when tha unbalance is due to non contigous +* subdomains. That is, the are no boundary vertices. +* It moves vertices from the domain that is overweight to the one that +* is underweight. +**************************************************************************/ +void General2WayBalance(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts) +{ + idx_t i, ii, j, k, kwgt, nvtxs, nbnd, nswaps, from, to, pass, me, tmp; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind, *pwgts; + idx_t *moved, *perm; + rpq_t *queue; + idx_t higain, mincut, mindiff; + idx_t tpwgts[2]; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + where = graph->where; + id = graph->id; + ed = graph->ed; + pwgts = graph->pwgts; + bndptr = graph->bndptr; + bndind = graph->bndind; + + moved = iwspacemalloc(ctrl, nvtxs); + perm = iwspacemalloc(ctrl, nvtxs); + + /* Determine from which domain you will be moving data */ + tpwgts[0] = graph->tvwgt[0]*ntpwgts[0]; + tpwgts[1] = graph->tvwgt[0] - tpwgts[0]; + mindiff = iabs(tpwgts[0]-pwgts[0]); + from = (pwgts[0] < tpwgts[0] ? 1 : 0); + to = (from+1)%2; + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("Partitions: [%6"PRIDX" %6"PRIDX"] T[%6"PRIDX" %6"PRIDX"], Nv-Nb[%6"PRIDX" %6"PRIDX"]. ICut: %6"PRIDX" [B]\n", + pwgts[0], pwgts[1], tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); + + queue = rpqCreate(nvtxs); + + iset(nvtxs, -1, moved); + + ASSERT(ComputeCut(graph, where) == graph->mincut); + ASSERT(CheckBnd(graph)); + + /* Insert the nodes of the proper partition whose size is OK in the priority queue */ + irandArrayPermute(nvtxs, perm, nvtxs/5, 1); + for (ii=0; iimincut; + nbnd = graph->nbnd; + for (nswaps=0; nswaps tpwgts[to]) + break; + + mincut -= (ed[higain]-id[higain]); + INC_DEC(pwgts[to], pwgts[from], vwgt[higain]); + + where[higain] = to; + moved[higain] = nswaps; + + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, + printf("Moved %6"PRIDX" from %"PRIDX". [%3"PRIDX" %3"PRIDX"] %5"PRIDX" [%4"PRIDX" %4"PRIDX"]\n", higain, from, ed[higain]-id[higain], vwgt[higain], mincut, pwgts[0], pwgts[1])); + + /************************************************************** + * Update the id[i]/ed[i] values of the affected nodes + ***************************************************************/ + SWAP(id[higain], ed[higain], tmp); + if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) + BNDDelete(nbnd, bndind, bndptr, higain); + if (ed[higain] > 0 && bndptr[higain] == -1) + BNDInsert(nbnd, bndind, bndptr, higain); + + for (j=xadj[higain]; j 0 && bndptr[k] == -1) + BNDInsert(nbnd, bndind, bndptr, k); + } + } + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("\tMinimum cut: %6"PRIDX", PWGTS: [%6"PRIDX" %6"PRIDX"], NBND: %6"PRIDX"\n", mincut, pwgts[0], pwgts[1], nbnd)); + + graph->mincut = mincut; + graph->nbnd = nbnd; + + rpqDestroy(queue); + + WCOREPOP; +} + + +/************************************************************************* +* This function performs an edge-based FM refinement +**************************************************************************/ +void McGeneral2WayBalance(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts) +{ + idx_t i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, + me, limit, tmp, cnum; + idx_t *xadj, *adjncy, *vwgt, *adjwgt, *where, *pwgts, *id, *ed, *bndptr, *bndind; + idx_t *moved, *swaps, *perm, *qnum, *qsizes; + idx_t higain, mincut, newcut, mincutorder; + real_t *invtvwgt, *minbalv, *newbalv, minbal, newbal; + rpq_t **queues; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + invtvwgt = graph->invtvwgt; + where = graph->where; + id = graph->id; + ed = graph->ed; + pwgts = graph->pwgts; + bndptr = graph->bndptr; + bndind = graph->bndind; + + moved = iwspacemalloc(ctrl, nvtxs); + swaps = iwspacemalloc(ctrl, nvtxs); + perm = iwspacemalloc(ctrl, nvtxs); + qnum = iwspacemalloc(ctrl, nvtxs); + newbalv = rwspacemalloc(ctrl, ncon); + minbalv = rwspacemalloc(ctrl, ncon); + qsizes = iwspacemalloc(ctrl, 2*ncon); + + limit = gk_min(gk_max(0.01*nvtxs, 15), 100); + + /* Initialize the queues */ + queues = (rpq_t **)wspacemalloc(ctrl, 2*ncon*sizeof(rpq_t *)); + for (i=0; i<2*ncon; i++) { + queues[i] = rpqCreate(nvtxs); + qsizes[i] = 0; + } + + for (i=0; i qsizes[2*j+from] && + vwgt[i*ncon+qnum[i]]*invtvwgt[qnum[i]] < 1.3*vwgt[i*ncon+j]*invtvwgt[j]) { + qsizes[2*qnum[i]+from]--; + qsizes[2*j+from]++; + qnum[i] = j; + } + } + } + } + } + + + minbal = ComputeLoadImbalanceDiffVec(graph, 2, ctrl->pijbm, ctrl->ubfactors, minbalv); + ASSERT(minbal > 0.0); + + newcut = mincut = graph->mincut; + mincutorder = -1; + + if (ctrl->dbglvl&METIS_DBG_REFINE) { + printf("Parts: ["); + for (l=0; lnvtxs, graph->nbnd, graph->mincut, minbal); + } + + iset(nvtxs, -1, moved); + + ASSERT(ComputeCut(graph, where) == graph->mincut); + ASSERT(CheckBnd(graph)); + + /* Insert all nodes in the priority queues */ + nbnd = graph->nbnd; + irandArrayPermute(nvtxs, perm, nvtxs/10, 1); + for (ii=0; iipijbm, ctrl->ubfactors, queues, &from, &cnum); + to = (from+1)%2; + + if (from == -1 || (higain = rpqGetTop(queues[2*cnum+from])) == -1) + break; + + newcut -= (ed[higain]-id[higain]); + + iaxpy(ncon, 1, vwgt+higain*ncon, 1, pwgts+to*ncon, 1); + iaxpy(ncon, -1, vwgt+higain*ncon, 1, pwgts+from*ncon, 1); + newbal = ComputeLoadImbalanceDiffVec(graph, 2, ctrl->pijbm, ctrl->ubfactors, newbalv); + + if (newbal < minbal || (newbal == minbal && + (newcut < mincut || + (newcut == mincut && BetterBalance2Way(ncon, minbalv, newbalv))))) { + mincut = newcut; + minbal = newbal; + mincutorder = nswaps; + rcopy(ncon, newbalv, minbalv); + } + else if (nswaps-mincutorder > limit) { /* We hit the limit, undo last move */ + newcut += (ed[higain]-id[higain]); + iaxpy(ncon, 1, vwgt+higain*ncon, 1, pwgts+from*ncon, 1); + iaxpy(ncon, -1, vwgt+higain*ncon, 1, pwgts+to*ncon, 1); + break; + } + + where[higain] = to; + moved[higain] = nswaps; + swaps[nswaps] = higain; + + if (ctrl->dbglvl&METIS_DBG_MOVEINFO) { + printf("Moved %6"PRIDX" from %"PRIDX"(%"PRIDX"). Gain: %5"PRIDX", " + "Cut: %5"PRIDX", NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut); + for (l=0; l 0 && bndptr[higain] == -1) + BNDInsert(nbnd, bndind, bndptr, higain); + + for (j=xadj[higain]; j 0 && bndptr[k] == -1) + BNDInsert(nbnd, bndind, bndptr, k); + } + } + + + + /**************************************************************** + * Roll back computations + *****************************************************************/ + for (nswaps--; nswaps>mincutorder; nswaps--) { + higain = swaps[nswaps]; + + to = where[higain] = (where[higain]+1)%2; + SWAP(id[higain], ed[higain], tmp); + if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) + BNDDelete(nbnd, bndind, bndptr, higain); + else if (ed[higain] > 0 && bndptr[higain] == -1) + BNDInsert(nbnd, bndind, bndptr, higain); + + iaxpy(ncon, 1, vwgt+higain*ncon, 1, pwgts+to*ncon, 1); + iaxpy(ncon, -1, vwgt+higain*ncon, 1, pwgts+((to+1)%2)*ncon, 1); + for (j=xadj[higain]; j 0) + BNDInsert(nbnd, bndind, bndptr, k); + } + } + + if (ctrl->dbglvl&METIS_DBG_REFINE) { + printf("\tMincut: %6"PRIDX" at %5"PRIDX", NBND: %6"PRIDX", NPwgts: [", + mincut, mincutorder, nbnd); + for (l=0; lpijbm)); + } + + graph->mincut = mincut; + graph->nbnd = nbnd; + + + for (i=0; i<2*ncon; i++) + rpqDestroy(queues[i]); + + WCOREPOP; +} + diff --git a/src/metis/bucketsort.c b/src/metis/libmetis/bucketsort.c similarity index 67% rename from src/metis/bucketsort.c rename to src/metis/libmetis/bucketsort.c index ddb2c3a..e126d02 100644 --- a/src/metis/bucketsort.c +++ b/src/metis/libmetis/bucketsort.c @@ -11,21 +11,24 @@ * */ -#include +#include "metislib.h" /************************************************************************* * This function uses simple counting sort to return a permutation array -* corresponding to the sorted order. The keys are agk_fsumed to start from +* corresponding to the sorted order. The keys are arsumed to start from * 0 and they are positive. This sorting is used during matching. **************************************************************************/ -void BucketSortKeysInc(idxtype n, idxtype max, idxtype *keys, idxtype *tperm, idxtype *perm) +void BucketSortKeysInc(ctrl_t *ctrl, idx_t n, idx_t max, idx_t *keys, + idx_t *tperm, idx_t *perm) { - idxtype i, ii; - idxtype *counts; + idx_t i, ii; + idx_t *counts; - counts = idxsmalloc(max+2, 0, "BucketSortKeysInc: counts"); + WCOREPUSH; + + counts = iset(max+2, 0, iwspacemalloc(ctrl, max+2)); for (i=0; invtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + ASSERT(adjwgt != NULL); + + htable = ismalloc(nvtxs, 0, "htable"); + + minedge = maxedge = adjncy[0]; + minewgt = maxewgt = adjwgt[0]; + + for (i=0; i maxedge) ? k : maxedge; + minewgt = (adjwgt[j] < minewgt) ? adjwgt[j] : minewgt; + maxewgt = (adjwgt[j] > maxewgt) ? adjwgt[j] : maxewgt; + + if (i == k) { + if (verbose) + printf("Vertex %"PRIDX" contains a self-loop " + "(i.e., diagonal entry in the matrix)!\n", i+numflag); + err++; + } + else { + for (l=xadj[k]; l 0 && verbose) { + printf("A total of %"PRIDX" errors exist in the input file. " + "Correct them, and run again!\n", err); + } + + gk_free((void **)&htable, LTERM); + + return (err == 0 ? 1 : 0); +} + + +/*************************************************************************/ +/*! This function performs a quick check of the weights of the graph */ +/*************************************************************************/ +int CheckInputGraphWeights(idx_t nvtxs, idx_t ncon, idx_t *xadj, idx_t *adjncy, + idx_t *vwgt, idx_t *vsize, idx_t *adjwgt) +{ + idx_t i; + + if (ncon <= 0) { + printf("Input Error: ncon must be >= 1.\n"); + return 0; + } + + if (vwgt) { + for (i=ncon*nvtxs; i>=0; i--) { + if (vwgt[i] < 0) { + printf("Input Error: negative vertex weight(s).\n"); + return 0; + } + } + } + if (vsize) { + for (i=nvtxs; i>=0; i--) { + if (vsize[i] < 0) { + printf("Input Error: negative vertex sizes(s).\n"); + return 0; + } + } + } + if (adjwgt) { + for (i=xadj[nvtxs]-1; i>=0; i--) { + if (adjwgt[i] < 0) { + printf("Input Error: non-positive edge weight(s).\n"); + return 0; + } + } + } + + return 1; +} + + +/*************************************************************************/ +/*! This function creates a graph whose topology is consistent with + Metis' requirements that: + - There are no self-edges. + - It is undirected; i.e., (u,v) and (v,u) should be present and of the + same weight. + - The adjacency list should not contain multiple edges to the same + other vertex. + + Any of the above errors are fixed by performing the following operations: + - Self-edges are removed. + - The undirected graph is formed by the union of edges. + - One of the duplicate edges is selected. + + The routine does not change the provided vertex weights. +*/ +/*************************************************************************/ +graph_t *FixGraph(graph_t *graph) +{ + idx_t i, j, k, l, nvtxs, nedges; + idx_t *xadj, *adjncy, *adjwgt; + idx_t *nxadj, *nadjncy, *nadjwgt; + graph_t *ngraph; + uvw_t *edges; + + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + ASSERT(adjwgt != NULL); + + ngraph = CreateGraph(); + + ngraph->nvtxs = nvtxs; + + /* deal with vertex weights/sizes */ + ngraph->ncon = graph->ncon; + ngraph->vwgt = icopy(nvtxs*graph->ncon, graph->vwgt, + imalloc(nvtxs*graph->ncon, "FixGraph: vwgt")); + + ngraph->vsize = ismalloc(nvtxs, 1, "FixGraph: vsize"); + if (graph->vsize) + icopy(nvtxs, graph->vsize, ngraph->vsize); + + /* fix graph by sorting the "superset" of edges */ + edges = (uvw_t *)gk_malloc(sizeof(uvw_t)*2*xadj[nvtxs], "FixGraph: edges"); + + for (nedges=0, i=0; i adjncy[j]) { + edges[nedges].u = adjncy[j]; + edges[nedges].v = i; + edges[nedges].w = adjwgt[j]; + nedges++; + } + } + } + + uvwsorti(nedges, edges); + + + /* keep the unique subset */ + for (k=0, i=1; ixadj = ismalloc(nvtxs+1, 0, "FixGraph: nxadj"); + nadjncy = ngraph->adjncy = imalloc(2*nedges, "FixGraph: nadjncy"); + nadjwgt = ngraph->adjwgt = imalloc(2*nedges, "FixGraph: nadjwgt"); + + /* create the adjacency list of the fixed graph from the upper-triangular + part of the adjacency matrix */ + for (k=0; kdbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->CoarsenTmr)); + + /* determine if the weights on the edges are all the same */ + for (eqewgts=1, i=1; inedges; i++) { + if (graph->adjwgt[0] != graph->adjwgt[i]) { + eqewgts = 0; + break; + } + } + + /* set the maximum allowed coarsest vertex weight */ + for (i=0; incon; i++) + ctrl->maxvwgt[i] = 1.5*graph->tvwgt[i]/ctrl->CoarsenTo; + + do { + IFSET(ctrl->dbglvl, METIS_DBG_COARSEN, PrintCGraphStats(ctrl, graph)); + + /* allocate memory for cmap, if it has not already been done due to + multiple cuts */ + if (graph->cmap == NULL) + graph->cmap = imalloc(graph->nvtxs, "CoarsenGraph: graph->cmap"); + + /* determine which matching scheme you will use */ + switch (ctrl->ctype) { + case METIS_CTYPE_RM: + Match_RM(ctrl, graph); + break; + case METIS_CTYPE_SHEM: + if (eqewgts || graph->nedges == 0) + Match_RM(ctrl, graph); + else + Match_SHEM(ctrl, graph); + break; + default: + gk_errexit(SIGERR, "Unknown ctype: %d\n", ctrl->ctype); + } + + graph = graph->coarser; + eqewgts = 0; + level++; + + ASSERT(CheckGraph(graph, 0, 1)); + + } while (graph->nvtxs > ctrl->CoarsenTo && + graph->nvtxs < COARSEN_FRACTION*graph->finer->nvtxs && + graph->nedges > graph->nvtxs/2); + + IFSET(ctrl->dbglvl, METIS_DBG_COARSEN, PrintCGraphStats(ctrl, graph)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->CoarsenTmr)); + + return graph; +} + + +/*************************************************************************/ +/*! This function takes a graph and creates a sequence of nlevels coarser + graphs, where nlevels is an input parameter. + */ +/*************************************************************************/ +graph_t *CoarsenGraphNlevels(ctrl_t *ctrl, graph_t *graph, idx_t nlevels) +{ + idx_t i, eqewgts, level; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->CoarsenTmr)); + + /* determine if the weights on the edges are all the same */ + for (eqewgts=1, i=1; inedges; i++) { + if (graph->adjwgt[0] != graph->adjwgt[i]) { + eqewgts = 0; + break; + } + } + + /* set the maximum allowed coarsest vertex weight */ + for (i=0; incon; i++) + ctrl->maxvwgt[i] = 1.5*graph->tvwgt[i]/ctrl->CoarsenTo; + + for (level=0; leveldbglvl, METIS_DBG_COARSEN, PrintCGraphStats(ctrl, graph)); + + /* allocate memory for cmap, if it has not already been done due to + multiple cuts */ + if (graph->cmap == NULL) + graph->cmap = imalloc(graph->nvtxs, "CoarsenGraph: graph->cmap"); + + /* determine which matching scheme you will use */ + switch (ctrl->ctype) { + case METIS_CTYPE_RM: + Match_RM(ctrl, graph); + break; + case METIS_CTYPE_SHEM: + if (eqewgts || graph->nedges == 0) + Match_RM(ctrl, graph); + else + Match_SHEM(ctrl, graph); + break; + default: + gk_errexit(SIGERR, "Unknown ctype: %d\n", ctrl->ctype); + } + + graph = graph->coarser; + eqewgts = 0; + + ASSERT(CheckGraph(graph, 0, 1)); + + if (graph->nvtxs < ctrl->CoarsenTo || + graph->nvtxs > COARSEN_FRACTION*graph->finer->nvtxs || + graph->nedges < graph->nvtxs/2) + break; + } + + IFSET(ctrl->dbglvl, METIS_DBG_COARSEN, PrintCGraphStats(ctrl, graph)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->CoarsenTmr)); + + return graph; +} + + +/*************************************************************************/ +/*! This function finds a matching by randomly selecting one of the + unmatched adjacent vertices. + */ +/**************************************************************************/ +idx_t Match_RM(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, pi, ii, j, jj, jjinc, k, nvtxs, ncon, cnvtxs, maxidx, last_unmatched; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *maxvwgt; + idx_t *match, *cmap, *perm; + size_t nunmatched=0; + + WCOREPUSH; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + cmap = graph->cmap; + + maxvwgt = ctrl->maxvwgt; + + match = iset(nvtxs, UNMATCHED, iwspacemalloc(ctrl, nvtxs)); + perm = iwspacemalloc(ctrl, nvtxs); + + irandArrayPermute(nvtxs, perm, nvtxs/8, 1); + + for (cnvtxs=0, last_unmatched=0, pi=0; pimaxvwgt requirements */ + if (xadj[i] == xadj[i+1]) { + last_unmatched = gk_max(pi, last_unmatched)+1; + for (; last_unmatchedno2hop && nunmatched > UNMATCHEDFOR2HOP*nvtxs) + cnvtxs = Match_2Hop(ctrl, graph, perm, match, cnvtxs, nunmatched); + + + /* match the final unmatched vertices with themselves and reorder the vertices + of the coarse graph for memory-friendly contraction */ + for (cnvtxs=0, i=0; idbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); + + CreateCoarseGraph(ctrl, graph, cnvtxs, match); + + WCOREPOP; + + return cnvtxs; +} + + +/**************************************************************************/ +/*! This function finds a matching using the HEM heuristic. The vertices + are visited based on increasing degree to ensure that all vertices are + given a chance to match with something. + */ +/**************************************************************************/ +idx_t Match_SHEM(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, pi, ii, j, jj, jjinc, k, nvtxs, ncon, cnvtxs, maxidx, maxwgt, + last_unmatched, avgdegree; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *maxvwgt; + idx_t *match, *cmap, *degrees, *perm, *tperm; + size_t nunmatched=0; + + WCOREPUSH; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + cmap = graph->cmap; + + maxvwgt = ctrl->maxvwgt; + + match = iset(nvtxs, UNMATCHED, iwspacemalloc(ctrl, nvtxs)); + perm = iwspacemalloc(ctrl, nvtxs); + tperm = iwspacemalloc(ctrl, nvtxs); + degrees = iwspacemalloc(ctrl, nvtxs); + + irandArrayPermute(nvtxs, tperm, nvtxs/8, 1); + + avgdegree = 0.7*(xadj[nvtxs]/nvtxs); + for (i=0; i avgdegree ? avgdegree : xadj[i+1]-xadj[i]); + BucketSortKeysInc(ctrl, nvtxs, avgdegree, degrees, tperm, perm); + + for (cnvtxs=0, last_unmatched=0, pi=0; pimaxvwgt requirements */ + if (xadj[i] == xadj[i+1]) { + last_unmatched = gk_max(pi, last_unmatched)+1; + for (; last_unmatchedinvtvwgt, vwgt+i*ncon, + vwgt+maxidx*ncon, vwgt+k*ncon)))) { + maxidx = k; + maxwgt = adjwgt[j]; + } + } + + /* If it did not match, record for a 2-hop matching. */ + if (maxidx == i && ivecaxpylez(ncon, 2, vwgt+i*ncon, vwgt+i*ncon, maxvwgt)) { + nunmatched++; + maxidx = UNMATCHED; + } + } + } + } + + if (maxidx != UNMATCHED) { + cmap[i] = cmap[maxidx] = cnvtxs++; + match[i] = maxidx; + match[maxidx] = i; + } + } + } + + //printf("nunmatched: %zu\n", nunmatched); + + /* see if a 2-hop matching is required/allowed */ + if (!ctrl->no2hop && nunmatched > UNMATCHEDFOR2HOP*nvtxs) + cnvtxs = Match_2Hop(ctrl, graph, perm, match, cnvtxs, nunmatched); + + + /* match the final unmatched vertices with themselves and reorder the vertices + of the coarse graph for memory-friendly contraction */ + for (cnvtxs=0, i=0; idbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); + + CreateCoarseGraph(ctrl, graph, cnvtxs, match); + + WCOREPOP; + + return cnvtxs; +} + + +/*************************************************************************/ +/*! This function matches the unmatched vertices using a 2-hop matching + that involves vertices that are two hops away from each other. */ +/**************************************************************************/ +idx_t Match_2Hop(ctrl_t *ctrl, graph_t *graph, idx_t *perm, idx_t *match, + idx_t cnvtxs, size_t nunmatched) +{ + + cnvtxs = Match_2HopAny(ctrl, graph, perm, match, cnvtxs, &nunmatched, 2); + cnvtxs = Match_2HopAll(ctrl, graph, perm, match, cnvtxs, &nunmatched, 64); + if (nunmatched > 1.5*UNMATCHEDFOR2HOP*graph->nvtxs) + cnvtxs = Match_2HopAny(ctrl, graph, perm, match, cnvtxs, &nunmatched, 3); + if (nunmatched > 2.0*UNMATCHEDFOR2HOP*graph->nvtxs) + cnvtxs = Match_2HopAny(ctrl, graph, perm, match, cnvtxs, &nunmatched, graph->nvtxs); + + return cnvtxs; +} + + +/*************************************************************************/ +/*! This function matches the unmatched vertices whose degree is less than + maxdegree using a 2-hop matching that involves vertices that are two + hops away from each other. + The requirement of the 2-hop matching is a simple non-empty overlap + between the adjancency lists of the vertices. */ +/**************************************************************************/ +idx_t Match_2HopAny(ctrl_t *ctrl, graph_t *graph, idx_t *perm, idx_t *match, + idx_t cnvtxs, size_t *r_nunmatched, size_t maxdegree) +{ + idx_t i, pi, ii, j, jj, k, nvtxs; + idx_t *xadj, *adjncy, *colptr, *rowind; + idx_t *cmap; + size_t nunmatched; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->Aux3Tmr)); + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + cmap = graph->cmap; + + nunmatched = *r_nunmatched; + + /*IFSET(ctrl->dbglvl, METIS_DBG_COARSEN, printf("IN: nunmatched: %zu\t", * nunmatched)); */ + + /* create the inverted index */ + WCOREPUSH; + colptr = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs+1)); + for (i=0; ij; jj--) { + if (match[rowind[jj]] == UNMATCHED) { + cmap[rowind[j]] = cmap[rowind[jj]] = cnvtxs++; + match[rowind[j]] = rowind[jj]; + match[rowind[jj]] = rowind[j]; + nunmatched -= 2; + break; + } + } + } + } + } + WCOREPOP; + + /* IFSET(ctrl->dbglvl, METIS_DBG_COARSEN, printf("OUT: nunmatched: %zu\n", nunmatched)); */ + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->Aux3Tmr)); + + *r_nunmatched = nunmatched; + return cnvtxs; +} + + +/*************************************************************************/ +/*! This function matches the unmatched vertices whose degree is less than + maxdegree using a 2-hop matching that involves vertices that are two + hops away from each other. + The requirement of the 2-hop matching is that of identical adjacency + lists. + */ +/**************************************************************************/ +idx_t Match_2HopAll(ctrl_t *ctrl, graph_t *graph, idx_t *perm, idx_t *match, + idx_t cnvtxs, size_t *r_nunmatched, size_t maxdegree) +{ + idx_t i, pi, pk, ii, j, jj, k, nvtxs, mask, idegree; + idx_t *xadj, *adjncy; + idx_t *cmap, *mark; + ikv_t *keys; + size_t nunmatched, ncand; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->Aux3Tmr)); + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + cmap = graph->cmap; + + nunmatched = *r_nunmatched; + mask = IDX_MAX/maxdegree; + + /*IFSET(ctrl->dbglvl, METIS_DBG_COARSEN, printf("IN: nunmatched: %zu\t", nunmatched)); */ + + WCOREPUSH; + + /* collapse vertices with identical adjancency lists */ + keys = ikvwspacemalloc(ctrl, nunmatched); + for (ncand=0, pi=0; pi 1 && idegree < maxdegree) { + for (k=0, j=xadj[i]; jdbglvl, METIS_DBG_COARSEN, printf("OUT: ncand: %zu, nunmatched: %zu\n", ncand, nunmatched)); */ + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->Aux3Tmr)); + + *r_nunmatched = nunmatched; + return cnvtxs; +} + + +/*************************************************************************/ +/*! This function prints various stats for each graph during coarsening + */ +/*************************************************************************/ +void PrintCGraphStats(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i; + + printf("%10"PRIDX" %10"PRIDX" %10"PRIDX" [%"PRIDX"] [", + graph->nvtxs, graph->nedges, isum(graph->nedges, graph->adjwgt, 1), ctrl->CoarsenTo); + + for (i=0; incon; i++) + printf(" %8"PRIDX":%8"PRIDX, ctrl->maxvwgt[i], graph->tvwgt[i]); + printf(" ]\n"); +} + + +/*************************************************************************/ +/*! This function creates the coarser graph. It uses a simple hash-table + for identifying the adjacent vertices that get collapsed to the same + node. The hash-table can have conflicts, which are handled via a + linear scan. + */ +/*************************************************************************/ +void CreateCoarseGraph(ctrl_t *ctrl, graph_t *graph, idx_t cnvtxs, + idx_t *match) +{ + idx_t j, jj, k, kk, l, m, istart, iend, nvtxs, nedges, ncon, cnedges, + v, u, mask, dovsize; + idx_t *xadj, *vwgt, *vsize, *adjncy, *adjwgt; + idx_t *cmap, *htable; + idx_t *cxadj, *cvwgt, *cvsize, *cadjncy, *cadjwgt; + graph_t *cgraph; + + dovsize = (ctrl->objtype == METIS_OBJTYPE_VOL ? 1 : 0); + + /* Check if the mask-version of the code is a good choice */ + mask = HTLENGTH; + if (cnvtxs < 2*mask || graph->nedges/graph->nvtxs > mask/20) { + CreateCoarseGraphNoMask(ctrl, graph, cnvtxs, match); + return; + } + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + for (v=0; v (mask>>3)) { + CreateCoarseGraphNoMask(ctrl, graph, cnvtxs, match); + return; + } + } + + + WCOREPUSH; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->ContractTmr)); + + ncon = graph->ncon; + vwgt = graph->vwgt; + vsize = graph->vsize; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + cmap = graph->cmap; + + /* Initialize the coarser graph */ + cgraph = SetupCoarseGraph(graph, cnvtxs, dovsize); + cxadj = cgraph->xadj; + cvwgt = cgraph->vwgt; + cvsize = cgraph->vsize; + cadjncy = cgraph->adjncy; + cadjwgt = cgraph->adjwgt; + + htable = iset(gk_min(cnvtxs+1, mask+1), -1, iwspacemalloc(ctrl, mask+1)); + + cxadj[0] = cnvtxs = cnedges = 0; + for (v=0; v= 0 && cadjncy[jj] != cnvtxs) { + for (jj=0; jj= 0 && jj < nedges && cadjncy[jj] == cnvtxs) { + cadjncy[jj] = cadjncy[--nedges]; + cadjwgt[jj] = cadjwgt[nedges]; + } + } + + /* Zero out the htable */ + for (j=0; jnedges = cnedges; + + for (j=0; jtvwgt[j] = isum(cgraph->nvtxs, cgraph->vwgt+j, ncon); + cgraph->invtvwgt[j] = 1.0/(cgraph->tvwgt[j] > 0 ? cgraph->tvwgt[j] : 1); + } + + + ReAdjustMemory(ctrl, graph, cgraph); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->ContractTmr)); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function creates the coarser graph. It uses a full-size array + (htable) for identifying the adjacent vertices that get collapsed to + the same node. + */ +/*************************************************************************/ +void CreateCoarseGraphNoMask(ctrl_t *ctrl, graph_t *graph, idx_t cnvtxs, + idx_t *match) +{ + idx_t j, k, m, istart, iend, nvtxs, nedges, ncon, cnedges, v, u, dovsize; + idx_t *xadj, *vwgt, *vsize, *adjncy, *adjwgt; + idx_t *cmap, *htable; + idx_t *cxadj, *cvwgt, *cvsize, *cadjncy, *cadjwgt; + graph_t *cgraph; + + WCOREPUSH; + + dovsize = (ctrl->objtype == METIS_OBJTYPE_VOL ? 1 : 0); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->ContractTmr)); + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + vsize = graph->vsize; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + cmap = graph->cmap; + + + /* Initialize the coarser graph */ + cgraph = SetupCoarseGraph(graph, cnvtxs, dovsize); + cxadj = cgraph->xadj; + cvwgt = cgraph->vwgt; + cvsize = cgraph->vsize; + cadjncy = cgraph->adjncy; + cadjwgt = cgraph->adjwgt; + + htable = iset(cnvtxs, -1, iwspacemalloc(ctrl, cnvtxs)); + + cxadj[0] = cnvtxs = cnedges = 0; + for (v=0; vnedges = cnedges; + + for (j=0; jtvwgt[j] = isum(cgraph->nvtxs, cgraph->vwgt+j, ncon); + cgraph->invtvwgt[j] = 1.0/(cgraph->tvwgt[j] > 0 ? cgraph->tvwgt[j] : 1); + } + + ReAdjustMemory(ctrl, graph, cgraph); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->ContractTmr)); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function creates the coarser graph. It uses a simple hash-table + for identifying the adjacent vertices that get collapsed to the same + node. The hash-table can have conflicts, which are handled via a + linear scan. It relies on the perm[] array to visit the vertices in + increasing cnvtxs order. + */ +/*************************************************************************/ +void CreateCoarseGraphPerm(ctrl_t *ctrl, graph_t *graph, idx_t cnvtxs, + idx_t *match, idx_t *perm) +{ + idx_t i, j, jj, k, kk, l, m, istart, iend, nvtxs, nedges, ncon, cnedges, + v, u, mask, dovsize; + idx_t *xadj, *vwgt, *vsize, *adjncy, *adjwgt; + idx_t *cmap, *htable; + idx_t *cxadj, *cvwgt, *cvsize, *cadjncy, *cadjwgt; + graph_t *cgraph; + + WCOREPUSH; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->ContractTmr)); + + dovsize = (ctrl->objtype == METIS_OBJTYPE_VOL ? 1 : 0); + + mask = HTLENGTH; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + vsize = graph->vsize; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + cmap = graph->cmap; + + /* Initialize the coarser graph */ + cgraph = SetupCoarseGraph(graph, cnvtxs, dovsize); + cxadj = cgraph->xadj; + cvwgt = cgraph->vwgt; + cvsize = cgraph->vsize; + cadjncy = cgraph->adjncy; + cadjwgt = cgraph->adjwgt; + + htable = iset(mask+1, -1, iwspacemalloc(ctrl, mask+1)); + + cxadj[0] = cnvtxs = cnedges = 0; + for (i=0; i= 0 && cadjncy[jj] != cnvtxs) { + for (jj=0; jj= 0 && cadjncy[jj] == cnvtxs) { /* This 2nd check is needed for non-adjacent matchings */ + cadjncy[jj] = cadjncy[--nedges]; + cadjwgt[jj] = cadjwgt[nedges]; + } + } + + for (j=0; jnedges = cnedges; + + for (i=0; itvwgt[i] = isum(cgraph->nvtxs, cgraph->vwgt+i, ncon); + cgraph->invtvwgt[i] = 1.0/(cgraph->tvwgt[i] > 0 ? cgraph->tvwgt[i] : 1); + } + + + ReAdjustMemory(ctrl, graph, cgraph); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->ContractTmr)); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! Setup the various arrays for the coarse graph + */ +/*************************************************************************/ +graph_t *SetupCoarseGraph(graph_t *graph, idx_t cnvtxs, idx_t dovsize) +{ + graph_t *cgraph; + + cgraph = CreateGraph(); + + cgraph->nvtxs = cnvtxs; + cgraph->ncon = graph->ncon; + + cgraph->finer = graph; + graph->coarser = cgraph; + + + /* Allocate memory for the coarser graph */ + cgraph->xadj = imalloc(cnvtxs+1, "SetupCoarseGraph: xadj"); + cgraph->adjncy = imalloc(graph->nedges, "SetupCoarseGraph: adjncy"); + cgraph->adjwgt = imalloc(graph->nedges, "SetupCoarseGraph: adjwgt"); + cgraph->vwgt = imalloc(cgraph->ncon*cnvtxs, "SetupCoarseGraph: vwgt"); + cgraph->tvwgt = imalloc(cgraph->ncon, "SetupCoarseGraph: tvwgt"); + cgraph->invtvwgt = rmalloc(cgraph->ncon, "SetupCoarseGraph: invtvwgt"); + + if (dovsize) + cgraph->vsize = imalloc(cnvtxs, "SetupCoarseGraph: vsize"); + + return cgraph; +} + + +/*************************************************************************/ +/*! This function re-adjusts the amount of memory that was allocated if + it will lead to significant savings + */ +/*************************************************************************/ +void ReAdjustMemory(ctrl_t *ctrl, graph_t *graph, graph_t *cgraph) +{ + if (cgraph->nedges > 10000 && cgraph->nedges < 0.9*graph->nedges) { + cgraph->adjncy = irealloc(cgraph->adjncy, cgraph->nedges, "ReAdjustMemory: adjncy"); + cgraph->adjwgt = irealloc(cgraph->adjwgt, cgraph->nedges, "ReAdjustMemory: adjwgt"); + } +} diff --git a/src/metis/libmetis/compress.c b/src/metis/libmetis/compress.c new file mode 100644 index 0000000..d72472b --- /dev/null +++ b/src/metis/libmetis/compress.c @@ -0,0 +1,229 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * compress.c + * + * This file contains code for compressing nodes with identical adjacency + * structure and for prunning dense columns + * + * Started 9/17/97 + * George + */ + +#include "metislib.h" + +/*************************************************************************/ +/*! This function compresses a graph by merging identical vertices + The compression should lead to at least 10% reduction. + + The compressed graph that is generated has its adjwgts set to 1. + + \returns 1 if compression was performed, otherwise it returns 0. + +*/ +/**************************************************************************/ +graph_t *CompressGraph(ctrl_t *ctrl, idx_t nvtxs, idx_t *xadj, idx_t *adjncy, + idx_t *vwgt, idx_t *cptr, idx_t *cind) +{ + idx_t i, ii, iii, j, jj, k, l, cnvtxs, cnedges; + idx_t *cxadj, *cadjncy, *cvwgt, *mark, *map; + ikv_t *keys; + graph_t *graph=NULL; + + mark = ismalloc(nvtxs, -1, "CompressGraph: mark"); + map = ismalloc(nvtxs, -1, "CompressGraph: map"); + keys = ikvmalloc(nvtxs, "CompressGraph: keys"); + + /* Compute a key for each adjacency list */ + for (i=0; idbglvl, METIS_DBG_INFO, + printf(" Compression: reduction in # of vertices: %"PRIDX".\n", nvtxs-cnvtxs)); + + + if (cnvtxs < COMPRESSION_FRACTION*nvtxs) { + /* Sufficient compression is possible, so go ahead and create the + compressed graph */ + + graph = CreateGraph(); + + cnedges = 0; + for (i=0; ixadj = imalloc(cnvtxs+1, "CompressGraph: xadj"); + cvwgt = graph->vwgt = ismalloc(cnvtxs, 0, "CompressGraph: vwgt"); + cadjncy = graph->adjncy = imalloc(cnedges, "CompressGraph: adjncy"); + graph->adjwgt = ismalloc(cnedges, 1, "CompressGraph: adjwgt"); + + /* Now go and compress the graph */ + iset(nvtxs, -1, mark); + l = cxadj[0] = 0; + for (i=0; invtxs = cnvtxs; + graph->nedges = l; + graph->ncon = 1; + + SetupGraph_tvwgt(graph); + SetupGraph_label(graph); + } + + gk_free((void **)&keys, &map, &mark, LTERM); + + return graph; + +} + + + +/*************************************************************************/ +/*! This function prunes all the vertices in a graph with degree greater + than factor*average. + + \returns the number of vertices that were prunned. +*/ +/*************************************************************************/ +graph_t *PruneGraph(ctrl_t *ctrl, idx_t nvtxs, idx_t *xadj, idx_t *adjncy, + idx_t *vwgt, idx_t *iperm, real_t factor) +{ + idx_t i, j, k, l, nlarge, pnvtxs, pnedges; + idx_t *pxadj, *padjncy, *padjwgt, *pvwgt; + idx_t *perm; + graph_t *graph=NULL; + + perm = imalloc(nvtxs, "PruneGraph: perm"); + + factor = factor*xadj[nvtxs]/nvtxs; + + pnvtxs = pnedges = nlarge = 0; + for (i=0; idbglvl, METIS_DBG_INFO, + printf(" Pruned %"PRIDX" of %"PRIDX" vertices.\n", nlarge, nvtxs)); + + + if (nlarge > 0 && nlarge < nvtxs) { + /* Prunning is possible, so go ahead and create the prunned graph */ + graph = CreateGraph(); + + /* Allocate memory for the prunned graph*/ + pxadj = graph->xadj = imalloc(pnvtxs+1, "PruneGraph: xadj"); + pvwgt = graph->vwgt = imalloc(pnvtxs, "PruneGraph: vwgt"); + padjncy = graph->adjncy = imalloc(pnedges, "PruneGraph: adjncy"); + graph->adjwgt = ismalloc(pnedges, 1, "PruneGraph: adjwgt"); + + pxadj[0] = pnedges = l = 0; + for (i=0; invtxs = pnvtxs; + graph->nedges = pnedges; + graph->ncon = 1; + + SetupGraph_tvwgt(graph); + SetupGraph_label(graph); + } + else if (nlarge > 0 && nlarge == nvtxs) { + IFSET(ctrl->dbglvl, METIS_DBG_INFO, + printf(" Pruning is ignored as it removes all vertices.\n")); + nlarge = 0; + } + + + gk_free((void **)&perm, LTERM); + + return graph; +} + + + + + + + + + diff --git a/src/metis/libmetis/contig.c b/src/metis/libmetis/contig.c new file mode 100644 index 0000000..3f45902 --- /dev/null +++ b/src/metis/libmetis/contig.c @@ -0,0 +1,699 @@ +/*! +\file +\brief Functions that deal with eliminating disconnected partitions + +\date Started 7/15/98 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version $Id: contig.c 10513 2011-07-07 22:06:03Z karypis $ +*/ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function finds the connected components induced by the + partitioning vector. + + \param graph is the graph structure + \param where is the partitioning vector. If this is NULL, then the + entire graph is treated to belong into a single partition. + \param cptr is the ptr structure of the CSR representation of the + components. The length of this vector must be graph->nvtxs+1. + \param cind is the indices structure of the CSR representation of + the components. The length of this vector must be graph->nvtxs. + + \returns the number of components that it found. + + \note The cptr and cind parameters can be NULL, in which case only the + number of connected components is returned. +*/ +/*************************************************************************/ +idx_t FindPartitionInducedComponents(graph_t *graph, idx_t *where, + idx_t *cptr, idx_t *cind) +{ + idx_t i, ii, j, jj, k, me=0, nvtxs, first, last, nleft, ncmps; + idx_t *xadj, *adjncy; + idx_t *touched, *perm, *todo; + idx_t mustfree_ccsr=0, mustfree_where=0; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + /* Deal with NULL supplied cptr/cind vectors */ + if (cptr == NULL) { + cptr = imalloc(nvtxs+1, "FindPartitionInducedComponents: cptr"); + cind = imalloc(nvtxs, "FindPartitionInducedComponents: cind"); + mustfree_ccsr = 1; + } + + /* Deal with NULL supplied where vector */ + if (where == NULL) { + where = ismalloc(nvtxs, 0, "FindPartitionInducedComponents: where"); + mustfree_where = 1; + } + + /* Allocate memory required for the BFS traversal */ + perm = iincset(nvtxs, 0, imalloc(nvtxs, "FindPartitionInducedComponents: perm")); + todo = iincset(nvtxs, 0, imalloc(nvtxs, "FindPartitionInducedComponents: todo")); + touched = ismalloc(nvtxs, 0, "FindPartitionInducedComponents: touched"); + + + /* Find the connected componends induced by the partition */ + ncmps = -1; + first = last = 0; + nleft = nvtxs; + while (nleft > 0) { + if (first == last) { /* Find another starting vertex */ + cptr[++ncmps] = first; + ASSERT(touched[todo[0]] == 0); + i = todo[0]; + cind[last++] = i; + touched[i] = 1; + me = where[i]; + } + + i = cind[first++]; + k = perm[i]; + j = todo[k] = todo[--nleft]; + perm[j] = k; + + for (j=xadj[i]; jnvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + /* Allocate memory required for the BFS traversal */ + perm = iincset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + + iincset(nvtxs, 0, bfsperm); /* this array will also store the vertices + still to be processed */ + + /* Find the connected componends induced by the partition */ + first = last = 0; + while (first < nvtxs) { + if (first == last) { /* Find another starting vertex */ + k = bfsperm[last]; + ASSERT(perm[k] != -1); + perm[k] = -1; /* mark node as being visited */ + last++; + } + + i = bfsperm[first++]; + for (j=xadj[i]; jnvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + where = graph->where; + + touched = ismalloc(nvtxs, 0, "IsConnected: touched"); + queue = imalloc(nvtxs, "IsConnected: queue"); + cptr = imalloc(nvtxs+1, "IsConnected: cptr"); + + nleft = 0; + for (i=0; i 1 && report) { + printf("The graph has %"PRIDX" connected components in partition %"PRIDX":\t", ncmps, pid); + for (i=0; ivwgt[queue[j]]; + printf("[%5"PRIDX" %5"PRIDX"] ", cptr[i+1]-cptr[i], wgt); + /* + if (cptr[i+1]-cptr[i] == 1) + printf("[%"PRIDX" %"PRIDX"] ", queue[cptr[i]], xadj[queue[cptr[i]]+1]-xadj[queue[cptr[i]]]); + */ + } + printf("\n"); + } + + gk_free((void **)&touched, &queue, &cptr, LTERM); + + return (ncmps == 1 ? 1 : 0); +} + + +/*************************************************************************/ +/*! This function identifies the number of connected components in a graph + that result after removing the vertices that belong to the vertex + separator (i.e., graph->where[i] == 2). + The connected component memberships are returned in the CSR-style + pair of arrays cptr, cind. +*/ +/**************************************************************************/ +idx_t FindSepInducedComponents(ctrl_t *ctrl, graph_t *graph, idx_t *cptr, + idx_t *cind) +{ + idx_t i, j, k, nvtxs, first, last, nleft, ncmps, wgt; + idx_t *xadj, *adjncy, *where, *touched, *queue; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + where = graph->where; + + touched = ismalloc(nvtxs, 0, "IsConnected: queue"); + + for (i=0; inbnd; i++) + touched[graph->bndind[i]] = 1; + + queue = cind; + + nleft = 0; + for (i=0; iwhere and tries to push them around to + remove some of them. */ +/*************************************************************************/ +void EliminateComponents(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, ii, j, jj, k, me, nparts, nvtxs, ncon, ncmps, other, + ncand, target; + idx_t *xadj, *adjncy, *vwgt, *adjwgt, *where, *pwgts; + idx_t *cptr, *cind, *cpvec, *pcptr, *pcind, *cwhere; + idx_t cid, bestcid, *cwgt, *bestcwgt; + idx_t ntodo, oldntodo, *todo; + rkv_t *cand; + real_t *tpwgts; + idx_t *vmarker=NULL, *pmarker=NULL, *modind=NULL; /* volume specific work arrays */ + + WCOREPUSH; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + adjwgt = (ctrl->objtype == METIS_OBJTYPE_VOL ? NULL : graph->adjwgt); + + where = graph->where; + pwgts = graph->pwgts; + + nparts = ctrl->nparts; + tpwgts = ctrl->tpwgts; + + cptr = iwspacemalloc(ctrl, nvtxs+1); + cind = iwspacemalloc(ctrl, nvtxs); + + ncmps = FindPartitionInducedComponents(graph, where, cptr, cind); + + IFSET(ctrl->dbglvl, METIS_DBG_CONTIGINFO, + printf("I found %"PRIDX" components, for this %"PRIDX"-way partition\n", + ncmps, nparts)); + + /* There are more components than partitions */ + if (ncmps > nparts) { + cwgt = iwspacemalloc(ctrl, ncon); + bestcwgt = iwspacemalloc(ctrl, ncon); + cpvec = iwspacemalloc(ctrl, nparts); + pcptr = iset(nparts+1, 0, iwspacemalloc(ctrl, nparts+1)); + pcind = iwspacemalloc(ctrl, ncmps); + cwhere = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs)); + todo = iwspacemalloc(ctrl, ncmps); + cand = (rkv_t *)wspacemalloc(ctrl, nparts*sizeof(rkv_t)); + + if (ctrl->objtype == METIS_OBJTYPE_VOL) { + /* Vol-refinement specific working arrays */ + modind = iwspacemalloc(ctrl, nvtxs); + vmarker = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + pmarker = iset(nparts, -1, iwspacemalloc(ctrl, nparts)); + } + + + /* Get a CSR representation of the components-2-partitions mapping */ + for (i=0; i 0) { + oldntodo = ntodo; + for (i=0; idbglvl, METIS_DBG_CONTIGINFO, + printf("Trying to move %"PRIDX" [%"PRIDX"] from %"PRIDX"\n", + cid, isum(ncon, cwgt, 1), me)); + + /* Determine the connectivity */ + iset(nparts, 0, cpvec); + for (j=cptr[cid]; j 0) { + cand[ncand].key = cpvec[j]; + cand[ncand++].val = j; + } + } + if (ncand == 0) + continue; + + rkvsortd(ncand, cand); + + /* Limit the moves to only the top candidates, which are defined as + those with connectivity at least 50% of the best. + This applies only when ncon=1, as for multi-constraint, balancing + will be hard. */ + if (ncon == 1) { + for (j=1; jubfactors, + 1, pwgts+target*ncon, ctrl->pijbm+target*ncon, + 1, pwgts+cand[j].val*ncon, ctrl->pijbm+cand[j].val*ncon)) + target = cand[j].val; + } + + IFSET(ctrl->dbglvl, METIS_DBG_CONTIGINFO, + printf("\tMoving it to %"PRIDX" [%"PRIDX"] [%"PRIDX"]\n", target, cpvec[target], ncand)); + + /* Note that as a result of a previous movement, a connected component may + now will like to stay to its original partition */ + if (target != me) { + switch (ctrl->objtype) { + case METIS_OBJTYPE_CUT: + MoveGroupContigForCut(ctrl, graph, target, cid, cptr, cind); + break; + + case METIS_OBJTYPE_VOL: + MoveGroupContigForVol(ctrl, graph, target, cid, cptr, cind, + vmarker, pmarker, modind); + break; + + default: + gk_errexit(SIGERR, "Unknown objtype %d\n", ctrl->objtype); + } + } + + /* Update the cwhere vector */ + for (j=cptr[cid]; jdbglvl, METIS_DBG_CONTIGINFO, printf("Stopped at ntodo: %"PRIDX"\n", ntodo)); + break; + } + } + + for (i=0; invtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + where = graph->where; + bndptr = graph->bndptr; + bndind = graph->bndind; + + nbnd = graph->nbnd; + + for (iii=ptr[gid]; iiickrinfo+i; + if (myrinfo->inbr == -1) { + myrinfo->inbr = cnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1); + myrinfo->nnbrs = 0; + } + mynbrs = ctrl->cnbrpool + myrinfo->inbr; + + /* find the location of 'to' in myrinfo or create it if it is not there */ + for (k=0; knnbrs; k++) { + if (mynbrs[k].pid == to) + break; + } + if (k == myrinfo->nnbrs) { + mynbrs[k].pid = to; + mynbrs[k].ed = 0; + myrinfo->nnbrs++; + } + + graph->mincut -= mynbrs[k].ed-myrinfo->id; + + /* Update ID/ED and BND related information for the moved vertex */ + iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1); + iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1); + UpdateMovedVertexInfoAndBND(i, from, k, to, myrinfo, mynbrs, where, nbnd, + bndptr, bndind, BNDTYPE_REFINE); + + /* Update the degrees of adjacent vertices */ + for (j=xadj[i]; jckrinfo+ii; + + UpdateAdjacentVertexInfoAndBND(ctrl, ii, xadj[ii+1]-xadj[ii], me, + from, to, myrinfo, adjwgt[j], nbnd, bndptr, bndind, BNDTYPE_REFINE); + } + + ASSERT(CheckRInfo(ctrl, graph->ckrinfo+i)); + } + + graph->nbnd = nbnd; +} + + +/*************************************************************************/ +/*! This function moves a collection of vertices and updates their rinfo + */ +/*************************************************************************/ +void MoveGroupContigForVol(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t gid, + idx_t *ptr, idx_t *ind, idx_t *vmarker, idx_t *pmarker, + idx_t *modind) +{ + idx_t i, ii, iii, j, jj, k, l, nvtxs, from, me, other, xgain; + idx_t *xadj, *vsize, *adjncy, *where; + vkrinfo_t *myrinfo, *orinfo; + vnbr_t *mynbrs, *onbrs; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vsize = graph->vsize; + adjncy = graph->adjncy; + where = graph->where; + + for (iii=ptr[gid]; iiivkrinfo+i; + if (myrinfo->inbr == -1) { + myrinfo->inbr = vnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1); + myrinfo->nnbrs = 0; + } + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + xgain = (myrinfo->nid == 0 && myrinfo->ned > 0 ? vsize[i] : 0); + + /* find the location of 'to' in myrinfo or create it if it is not there */ + for (k=0; knnbrs; k++) { + if (mynbrs[k].pid == to) + break; + } + if (k == myrinfo->nnbrs) { + if (myrinfo->nid > 0) + xgain -= vsize[i]; + + /* determine the volume gain resulting from that move */ + for (j=xadj[i]; jvkrinfo+ii; + onbrs = ctrl->vnbrpool + orinfo->inbr; + ASSERT(other != to) + + if (from == other) { + /* Same subdomain vertex: Decrease the gain if 'to' is a new neighbor. */ + for (l=0; lnnbrs; l++) { + if (onbrs[l].pid == to) + break; + } + if (l == orinfo->nnbrs) + xgain -= vsize[ii]; + } + else { + /* Remote vertex: increase if 'to' is a new subdomain */ + for (l=0; lnnbrs; l++) { + if (onbrs[l].pid == to) + break; + } + if (l == orinfo->nnbrs) + xgain -= vsize[ii]; + + /* Remote vertex: decrease if i is the only connection to 'from' */ + for (l=0; lnnbrs; l++) { + if (onbrs[l].pid == from && onbrs[l].ned == 1) { + xgain += vsize[ii]; + break; + } + } + } + } + graph->minvol -= xgain; + graph->mincut -= -myrinfo->nid; + } + else { + graph->minvol -= (xgain + mynbrs[k].gv); + graph->mincut -= mynbrs[k].ned-myrinfo->nid; + } + + + /* Update where and pwgts */ + where[i] = to; + iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1); + iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1); + + /* Update the id/ed/gains/bnd of potentially affected nodes */ + KWayVolUpdate(ctrl, graph, i, from, to, NULL, NULL, NULL, NULL, + NULL, BNDTYPE_REFINE, vmarker, pmarker, modind); + + /*CheckKWayVolPartitionParams(ctrl, graph);*/ + } + + ASSERT(ComputeCut(graph, where) == graph->mincut); + ASSERTP(ComputeVolume(graph, where) == graph->minvol, + ("%"PRIDX" %"PRIDX"\n", ComputeVolume(graph, where), graph->minvol)); + +} + diff --git a/src/metis/libmetis/debug.c b/src/metis/libmetis/debug.c new file mode 100644 index 0000000..e188135 --- /dev/null +++ b/src/metis/libmetis/debug.c @@ -0,0 +1,461 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * debug.c + * + * This file contains code that performs self debuging + * + * Started 7/24/97 + * George + * + */ + +#include "metislib.h" + + + +/*************************************************************************/ +/*! This function computes the total edgecut + */ +/*************************************************************************/ +idx_t ComputeCut(graph_t *graph, idx_t *where) +{ + idx_t i, j, cut; + + if (graph->adjwgt == NULL) { + for (cut=0, i=0; invtxs; i++) { + for (j=graph->xadj[i]; jxadj[i+1]; j++) + if (where[i] != where[graph->adjncy[j]]) + cut++; + } + } + else { + for (cut=0, i=0; invtxs; i++) { + for (j=graph->xadj[i]; jxadj[i+1]; j++) + if (where[i] != where[graph->adjncy[j]]) + cut += graph->adjwgt[j]; + } + } + + return cut/2; +} + + +/*************************************************************************/ +/*! This function computes the total volume + */ +/*************************************************************************/ +idx_t ComputeVolume(graph_t *graph, idx_t *where) +{ + idx_t i, j, k, me, nvtxs, nparts, totalv; + idx_t *xadj, *adjncy, *vsize, *marker; + + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + vsize = graph->vsize; + + nparts = where[iargmax(nvtxs, where)]+1; + marker = ismalloc(nparts, -1, "ComputeVolume: marker"); + + totalv = 0; + + for (i=0; iadjwgt == NULL) { + for (i=0; invtxs; i++) { + for (j=graph->xadj[i]; jxadj[i+1]; j++) + if (where[i] != where[graph->adjncy[j]]) + cuts[where[i]]++; + } + } + else { + for (i=0; invtxs; i++) { + for (j=graph->xadj[i]; jxadj[i+1]; j++) + if (where[i] != where[graph->adjncy[j]]) + cuts[where[i]] += graph->adjwgt[j]; + } + } + + maxcut = cuts[iargmax(nparts, cuts)]; + + printf("%zu => %"PRIDX"\n", iargmax(nparts, cuts), maxcut); + + gk_free((void **)&cuts, LTERM); + + return maxcut; +} + + +/*************************************************************************/ +/*! This function checks whether or not the boundary information is correct + */ +/*************************************************************************/ +idx_t CheckBnd(graph_t *graph) +{ + idx_t i, j, nvtxs, nbnd; + idx_t *xadj, *adjncy, *where, *bndptr, *bndind; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + where = graph->where; + bndptr = graph->bndptr; + bndind = graph->bndind; + + for (nbnd=0, i=0; inbnd, ("%"PRIDX" %"PRIDX"\n", nbnd, graph->nbnd)); + + return 1; +} + + + +/*************************************************************************/ +/*! This function checks whether or not the boundary information is correct + */ +/*************************************************************************/ +idx_t CheckBnd2(graph_t *graph) +{ + idx_t i, j, nvtxs, nbnd, id, ed; + idx_t *xadj, *adjncy, *where, *bndptr, *bndind; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + where = graph->where; + bndptr = graph->bndptr; + bndind = graph->bndind; + + for (nbnd=0, i=0; iadjwgt[j]; + else + id += graph->adjwgt[j]; + } + if (ed - id >= 0 && xadj[i] < xadj[i+1]) { + nbnd++; + ASSERTP(bndptr[i] != -1, ("%"PRIDX" %"PRIDX" %"PRIDX"\n", i, id, ed)); + ASSERT(bndind[bndptr[i]] == i); + } + } + + ASSERTP(nbnd == graph->nbnd, ("%"PRIDX" %"PRIDX"\n", nbnd, graph->nbnd)); + + return 1; +} + + +/*************************************************************************/ +/*! This function checks whether or not the boundary information is correct + */ +/*************************************************************************/ +idx_t CheckNodeBnd(graph_t *graph, idx_t onbnd) +{ + idx_t i, j, nvtxs, nbnd; + idx_t *xadj, *adjncy, *where, *bndptr, *bndind; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + where = graph->where; + bndptr = graph->bndptr; + bndind = graph->bndind; + + for (nbnd=0, i=0; icnbrpool + rinfo->inbr; + + for (i=0; innbrs; i++) { + for (j=i+1; jnnbrs; j++) + ASSERTP(nbrs[i].pid != nbrs[j].pid, + ("%"PRIDX" %"PRIDX" %"PRIDX" %"PRIDX"\n", + i, j, nbrs[i].pid, nbrs[j].pid)); + } + + return 1; +} + + + +/*************************************************************************/ +/*! This function checks the correctness of the NodeFM data structures + */ +/*************************************************************************/ +idx_t CheckNodePartitionParams(graph_t *graph) +{ + idx_t i, j, k, l, nvtxs, me, other; + idx_t *xadj, *adjncy, *adjwgt, *vwgt, *where; + idx_t edegrees[2], pwgts[3]; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + where = graph->where; + + /*------------------------------------------------------------ + / Compute now the separator external degrees + /------------------------------------------------------------*/ + pwgts[0] = pwgts[1] = pwgts[2] = 0; + for (i=0; inrinfo[i].edegrees[0] || + edegrees[1] != graph->nrinfo[i].edegrees[1]) { + printf("Something wrong with edegrees: %"PRIDX" %"PRIDX" %"PRIDX" %"PRIDX" %"PRIDX"\n", + i, edegrees[0], edegrees[1], + graph->nrinfo[i].edegrees[0], graph->nrinfo[i].edegrees[1]); + return 0; + } + } + } + + if (pwgts[0] != graph->pwgts[0] || + pwgts[1] != graph->pwgts[1] || + pwgts[2] != graph->pwgts[2]) { + printf("Something wrong with part-weights: %"PRIDX" %"PRIDX" %"PRIDX" %"PRIDX" %"PRIDX" %"PRIDX"\n", pwgts[0], pwgts[1], pwgts[2], graph->pwgts[0], graph->pwgts[1], graph->pwgts[2]); + return 0; + } + + return 1; +} + + +/*************************************************************************/ +/*! This function checks if the separator is indeed a separator + */ +/*************************************************************************/ +idx_t IsSeparable(graph_t *graph) +{ + idx_t i, j, nvtxs, other; + idx_t *xadj, *adjncy, *where; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + where = graph->where; + + for (i=0; ivrinfo structure */ +/*************************************************************************/ +void CheckKWayVolPartitionParams(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, ii, j, k, kk, l, nvtxs, nbnd, mincut, minvol, me, other, pid; + idx_t *xadj, *vsize, *adjncy, *pwgts, *where, *bndind, *bndptr; + vkrinfo_t *rinfo, *myrinfo, *orinfo, tmprinfo; + vnbr_t *mynbrs, *onbrs, *tmpnbrs; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vsize = graph->vsize; + adjncy = graph->adjncy; + where = graph->where; + rinfo = graph->vkrinfo; + + tmpnbrs = (vnbr_t *)wspacemalloc(ctrl, ctrl->nparts*sizeof(vnbr_t)); + + /*------------------------------------------------------------ + / Compute now the iv/ev degrees + /------------------------------------------------------------*/ + for (i=0; ivnbrpool + myrinfo->inbr; + + for (k=0; knnbrs; k++) + tmpnbrs[k] = mynbrs[k]; + + tmprinfo.nnbrs = myrinfo->nnbrs; + tmprinfo.nid = myrinfo->nid; + tmprinfo.ned = myrinfo->ned; + + myrinfo = &tmprinfo; + mynbrs = tmpnbrs; + + for (k=0; knnbrs; k++) + mynbrs[k].gv = 0; + + for (j=xadj[i]; jvnbrpool + orinfo->inbr; + + if (me == other) { + /* Find which domains 'i' is connected and 'ii' is not and update their gain */ + for (k=0; knnbrs; k++) { + pid = mynbrs[k].pid; + for (kk=0; kknnbrs; kk++) { + if (onbrs[kk].pid == pid) + break; + } + if (kk == orinfo->nnbrs) + mynbrs[k].gv -= vsize[ii]; + } + } + else { + /* Find the orinfo[me].ed and see if I'm the only connection */ + for (k=0; knnbrs; k++) { + if (onbrs[k].pid == me) + break; + } + + if (onbrs[k].ned == 1) { /* I'm the only connection of 'ii' in 'me' */ + for (k=0; knnbrs; k++) { + if (mynbrs[k].pid == other) { + mynbrs[k].gv += vsize[ii]; + break; + } + } + + /* Increase the gains for all the common domains between 'i' and 'ii' */ + for (k=0; knnbrs; k++) { + if ((pid = mynbrs[k].pid) == other) + continue; + for (kk=0; kknnbrs; kk++) { + if (onbrs[kk].pid == pid) { + mynbrs[k].gv += vsize[ii]; + break; + } + } + } + + } + else { + /* Find which domains 'i' is connected and 'ii' is not and update their gain */ + for (k=0; knnbrs; k++) { + if ((pid = mynbrs[k].pid) == other) + continue; + for (kk=0; kknnbrs; kk++) { + if (onbrs[kk].pid == pid) + break; + } + if (kk == orinfo->nnbrs) + mynbrs[k].gv -= vsize[ii]; + } + } + } + } + + myrinfo = rinfo+i; + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + for (k=0; knnbrs; k++) { + pid = mynbrs[k].pid; + for (kk=0; kkncon == 1) + FM_2WayCutRefine(ctrl, graph, ntpwgts, niter); + else + FM_Mc2WayCutRefine(ctrl, graph, ntpwgts, niter); +} + + +/*************************************************************************/ +/*! This function performs a cut-focused FM refinement */ +/*************************************************************************/ +void FM_2WayCutRefine(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niter) +{ + idx_t i, ii, j, k, kwgt, nvtxs, nbnd, nswaps, from, to, pass, me, limit, tmp; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind, *pwgts; + idx_t *moved, *swaps, *perm; + rpq_t *queues[2]; + idx_t higain, mincut, mindiff, origdiff, initcut, newcut, mincutorder, avgvwgt; + idx_t tpwgts[2]; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + where = graph->where; + id = graph->id; + ed = graph->ed; + pwgts = graph->pwgts; + bndptr = graph->bndptr; + bndind = graph->bndind; + + moved = iwspacemalloc(ctrl, nvtxs); + swaps = iwspacemalloc(ctrl, nvtxs); + perm = iwspacemalloc(ctrl, nvtxs); + + tpwgts[0] = graph->tvwgt[0]*ntpwgts[0]; + tpwgts[1] = graph->tvwgt[0]-tpwgts[0]; + + limit = gk_min(gk_max(0.01*nvtxs, 15), 100); + avgvwgt = gk_min((pwgts[0]+pwgts[1])/20, 2*(pwgts[0]+pwgts[1])/nvtxs); + + queues[0] = rpqCreate(nvtxs); + queues[1] = rpqCreate(nvtxs); + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + Print2WayRefineStats(ctrl, graph, ntpwgts, 0, -2)); + + origdiff = iabs(tpwgts[0]-pwgts[0]); + iset(nvtxs, -1, moved); + for (pass=0; passmincut; + mindiff = iabs(tpwgts[0]-pwgts[0]); + + ASSERT(ComputeCut(graph, where) == graph->mincut); + ASSERT(CheckBnd(graph)); + + /* Insert boundary nodes in the priority queues */ + nbnd = graph->nbnd; + irandArrayPermute(nbnd, perm, nbnd, 1); + for (ii=0; ii 0 || id[bndind[i]] == 0); + ASSERT(bndptr[bndind[i]] != -1); + rpqInsert(queues[where[bndind[i]]], bndind[i], ed[bndind[i]]-id[bndind[i]]); + } + + for (nswaps=0; nswaps limit) { /* We hit the limit, undo last move */ + newcut += (ed[higain]-id[higain]); + INC_DEC(pwgts[from], pwgts[to], vwgt[higain]); + break; + } + + where[higain] = to; + moved[higain] = nswaps; + swaps[nswaps] = higain; + + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, + printf("Moved %6"PRIDX" from %"PRIDX". [%3"PRIDX" %3"PRIDX"] %5"PRIDX" [%4"PRIDX" %4"PRIDX"]\n", higain, from, ed[higain]-id[higain], vwgt[higain], newcut, pwgts[0], pwgts[1])); + + /************************************************************** + * Update the id[i]/ed[i] values of the affected nodes + ***************************************************************/ + SWAP(id[higain], ed[higain], tmp); + if (ed[higain] == 0 && xadj[higain] < xadj[higain+1]) + BNDDelete(nbnd, bndind, bndptr, higain); + + for (j=xadj[higain]; j 0) { /* It will now become a boundary vertex */ + BNDInsert(nbnd, bndind, bndptr, k); + if (moved[k] == -1) + rpqInsert(queues[where[k]], k, ed[k]-id[k]); + } + } + } + + } + + + /**************************************************************** + * Roll back computations + *****************************************************************/ + for (i=0; imincutorder; nswaps--) { + higain = swaps[nswaps]; + + to = where[higain] = (where[higain]+1)%2; + SWAP(id[higain], ed[higain], tmp); + if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) + BNDDelete(nbnd, bndind, bndptr, higain); + else if (ed[higain] > 0 && bndptr[higain] == -1) + BNDInsert(nbnd, bndind, bndptr, higain); + + INC_DEC(pwgts[to], pwgts[(to+1)%2], vwgt[higain]); + for (j=xadj[higain]; j 0) + BNDInsert(nbnd, bndind, bndptr, k); + } + } + + graph->mincut = mincut; + graph->nbnd = nbnd; + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + Print2WayRefineStats(ctrl, graph, ntpwgts, 0, mincutorder)); + + if (mincutorder <= 0 || mincut == initcut) + break; + } + + rpqDestroy(queues[0]); + rpqDestroy(queues[1]); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function performs a cut-focused multi-constraint FM refinement */ +/*************************************************************************/ +void FM_Mc2WayCutRefine(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niter) +{ + idx_t i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, + me, limit, tmp, cnum; + idx_t *xadj, *adjncy, *vwgt, *adjwgt, *pwgts, *where, *id, *ed, + *bndptr, *bndind; + idx_t *moved, *swaps, *perm, *qnum; + idx_t higain, mincut, initcut, newcut, mincutorder; + real_t *invtvwgt, *ubfactors, *minbalv, *newbalv; + real_t origbal, minbal, newbal, rgain, ffactor; + rpq_t **queues; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + invtvwgt = graph->invtvwgt; + where = graph->where; + id = graph->id; + ed = graph->ed; + pwgts = graph->pwgts; + bndptr = graph->bndptr; + bndind = graph->bndind; + + moved = iwspacemalloc(ctrl, nvtxs); + swaps = iwspacemalloc(ctrl, nvtxs); + perm = iwspacemalloc(ctrl, nvtxs); + qnum = iwspacemalloc(ctrl, nvtxs); + ubfactors = rwspacemalloc(ctrl, ncon); + newbalv = rwspacemalloc(ctrl, ncon); + minbalv = rwspacemalloc(ctrl, ncon); + + limit = gk_min(gk_max(0.01*nvtxs, 25), 150); + + + /* Determine a fudge factor to allow the refinement routines to get out + of tight balancing constraints. */ + ffactor = .5/gk_max(20, nvtxs); + + /* Initialize the queues */ + queues = (rpq_t **)wspacemalloc(ctrl, 2*ncon*sizeof(rpq_t *)); + for (i=0; i<2*ncon; i++) + queues[i] = rpqCreate(nvtxs); + for (i=0; ipijbm, ctrl->ubfactors, ubfactors); + for (i=0; i 0 ? ctrl->ubfactors[i]+ubfactors[i] : ctrl->ubfactors[i]); + + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + Print2WayRefineStats(ctrl, graph, ntpwgts, origbal, -2)); + + iset(nvtxs, -1, moved); + for (pass=0; passmincut; + + minbal = ComputeLoadImbalanceDiffVec(graph, 2, ctrl->pijbm, ubfactors, minbalv); + + ASSERT(ComputeCut(graph, where) == graph->mincut); + ASSERT(CheckBnd(graph)); + + /* Insert boundary nodes in the priority queues */ + nbnd = graph->nbnd; + irandArrayPermute(nbnd, perm, nbnd/5, 1); + for (ii=0; ii 0 || id[i] == 0); + ASSERT(bndptr[i] != -1); + //rgain = 1.0*(ed[i]-id[i])/sqrt(vwgt[i*ncon+qnum[i]]+1); + //rgain = (ed[i]-id[i] > 0 ? 1.0*(ed[i]-id[i])/sqrt(vwgt[i*ncon+qnum[i]]+1) : ed[i]-id[i]); + rgain = ed[i]-id[i]; + rpqInsert(queues[2*qnum[i]+where[i]], i, rgain); + } + + for (nswaps=0; nswapspijbm, ubfactors, queues, &from, &cnum); + + to = (from+1)%2; + + if (from == -1 || (higain = rpqGetTop(queues[2*cnum+from])) == -1) + break; + ASSERT(bndptr[higain] != -1); + + newcut -= (ed[higain]-id[higain]); + + iaxpy(ncon, 1, vwgt+higain*ncon, 1, pwgts+to*ncon, 1); + iaxpy(ncon, -1, vwgt+higain*ncon, 1, pwgts+from*ncon, 1); + newbal = ComputeLoadImbalanceDiffVec(graph, 2, ctrl->pijbm, ubfactors, newbalv); + + if ((newcut < mincut && newbal <= ffactor) || + (newcut == mincut && (newbal < minbal || + (newbal == minbal && BetterBalance2Way(ncon, minbalv, newbalv))))) { + mincut = newcut; + minbal = newbal; + mincutorder = nswaps; + rcopy(ncon, newbalv, minbalv); + } + else if (nswaps-mincutorder > limit) { /* We hit the limit, undo last move */ + newcut += (ed[higain]-id[higain]); + iaxpy(ncon, 1, vwgt+higain*ncon, 1, pwgts+from*ncon, 1); + iaxpy(ncon, -1, vwgt+higain*ncon, 1, pwgts+to*ncon, 1); + break; + } + + where[higain] = to; + moved[higain] = nswaps; + swaps[nswaps] = higain; + + if (ctrl->dbglvl&METIS_DBG_MOVEINFO) { + printf("Moved%6"PRIDX" from %"PRIDX"(%"PRIDX") Gain:%5"PRIDX", " + "Cut:%5"PRIDX", NPwgts:", higain, from, cnum, ed[higain]-id[higain], newcut); + for (l=0; lpijbm), newbal); + } + + + /************************************************************** + * Update the id[i]/ed[i] values of the affected nodes + ***************************************************************/ + SWAP(id[higain], ed[higain], tmp); + if (ed[higain] == 0 && xadj[higain] < xadj[higain+1]) + BNDDelete(nbnd, bndind, bndptr, higain); + + for (j=xadj[higain]; j 0 ? + // 1.0*(ed[k]-id[k])/sqrt(vwgt[k*ncon+qnum[k]]+1) : ed[k]-id[k]); + rgain = ed[k]-id[k]; + rpqUpdate(queues[2*qnum[k]+where[k]], k, rgain); + } + } + } + else { + if (ed[k] > 0) { /* It will now become a boundary vertex */ + BNDInsert(nbnd, bndind, bndptr, k); + if (moved[k] == -1) { + //rgain = 1.0*(ed[k]-id[k])/sqrt(vwgt[k*ncon+qnum[k]]+1); + //rgain = (ed[k]-id[k] > 0 ? + // 1.0*(ed[k]-id[k])/sqrt(vwgt[k*ncon+qnum[k]]+1) : ed[k]-id[k]); + rgain = ed[k]-id[k]; + rpqInsert(queues[2*qnum[k]+where[k]], k, rgain); + } + } + } + } + + } + + + /**************************************************************** + * Roll back computations + *****************************************************************/ + for (i=0; imincutorder; nswaps--) { + higain = swaps[nswaps]; + + to = where[higain] = (where[higain]+1)%2; + SWAP(id[higain], ed[higain], tmp); + if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) + BNDDelete(nbnd, bndind, bndptr, higain); + else if (ed[higain] > 0 && bndptr[higain] == -1) + BNDInsert(nbnd, bndind, bndptr, higain); + + iaxpy(ncon, 1, vwgt+higain*ncon, 1, pwgts+to*ncon, 1); + iaxpy(ncon, -1, vwgt+higain*ncon, 1, pwgts+((to+1)%2)*ncon, 1); + for (j=xadj[higain]; j 0) + BNDInsert(nbnd, bndind, bndptr, k); + } + } + + graph->mincut = mincut; + graph->nbnd = nbnd; + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + Print2WayRefineStats(ctrl, graph, ntpwgts, minbal, mincutorder)); + + if (mincutorder <= 0 || mincut == initcut) + break; + } + + for (i=0; i<2*ncon; i++) + rpqDestroy(queues[i]); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function selects the partition number and the queue from which + we will move vertices out. */ +/*************************************************************************/ +void SelectQueue(graph_t *graph, real_t *pijbm, real_t *ubfactors, + rpq_t **queues, idx_t *from, idx_t *cnum) +{ + idx_t ncon, i, part; + real_t max, tmp; + + ncon = graph->ncon; + + *from = -1; + *cnum = -1; + + /* First determine the side and the queue, irrespective of the presence of nodes. + The side & queue is determined based on the most violated balancing constraint. */ + for (max=0.0, part=0; part<2; part++) { + for (i=0; ipwgts[part*ncon+i]*pijbm[part*ncon+i] - ubfactors[i]; + /* the '=' in the test bellow is to ensure that under tight constraints + the partition that is at the max is selected */ + if (tmp >= max) { + max = tmp; + *from = part; + *cnum = i; + } + } + } + + + if (*from != -1) { + /* in case the desired queue is empty, select a queue from the same side */ + if (rpqLength(queues[2*(*cnum)+(*from)]) == 0) { + for (i=0; i 0) { + max = graph->pwgts[(*from)*ncon+i]*pijbm[(*from)*ncon+i] - ubfactors[i]; + *cnum = i; + break; + } + } + + for (i++; ipwgts[(*from)*ncon+i]*pijbm[(*from)*ncon+i] - ubfactors[i]; + if (tmp > max && rpqLength(queues[2*i+(*from)]) > 0) { + max = tmp; + *cnum = i; + } + } + } + + /* + printf("Selected1 %"PRIDX"(%"PRIDX") -> %"PRIDX" [%5"PRREAL"]\n", + *from, *cnum, rpqLength(queues[2*(*cnum)+(*from)]), max); + */ + } + else { + /* the partitioning does not violate balancing constraints, in which case select + a queue based on cut criteria */ + for (part=0; part<2; part++) { + for (i=0; i 0 && + (*from == -1 || rpqSeeTopKey(queues[2*i+part]) > max)) { + max = rpqSeeTopKey(queues[2*i+part]); + *from = part; + *cnum = i; + } + } + } + /* + printf("Selected2 %"PRIDX"(%"PRIDX") -> %"PRIDX"\n", + *from, *cnum, rpqLength(queues[2*(*cnum)+(*from)]), max); + */ + } +} + + +/*************************************************************************/ +/*! Prints statistics about the refinement */ +/*************************************************************************/ +void Print2WayRefineStats(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, + real_t deltabal, idx_t mincutorder) +{ + int i; + + if (mincutorder == -2) { + printf("Parts: "); + printf("Nv-Nb[%5"PRIDX" %5"PRIDX"] ICut: %6"PRIDX, + graph->nvtxs, graph->nbnd, graph->mincut); + printf(" ["); + for (i=0; incon; i++) + printf("(%.3"PRREAL" %.3"PRREAL" T:%.3"PRREAL" %.3"PRREAL")", + graph->pwgts[i]*graph->invtvwgt[i], + graph->pwgts[graph->ncon+i]*graph->invtvwgt[i], + ntpwgts[i], ntpwgts[graph->ncon+i]); + printf("] LB: %.3"PRREAL"(%+.3"PRREAL")\n", + ComputeLoadImbalance(graph, 2, ctrl->pijbm), deltabal); + } + else { + printf("\tMincut: %6"PRIDX" at %5"PRIDX" NBND %6"PRIDX" NPwgts: [", + graph->mincut, mincutorder, graph->nbnd); + for (i=0; incon; i++) + printf("(%.3"PRREAL" %.3"PRREAL")", + graph->pwgts[i]*graph->invtvwgt[i], graph->pwgts[graph->ncon+i]*graph->invtvwgt[i]); + printf("] LB: %.3"PRREAL"(%+.3"PRREAL")\n", + ComputeLoadImbalance(graph, 2, ctrl->pijbm), deltabal); + } +} + diff --git a/src/metis/libmetis/fortran.c b/src/metis/libmetis/fortran.c new file mode 100644 index 0000000..5c3ed90 --- /dev/null +++ b/src/metis/libmetis/fortran.c @@ -0,0 +1,142 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * fortran.c + * + * This file contains code for the fortran to C interface + * + * Started 8/19/97 + * George + * + */ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function changes the numbering to start from 0 instead of 1 */ +/*************************************************************************/ +void Change2CNumbering(idx_t nvtxs, idx_t *xadj, idx_t *adjncy) +{ + idx_t i; + + for (i=0; i<=nvtxs; i++) + xadj[i]--; + + for (i=0; i (b)) +GK_MKPQUEUE(ipq, ipq_t, ikv_t, idx_t, idx_t, ikvmalloc, IDX_MAX, key_gt) +GK_MKPQUEUE(rpq, rpq_t, rkv_t, real_t, idx_t, rkvmalloc, REAL_MAX, key_gt) +#undef key_gt + +/*************************************************************************/ +/*! Random number generation routines */ +/*************************************************************************/ +GK_MKRANDOM(i, idx_t, idx_t) + +/*************************************************************************/ +/*! Utility routines */ +/*************************************************************************/ +GK_MKARRAY2CSR(i, idx_t) + +/*************************************************************************/ +/*! Sorting routines */ +/*************************************************************************/ +void isorti(size_t n, idx_t *base) +{ +#define i_lt(a, b) ((*a) < (*b)) + GK_MKQSORT(idx_t, base, n, i_lt); +#undef i_lt +} + +void isortd(size_t n, idx_t *base) +{ +#define i_gt(a, b) ((*a) > (*b)) + GK_MKQSORT(idx_t, base, n, i_gt); +#undef i_gt +} + +void rsorti(size_t n, real_t *base) +{ +#define r_lt(a, b) ((*a) < (*b)) + GK_MKQSORT(real_t, base, n, r_lt); +#undef r_lt +} + +void rsortd(size_t n, real_t *base) +{ +#define r_gt(a, b) ((*a) > (*b)) + GK_MKQSORT(real_t, base, n, r_gt); +#undef r_gt +} + +void ikvsorti(size_t n, ikv_t *base) +{ +#define ikey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(ikv_t, base, n, ikey_lt); +#undef ikey_lt +} + +/* Sorts based both on key and val */ +void ikvsortii(size_t n, ikv_t *base) +{ +#define ikeyval_lt(a, b) ((a)->key < (b)->key || ((a)->key == (b)->key && (a)->val < (b)->val)) + GK_MKQSORT(ikv_t, base, n, ikeyval_lt); +#undef ikeyval_lt +} + +void ikvsortd(size_t n, ikv_t *base) +{ +#define ikey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(ikv_t, base, n, ikey_gt); +#undef ikey_gt +} + +void rkvsorti(size_t n, rkv_t *base) +{ +#define rkey_lt(a, b) ((a)->key < (b)->key) + GK_MKQSORT(rkv_t, base, n, rkey_lt); +#undef rkey_lt +} + +void rkvsortd(size_t n, rkv_t *base) +{ +#define rkey_gt(a, b) ((a)->key > (b)->key) + GK_MKQSORT(rkv_t, base, n, rkey_gt); +#undef rkey_gt +} + +void uvwsorti(size_t n, uvw_t *base) +{ +#define uvwkey_lt(a, b) ((a)->u < (b)->u || ((a)->u == (b)->u && (a)->v < (b)->v)) + GK_MKQSORT(uvw_t, base, n, uvwkey_lt); +#undef uvwkey_lt +} + diff --git a/src/metis/libmetis/gklib_defs.h b/src/metis/libmetis/gklib_defs.h new file mode 100644 index 0000000..dfac5ca --- /dev/null +++ b/src/metis/libmetis/gklib_defs.h @@ -0,0 +1,53 @@ +/*! +\file +\brief Data structures and prototypes for GKlib integration + +\date Started 12/23/2008 +\author George +\version\verbatim $Id: gklib_defs.h 10395 2011-06-23 23:28:06Z karypis $ \endverbatim +*/ + +#ifndef _LIBMETIS_GKLIB_H_ +#define _LIBMETIS_GKLIB_H_ + +#include "gklib_rename.h" + +/*************************************************************************/ +/*! Stores a weighted edge */ +/*************************************************************************/ +typedef struct { + idx_t u, v, w; /*!< Edge (u,v) with weight w */ +} uvw_t; + +/************************************************************************* +* Define various data structure using GKlib's templates. +**************************************************************************/ +GK_MKKEYVALUE_T(ikv_t, idx_t, idx_t) +GK_MKKEYVALUE_T(rkv_t, real_t, idx_t) +GK_MKPQUEUE_T(ipq_t, ikv_t) +GK_MKPQUEUE_T(rpq_t, rkv_t) + + +/* gklib.c */ +GK_MKBLAS_PROTO(i, idx_t, idx_t) +GK_MKBLAS_PROTO(r, real_t, real_t) +GK_MKALLOC_PROTO(i, idx_t) +GK_MKALLOC_PROTO(r, real_t) +GK_MKALLOC_PROTO(ikv, ikv_t) +GK_MKALLOC_PROTO(rkv, rkv_t) +GK_MKPQUEUE_PROTO(ipq, ipq_t, idx_t, idx_t) +GK_MKPQUEUE_PROTO(rpq, rpq_t, real_t, idx_t) +GK_MKRANDOM_PROTO(i, idx_t, idx_t) +GK_MKARRAY2CSR_PROTO(i, idx_t) +void isorti(size_t n, idx_t *base); +void isortd(size_t n, idx_t *base); +void rsorti(size_t n, real_t *base); +void rsortd(size_t n, real_t *base); +void ikvsorti(size_t n, ikv_t *base); +void ikvsortii(size_t n, ikv_t *base); +void ikvsortd(size_t n, ikv_t *base); +void rkvsorti(size_t n, rkv_t *base); +void rkvsortd(size_t n, rkv_t *base); +void uvwsorti(size_t n, uvw_t *base); + +#endif diff --git a/src/metis/libmetis/gklib_rename.h b/src/metis/libmetis/gklib_rename.h new file mode 100644 index 0000000..78dc8b3 --- /dev/null +++ b/src/metis/libmetis/gklib_rename.h @@ -0,0 +1,122 @@ +/*! +\file + + * Copyright 1997, Regents of the University of Minnesota + * + * This file contains header files + * + * Started 10/2/97 + * George + * + * $Id: gklib_rename.h 10395 2011-06-23 23:28:06Z karypis $ + * + */ + + +#ifndef _LIBMETIS_GKLIB_RENAME_H_ +#define _LIBMETIS_GKLIB_RENAME_H_ + +/* gklib.c - generated from the .o files using the ./utils/listundescapedsumbols.csh */ +#define iAllocMatrix libmetis__iAllocMatrix +#define iFreeMatrix libmetis__iFreeMatrix +#define iSetMatrix libmetis__iSetMatrix +#define iargmax libmetis__iargmax +#define iargmax_n libmetis__iargmax_n +#define iargmin libmetis__iargmin +#define iarray2csr libmetis__iarray2csr +#define iaxpy libmetis__iaxpy +#define icopy libmetis__icopy +#define idot libmetis__idot +#define iincset libmetis__iincset +#define ikvAllocMatrix libmetis__ikvAllocMatrix +#define ikvFreeMatrix libmetis__ikvFreeMatrix +#define ikvSetMatrix libmetis__ikvSetMatrix +#define ikvcopy libmetis__ikvcopy +#define ikvmalloc libmetis__ikvmalloc +#define ikvrealloc libmetis__ikvrealloc +#define ikvset libmetis__ikvset +#define ikvsmalloc libmetis__ikvsmalloc +#define ikvsortd libmetis__ikvsortd +#define ikvsorti libmetis__ikvsorti +#define ikvsortii libmetis__ikvsortii +#define imalloc libmetis__imalloc +#define imax libmetis__imax +#define imin libmetis__imin +#define inorm2 libmetis__inorm2 +#define ipqCheckHeap libmetis__ipqCheckHeap +#define ipqCreate libmetis__ipqCreate +#define ipqDelete libmetis__ipqDelete +#define ipqDestroy libmetis__ipqDestroy +#define ipqFree libmetis__ipqFree +#define ipqGetTop libmetis__ipqGetTop +#define ipqInit libmetis__ipqInit +#define ipqInsert libmetis__ipqInsert +#define ipqLength libmetis__ipqLength +#define ipqReset libmetis__ipqReset +#define ipqSeeKey libmetis__ipqSeeKey +#define ipqSeeTopKey libmetis__ipqSeeTopKey +#define ipqSeeTopVal libmetis__ipqSeeTopVal +#define ipqUpdate libmetis__ipqUpdate +#define isrand libmetis__isrand +#define irand libmetis__irand +#define irandArrayPermute libmetis__irandArrayPermute +#define irandArrayPermuteFine libmetis__irandArrayPermuteFine +#define irandInRange libmetis__irandInRange +#define irealloc libmetis__irealloc +#define iscale libmetis__iscale +#define iset libmetis__iset +#define ismalloc libmetis__ismalloc +#define isortd libmetis__isortd +#define isorti libmetis__isorti +#define isrand libmetis__isrand +#define isum libmetis__isum +#define rAllocMatrix libmetis__rAllocMatrix +#define rFreeMatrix libmetis__rFreeMatrix +#define rSetMatrix libmetis__rSetMatrix +#define rargmax libmetis__rargmax +#define rargmax_n libmetis__rargmax_n +#define rargmin libmetis__rargmin +#define raxpy libmetis__raxpy +#define rcopy libmetis__rcopy +#define rdot libmetis__rdot +#define rincset libmetis__rincset +#define rkvAllocMatrix libmetis__rkvAllocMatrix +#define rkvFreeMatrix libmetis__rkvFreeMatrix +#define rkvSetMatrix libmetis__rkvSetMatrix +#define rkvcopy libmetis__rkvcopy +#define rkvmalloc libmetis__rkvmalloc +#define rkvrealloc libmetis__rkvrealloc +#define rkvset libmetis__rkvset +#define rkvsmalloc libmetis__rkvsmalloc +#define rkvsortd libmetis__rkvsortd +#define rkvsorti libmetis__rkvsorti +#define rmalloc libmetis__rmalloc +#define rmax libmetis__rmax +#define rmin libmetis__rmin +#define rnorm2 libmetis__rnorm2 +#define rpqCheckHeap libmetis__rpqCheckHeap +#define rpqCreate libmetis__rpqCreate +#define rpqDelete libmetis__rpqDelete +#define rpqDestroy libmetis__rpqDestroy +#define rpqFree libmetis__rpqFree +#define rpqGetTop libmetis__rpqGetTop +#define rpqInit libmetis__rpqInit +#define rpqInsert libmetis__rpqInsert +#define rpqLength libmetis__rpqLength +#define rpqReset libmetis__rpqReset +#define rpqSeeKey libmetis__rpqSeeKey +#define rpqSeeTopKey libmetis__rpqSeeTopKey +#define rpqSeeTopVal libmetis__rpqSeeTopVal +#define rpqUpdate libmetis__rpqUpdate +#define rrealloc libmetis__rrealloc +#define rscale libmetis__rscale +#define rset libmetis__rset +#define rsmalloc libmetis__rsmalloc +#define rsortd libmetis__rsortd +#define rsorti libmetis__rsorti +#define rsum libmetis__rsum +#define uvwsorti libmetis__uvwsorti + +#endif + + diff --git a/src/metis/libmetis/graph.c b/src/metis/libmetis/graph.c new file mode 100644 index 0000000..37f7d09 --- /dev/null +++ b/src/metis/libmetis/graph.c @@ -0,0 +1,274 @@ +/** +\file +\brief Functions that deal with setting up the graphs for METIS. + +\date Started 7/25/1997 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version\verbatim $Id: graph.c 10513 2011-07-07 22:06:03Z karypis $ \endverbatim +*/ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function sets up the graph from the user input */ +/*************************************************************************/ +graph_t *SetupGraph(ctrl_t *ctrl, idx_t nvtxs, idx_t ncon, idx_t *xadj, + idx_t *adjncy, idx_t *vwgt, idx_t *vsize, idx_t *adjwgt) +{ + idx_t i, j, k, sum; + real_t *nvwgt; + graph_t *graph; + + /* allocate the graph and fill in the fields */ + graph = CreateGraph(); + + graph->nvtxs = nvtxs; + graph->nedges = xadj[nvtxs]; + graph->ncon = ncon; + + graph->xadj = xadj; + graph->free_xadj = 0; + + graph->adjncy = adjncy; + graph->free_adjncy = 0; + + + /* setup the vertex weights */ + if (vwgt) { + graph->vwgt = vwgt; + graph->free_vwgt = 0; + } + else { + vwgt = graph->vwgt = ismalloc(ncon*nvtxs, 1, "SetupGraph: vwgt"); + } + + graph->tvwgt = imalloc(ncon, "SetupGraph: tvwgts"); + graph->invtvwgt = rmalloc(ncon, "SetupGraph: invtvwgts"); + for (i=0; itvwgt[i] = isum(nvtxs, vwgt+i, ncon); + graph->invtvwgt[i] = 1.0/(graph->tvwgt[i] > 0 ? graph->tvwgt[i] : 1); + } + + + if (ctrl->objtype == METIS_OBJTYPE_VOL) { + /* Setup the vsize */ + if (vsize) { + graph->vsize = vsize; + graph->free_vsize = 0; + } + else { + vsize = graph->vsize = ismalloc(nvtxs, 1, "SetupGraph: vsize"); + } + + /* Allocate memory for edge weights and initialize them to the sum of the vsize */ + adjwgt = graph->adjwgt = imalloc(graph->nedges, "SetupGraph: adjwgt"); + for (i=0; iadjwgt = adjwgt; + graph->free_adjwgt = 0; + } + else { + adjwgt = graph->adjwgt = ismalloc(graph->nedges, 1, "SetupGraph: adjwgt"); + } + } + + + /* setup various derived info */ + SetupGraph_tvwgt(graph); + + if (ctrl->optype == METIS_OP_PMETIS || ctrl->optype == METIS_OP_OMETIS) + SetupGraph_label(graph); + + ASSERT(CheckGraph(graph, ctrl->numflag, 1)); + + return graph; +} + + +/*************************************************************************/ +/*! Set's up the tvwgt/invtvwgt info */ +/*************************************************************************/ +void SetupGraph_tvwgt(graph_t *graph) +{ + idx_t i; + + if (graph->tvwgt == NULL) + graph->tvwgt = imalloc(graph->ncon, "SetupGraph_tvwgt: tvwgt"); + if (graph->invtvwgt == NULL) + graph->invtvwgt = rmalloc(graph->ncon, "SetupGraph_tvwgt: invtvwgt"); + + for (i=0; incon; i++) { + graph->tvwgt[i] = isum(graph->nvtxs, graph->vwgt+i, graph->ncon); + graph->invtvwgt[i] = 1.0/(graph->tvwgt[i] > 0 ? graph->tvwgt[i] : 1); + } +} + + +/*************************************************************************/ +/*! Set's up the label info */ +/*************************************************************************/ +void SetupGraph_label(graph_t *graph) +{ + idx_t i; + + if (graph->label == NULL) + graph->label = imalloc(graph->nvtxs, "SetupGraph_label: label"); + + for (i=0; invtxs; i++) + graph->label[i] = i; +} + + +/*************************************************************************/ +/*! Setup the various arrays for the splitted graph */ +/*************************************************************************/ +graph_t *SetupSplitGraph(graph_t *graph, idx_t snvtxs, idx_t snedges) +{ + graph_t *sgraph; + + sgraph = CreateGraph(); + + sgraph->nvtxs = snvtxs; + sgraph->nedges = snedges; + sgraph->ncon = graph->ncon; + + /* Allocate memory for the splitted graph */ + sgraph->xadj = imalloc(snvtxs+1, "SetupSplitGraph: xadj"); + sgraph->vwgt = imalloc(sgraph->ncon*snvtxs, "SetupSplitGraph: vwgt"); + sgraph->adjncy = imalloc(snedges, "SetupSplitGraph: adjncy"); + sgraph->adjwgt = imalloc(snedges, "SetupSplitGraph: adjwgt"); + sgraph->label = imalloc(snvtxs, "SetupSplitGraph: label"); + sgraph->tvwgt = imalloc(sgraph->ncon, "SetupSplitGraph: tvwgt"); + sgraph->invtvwgt = rmalloc(sgraph->ncon, "SetupSplitGraph: invtvwgt"); + + if (graph->vsize) + sgraph->vsize = imalloc(snvtxs, "SetupSplitGraph: vsize"); + + return sgraph; +} + + +/*************************************************************************/ +/*! This function creates and initializes a graph_t data structure */ +/*************************************************************************/ +graph_t *CreateGraph(void) +{ + graph_t *graph; + + graph = (graph_t *)gk_malloc(sizeof(graph_t), "CreateGraph: graph"); + + InitGraph(graph); + + return graph; +} + + +/*************************************************************************/ +/*! This function initializes a graph_t data structure */ +/*************************************************************************/ +void InitGraph(graph_t *graph) +{ + memset((void *)graph, 0, sizeof(graph_t)); + + /* graph size constants */ + graph->nvtxs = -1; + graph->nedges = -1; + graph->ncon = -1; + graph->mincut = -1; + graph->minvol = -1; + graph->nbnd = -1; + + /* memory for the graph structure */ + graph->xadj = NULL; + graph->vwgt = NULL; + graph->vsize = NULL; + graph->adjncy = NULL; + graph->adjwgt = NULL; + graph->label = NULL; + graph->cmap = NULL; + graph->tvwgt = NULL; + graph->invtvwgt = NULL; + + /* by default these are set to true, but the can be explicitly changed afterwards */ + graph->free_xadj = 1; + graph->free_vwgt = 1; + graph->free_vsize = 1; + graph->free_adjncy = 1; + graph->free_adjwgt = 1; + + + /* memory for the partition/refinement structure */ + graph->where = NULL; + graph->pwgts = NULL; + graph->id = NULL; + graph->ed = NULL; + graph->bndptr = NULL; + graph->bndind = NULL; + graph->nrinfo = NULL; + graph->ckrinfo = NULL; + graph->vkrinfo = NULL; + + /* linked-list structure */ + graph->coarser = NULL; + graph->finer = NULL; +} + + +/*************************************************************************/ +/*! This function frees the refinement/partition memory stored in a graph */ +/*************************************************************************/ +void FreeRData(graph_t *graph) +{ + + /* The following is for the -minconn and -contig to work properly in + the vol-refinement routines */ + if ((void *)graph->ckrinfo == (void *)graph->vkrinfo) + graph->ckrinfo = NULL; + + + /* free partition/refinement structure */ + gk_free((void **)&graph->where, &graph->pwgts, &graph->id, &graph->ed, + &graph->bndptr, &graph->bndind, &graph->nrinfo, &graph->ckrinfo, + &graph->vkrinfo, LTERM); +} + + +/*************************************************************************/ +/*! This function deallocates any memory stored in a graph */ +/*************************************************************************/ +void FreeGraph(graph_t **r_graph) +{ + graph_t *graph; + + graph = *r_graph; + + /* free graph structure */ + if (graph->free_xadj) + gk_free((void **)&graph->xadj, LTERM); + if (graph->free_vwgt) + gk_free((void **)&graph->vwgt, LTERM); + if (graph->free_vsize) + gk_free((void **)&graph->vsize, LTERM); + if (graph->free_adjncy) + gk_free((void **)&graph->adjncy, LTERM); + if (graph->free_adjwgt) + gk_free((void **)&graph->adjwgt, LTERM); + + /* free partition/refinement structure */ + FreeRData(graph); + + gk_free((void **)&graph->tvwgt, &graph->invtvwgt, &graph->label, + &graph->cmap, &graph, LTERM); + + *r_graph = NULL; +} + + diff --git a/src/metis/libmetis/initpart.c b/src/metis/libmetis/initpart.c new file mode 100644 index 0000000..2f6c81b --- /dev/null +++ b/src/metis/libmetis/initpart.c @@ -0,0 +1,630 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * initpart.c + * + * This file contains code that performs the initial partition of the + * coarsest graph + * + * Started 7/23/97 + * George + * + */ + +#include "metislib.h" + +/*************************************************************************/ +/*! This function computes the initial bisection of the coarsest graph */ +/*************************************************************************/ +void Init2WayPartition(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, + idx_t niparts) +{ + mdbglvl_et dbglvl; + + ASSERT(graph->tvwgt[0] >= 0); + + dbglvl = ctrl->dbglvl; + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, ctrl->dbglvl -= METIS_DBG_REFINE); + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, ctrl->dbglvl -= METIS_DBG_MOVEINFO); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); + + switch (ctrl->iptype) { + case METIS_IPTYPE_RANDOM: + if (graph->ncon == 1) + RandomBisection(ctrl, graph, ntpwgts, niparts); + else + McRandomBisection(ctrl, graph, ntpwgts, niparts); + break; + + case METIS_IPTYPE_GROW: + if (graph->nedges == 0) + if (graph->ncon == 1) + RandomBisection(ctrl, graph, ntpwgts, niparts); + else + McRandomBisection(ctrl, graph, ntpwgts, niparts); + else + if (graph->ncon == 1) + GrowBisection(ctrl, graph, ntpwgts, niparts); + else + McGrowBisection(ctrl, graph, ntpwgts, niparts); + break; + + default: + gk_errexit(SIGERR, "Unknown initial partition type: %d\n", ctrl->iptype); + } + + IFSET(ctrl->dbglvl, METIS_DBG_IPART, printf("Initial Cut: %"PRIDX"\n", graph->mincut)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); + ctrl->dbglvl = dbglvl; + +} + + +/*************************************************************************/ +/*! This function computes the initial separator of the coarsest graph */ +/*************************************************************************/ +void InitSeparator(ctrl_t *ctrl, graph_t *graph, idx_t niparts) +{ + real_t ntpwgts[2] = {0.5, 0.5}; + mdbglvl_et dbglvl; + + dbglvl = ctrl->dbglvl; + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, ctrl->dbglvl -= METIS_DBG_REFINE); + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, ctrl->dbglvl -= METIS_DBG_MOVEINFO); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); + + /* this is required for the cut-based part of the refinement */ + Setup2WayBalMultipliers(ctrl, graph, ntpwgts); + + switch (ctrl->iptype) { + case METIS_IPTYPE_EDGE: + if (graph->nedges == 0) + RandomBisection(ctrl, graph, ntpwgts, niparts); + else + GrowBisection(ctrl, graph, ntpwgts, niparts); + + Compute2WayPartitionParams(ctrl, graph); + ConstructSeparator(ctrl, graph); + break; + + case METIS_IPTYPE_NODE: + GrowBisectionNode(ctrl, graph, ntpwgts, niparts); + break; + + default: + gk_errexit(SIGERR, "Unkown iptype of %"PRIDX"\n", ctrl->iptype); + } + + IFSET(ctrl->dbglvl, METIS_DBG_IPART, printf("Initial Sep: %"PRIDX"\n", graph->mincut)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); + + ctrl->dbglvl = dbglvl; + +} + + +/*************************************************************************/ +/*! This function computes a bisection of a graph by randomly assigning + the vertices followed by a bisection refinement. + The resulting partition is returned in graph->where. +*/ +/*************************************************************************/ +void RandomBisection(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, + idx_t niparts) +{ + idx_t i, ii, j, k, nvtxs, pwgts[2], zeromaxpwgt, from, me, + bestcut=0, icut, mincut, inbfs; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *where; + idx_t *perm, *bestwhere; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + Allocate2WayPartitionMemory(ctrl, graph); + where = graph->where; + + bestwhere = iwspacemalloc(ctrl, nvtxs); + perm = iwspacemalloc(ctrl, nvtxs); + + zeromaxpwgt = ctrl->ubfactors[0]*graph->tvwgt[0]*ntpwgts[0]; + + for (inbfs=0; inbfs 0) { + irandArrayPermute(nvtxs, perm, nvtxs/2, 1); + pwgts[1] = graph->tvwgt[0]; + pwgts[0] = 0; + + for (ii=0; ii zeromaxpwgt) + break; + } + } + } + + /* Do some partition refinement */ + Compute2WayPartitionParams(ctrl, graph); + /* printf("IPART: %3"PRIDX" [%5"PRIDX" %5"PRIDX"] [%5"PRIDX" %5"PRIDX"] %5"PRIDX"\n", graph->nvtxs, pwgts[0], pwgts[1], graph->pwgts[0], graph->pwgts[1], graph->mincut); */ + + Balance2Way(ctrl, graph, ntpwgts); + /* printf("BPART: [%5"PRIDX" %5"PRIDX"] %5"PRIDX"\n", graph->pwgts[0], graph->pwgts[1], graph->mincut); */ + + FM_2WayRefine(ctrl, graph, ntpwgts, 4); + /* printf("RPART: [%5"PRIDX" %5"PRIDX"] %5"PRIDX"\n", graph->pwgts[0], graph->pwgts[1], graph->mincut); */ + + if (inbfs==0 || bestcut > graph->mincut) { + bestcut = graph->mincut; + icopy(nvtxs, where, bestwhere); + if (bestcut == 0) + break; + } + } + + graph->mincut = bestcut; + icopy(nvtxs, bestwhere, where); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function takes a graph and produces a bisection by using a region + growing algorithm. The resulting bisection is refined using FM. + The resulting partition is returned in graph->where. +*/ +/*************************************************************************/ +void GrowBisection(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, + idx_t niparts) +{ + idx_t i, j, k, nvtxs, drain, nleft, first, last, + pwgts[2], oneminpwgt, onemaxpwgt, + from, me, bestcut=0, icut, mincut, inbfs; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *where; + idx_t *queue, *touched, *gain, *bestwhere; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + Allocate2WayPartitionMemory(ctrl, graph); + where = graph->where; + + bestwhere = iwspacemalloc(ctrl, nvtxs); + queue = iwspacemalloc(ctrl, nvtxs); + touched = iwspacemalloc(ctrl, nvtxs); + + onemaxpwgt = ctrl->ubfactors[0]*graph->tvwgt[0]*ntpwgts[1]; + oneminpwgt = (1.0/ctrl->ubfactors[0])*graph->tvwgt[0]*ntpwgts[1]; + + for (inbfs=0; inbfstvwgt[0]; + pwgts[0] = 0; + + + queue[0] = irandInRange(nvtxs); + touched[queue[0]] = 1; + first = 0; + last = 1; + nleft = nvtxs-1; + drain = 0; + + /* Start the BFS from queue to get a partition */ + for (;;) { + if (first == last) { /* Empty. Disconnected graph! */ + if (nleft == 0 || drain) + break; + + k = irandInRange(nleft); + for (i=0; i 0 && pwgts[1]-vwgt[i] < oneminpwgt) { + drain = 1; + continue; + } + + where[i] = 0; + INC_DEC(pwgts[0], pwgts[1], vwgt[i]); + if (pwgts[1] <= onemaxpwgt) + break; + + drain = 0; + for (j=xadj[i]; jnvtxs, pwgts[0], pwgts[1], graph->pwgts[0], graph->pwgts[1], graph->mincut); + */ + + Balance2Way(ctrl, graph, ntpwgts); + /* + printf("BPART: [%5"PRIDX" %5"PRIDX"] %5"PRIDX"\n", graph->pwgts[0], + graph->pwgts[1], graph->mincut); + */ + + FM_2WayRefine(ctrl, graph, ntpwgts, ctrl->niter); + /* + printf("RPART: [%5"PRIDX" %5"PRIDX"] %5"PRIDX"\n", graph->pwgts[0], + graph->pwgts[1], graph->mincut); + */ + + if (inbfs == 0 || bestcut > graph->mincut) { + bestcut = graph->mincut; + icopy(nvtxs, where, bestwhere); + if (bestcut == 0) + break; + } + } + + graph->mincut = bestcut; + icopy(nvtxs, bestwhere, where); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function takes a multi-constraint graph and computes a bisection + by randomly assigning the vertices and then refining it. The resulting + partition is returned in graph->where. +*/ +/**************************************************************************/ +void McRandomBisection(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, + idx_t niparts) +{ + idx_t i, ii, j, k, nvtxs, ncon, from, bestcut=0, mincut, inbfs, qnum; + idx_t *bestwhere, *where, *perm, *counts; + idx_t *vwgt; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + vwgt = graph->vwgt; + + Allocate2WayPartitionMemory(ctrl, graph); + where = graph->where; + + bestwhere = iwspacemalloc(ctrl, nvtxs); + perm = iwspacemalloc(ctrl, nvtxs); + counts = iwspacemalloc(ctrl, ncon); + + for (inbfs=0; inbfs<2*niparts; inbfs++) { + irandArrayPermute(nvtxs, perm, nvtxs/2, 1); + iset(ncon, 0, counts); + + /* partition by spliting the queues randomly */ + for (ii=0; iiniter); + Balance2Way(ctrl, graph, ntpwgts); + FM_2WayRefine(ctrl, graph, ntpwgts, ctrl->niter); + Balance2Way(ctrl, graph, ntpwgts); + FM_2WayRefine(ctrl, graph, ntpwgts, ctrl->niter); + + if (inbfs == 0 || bestcut >= graph->mincut) { + bestcut = graph->mincut; + icopy(nvtxs, where, bestwhere); + if (bestcut == 0) + break; + } + } + + graph->mincut = bestcut; + icopy(nvtxs, bestwhere, where); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function takes a multi-constraint graph and produces a bisection + by using a region growing algorithm. The resulting partition is + returned in graph->where. +*/ +/*************************************************************************/ +void McGrowBisection(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, + idx_t niparts) +{ + idx_t i, j, k, nvtxs, ncon, from, bestcut=0, mincut, inbfs; + idx_t *bestwhere, *where; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + + Allocate2WayPartitionMemory(ctrl, graph); + where = graph->where; + + bestwhere = iwspacemalloc(ctrl, nvtxs); + + for (inbfs=0; inbfs<2*niparts; inbfs++) { + iset(nvtxs, 1, where); + where[irandInRange(nvtxs)] = 0; + + Compute2WayPartitionParams(ctrl, graph); + + Balance2Way(ctrl, graph, ntpwgts); + FM_2WayRefine(ctrl, graph, ntpwgts, ctrl->niter); + Balance2Way(ctrl, graph, ntpwgts); + FM_2WayRefine(ctrl, graph, ntpwgts, ctrl->niter); + + if (inbfs == 0 || bestcut >= graph->mincut) { + bestcut = graph->mincut; + icopy(nvtxs, where, bestwhere); + if (bestcut == 0) + break; + } + } + + graph->mincut = bestcut; + icopy(nvtxs, bestwhere, where); + + WCOREPOP; +} + + +/*************************************************************************/ +/* This function takes a graph and produces a tri-section into left, right, + and separator using a region growing algorithm. The resulting separator + is refined using node FM. + The resulting partition is returned in graph->where. +*/ +/**************************************************************************/ +void GrowBisectionNode(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, + idx_t niparts) +{ + idx_t i, j, k, nvtxs, drain, nleft, first, last, pwgts[2], oneminpwgt, + onemaxpwgt, from, me, bestcut=0, icut, mincut, inbfs; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *where, *bndind; + idx_t *queue, *touched, *gain, *bestwhere; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + bestwhere = iwspacemalloc(ctrl, nvtxs); + queue = iwspacemalloc(ctrl, nvtxs); + touched = iwspacemalloc(ctrl, nvtxs); + + onemaxpwgt = ctrl->ubfactors[0]*graph->tvwgt[0]*0.5; + oneminpwgt = (1.0/ctrl->ubfactors[0])*graph->tvwgt[0]*0.5; + + + /* Allocate refinement memory. Allocate sufficient memory for both edge and node */ + graph->pwgts = imalloc(3, "GrowBisectionNode: pwgts"); + graph->where = imalloc(nvtxs, "GrowBisectionNode: where"); + graph->bndptr = imalloc(nvtxs, "GrowBisectionNode: bndptr"); + graph->bndind = imalloc(nvtxs, "GrowBisectionNode: bndind"); + graph->id = imalloc(nvtxs, "GrowBisectionNode: id"); + graph->ed = imalloc(nvtxs, "GrowBisectionNode: ed"); + graph->nrinfo = (nrinfo_t *)gk_malloc(nvtxs*sizeof(nrinfo_t), "GrowBisectionNode: nrinfo"); + + where = graph->where; + bndind = graph->bndind; + + for (inbfs=0; inbfstvwgt[0]; + pwgts[0] = 0; + + queue[0] = irandInRange(nvtxs); + touched[queue[0]] = 1; + first = 0; last = 1; + nleft = nvtxs-1; + drain = 0; + + /* Start the BFS from queue to get a partition */ + for (;;) { + if (first == last) { /* Empty. Disconnected graph! */ + if (nleft == 0 || drain) + break; + + k = irandInRange(nleft); + for (i=0; inbnd; i++) { + j = bndind[i]; + if (xadj[j+1]-xadj[j] > 0) /* ignore islands */ + where[j] = 2; + } + + Compute2WayNodePartitionParams(ctrl, graph); + FM_2WayNodeRefine2Sided(ctrl, graph, 1); + FM_2WayNodeRefine1Sided(ctrl, graph, 4); + + /* + printf("ISep: [%"PRIDX" %"PRIDX" %"PRIDX" %"PRIDX"] %"PRIDX"\n", + inbfs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2], bestcut); + */ + + if (inbfs == 0 || bestcut > graph->mincut) { + bestcut = graph->mincut; + icopy(nvtxs, where, bestwhere); + } + } + + graph->mincut = bestcut; + icopy(nvtxs, bestwhere, where); + + WCOREPOP; +} + + +/*************************************************************************/ +/* This function takes a graph and produces a tri-section into left, right, + and separator using a region growing algorithm. The resulting separator + is refined using node FM. + The resulting partition is returned in graph->where. +*/ +/**************************************************************************/ +void GrowBisectionNode2(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, + idx_t niparts) +{ + idx_t i, j, k, nvtxs, bestcut=0, mincut, inbfs; + idx_t *xadj, *where, *bndind, *bestwhere; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + + /* Allocate refinement memory. Allocate sufficient memory for both edge and node */ + graph->pwgts = imalloc(3, "GrowBisectionNode: pwgts"); + graph->where = imalloc(nvtxs, "GrowBisectionNode: where"); + graph->bndptr = imalloc(nvtxs, "GrowBisectionNode: bndptr"); + graph->bndind = imalloc(nvtxs, "GrowBisectionNode: bndind"); + graph->id = imalloc(nvtxs, "GrowBisectionNode: id"); + graph->ed = imalloc(nvtxs, "GrowBisectionNode: ed"); + graph->nrinfo = (nrinfo_t *)gk_malloc(nvtxs*sizeof(nrinfo_t), "GrowBisectionNode: nrinfo"); + + bestwhere = iwspacemalloc(ctrl, nvtxs); + + where = graph->where; + bndind = graph->bndind; + + for (inbfs=0; inbfs 0) + where[irandInRange(nvtxs)] = 0; + + Compute2WayPartitionParams(ctrl, graph); + General2WayBalance(ctrl, graph, ntpwgts); + FM_2WayRefine(ctrl, graph, ntpwgts, ctrl->niter); + + /* Construct and refine the vertex separator */ + for (i=0; inbnd; i++) { + j = bndind[i]; + if (xadj[j+1]-xadj[j] > 0) /* ignore islands */ + where[j] = 2; + } + + Compute2WayNodePartitionParams(ctrl, graph); + FM_2WayNodeRefine2Sided(ctrl, graph, 4); + + /* + printf("ISep: [%"PRIDX" %"PRIDX" %"PRIDX" %"PRIDX"] %"PRIDX"\n", + inbfs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2], bestcut); + */ + + if (inbfs == 0 || bestcut > graph->mincut) { + bestcut = graph->mincut; + icopy(nvtxs, where, bestwhere); + } + } + + graph->mincut = bestcut; + icopy(nvtxs, bestwhere, where); + + WCOREPOP; +} + diff --git a/src/metis/libmetis/kmetis.c b/src/metis/libmetis/kmetis.c new file mode 100644 index 0000000..cb6d1af --- /dev/null +++ b/src/metis/libmetis/kmetis.c @@ -0,0 +1,243 @@ +/*! +\file +\brief The top-level routines for multilevel k-way partitioning that minimizes + the edge cut. + +\date Started 7/28/1997 +\author George +\author Copyright 1997-2011, Regents of the University of Minnesota +\version\verbatim $Id: kmetis.c 13905 2013-03-25 13:21:20Z karypis $ \endverbatim +*/ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function is the entry point for MCKMETIS */ +/*************************************************************************/ +int METIS_PartGraphKway(idx_t *nvtxs, idx_t *ncon, idx_t *xadj, idx_t *adjncy, + idx_t *vwgt, idx_t *vsize, idx_t *adjwgt, idx_t *nparts, + real_t *tpwgts, real_t *ubvec, idx_t *options, idx_t *objval, + idx_t *part) +{ + int sigrval=0, renumber=0; + graph_t *graph; + ctrl_t *ctrl; + + /* set up malloc cleaning code and signal catchers */ + if (!gk_malloc_init()) + return METIS_ERROR_MEMORY; + + gk_sigtrap(); + + if ((sigrval = gk_sigcatch()) != 0) + goto SIGTHROW; + + + /* set up the run parameters */ + ctrl = SetupCtrl(METIS_OP_KMETIS, options, *ncon, *nparts, tpwgts, ubvec); + if (!ctrl) { + gk_siguntrap(); + return METIS_ERROR_INPUT; + } + + /* if required, change the numbering to 0 */ + if (ctrl->numflag == 1) { + Change2CNumbering(*nvtxs, xadj, adjncy); + renumber = 1; + } + + /* set up the graph */ + graph = SetupGraph(ctrl, *nvtxs, *ncon, xadj, adjncy, vwgt, vsize, adjwgt); + + /* set up multipliers for making balance computations easier */ + SetupKWayBalMultipliers(ctrl, graph); + + /* set various run parameters that depend on the graph */ + ctrl->CoarsenTo = gk_max((*nvtxs)/(20*gk_log2(*nparts)), 30*(*nparts)); + ctrl->nIparts = (ctrl->CoarsenTo == 30*(*nparts) ? 4 : 5); + + /* take care contiguity requests for disconnected graphs */ + if (ctrl->contig && !IsConnected(graph, 0)) + gk_errexit(SIGERR, "METIS Error: A contiguous partition is requested for a non-contiguous input graph.\n"); + + /* allocate workspace memory */ + AllocateWorkSpace(ctrl, graph); + + /* start the partitioning */ + IFSET(ctrl->dbglvl, METIS_DBG_TIME, InitTimers(ctrl)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->TotalTmr)); + + *objval = MlevelKWayPartitioning(ctrl, graph, part); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->TotalTmr)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, PrintTimers(ctrl)); + + /* clean up */ + FreeCtrl(&ctrl); + +SIGTHROW: + /* if required, change the numbering back to 1 */ + if (renumber) + Change2FNumbering(*nvtxs, xadj, adjncy, part); + + gk_siguntrap(); + gk_malloc_cleanup(0); + + return metis_rcode(sigrval); +} + + +/*************************************************************************/ +/*! This function computes a k-way partitioning of a graph that minimizes + the specified objective function. + + \param ctrl is the control structure + \param graph is the graph to be partitioned + \param part is the vector that on return will store the partitioning + + \returns the objective value of the partitoning. The partitioning + itself is stored in the part vector. +*/ +/*************************************************************************/ +idx_t MlevelKWayPartitioning(ctrl_t *ctrl, graph_t *graph, idx_t *part) +{ + idx_t i, j, objval=0, curobj=0, bestobj=0; + real_t curbal=0.0, bestbal=0.0; + graph_t *cgraph; + int status; + + + for (i=0; incuts; i++) { + cgraph = CoarsenGraph(ctrl, graph); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); + AllocateKWayPartitionMemory(ctrl, cgraph); + + /* Release the work space */ + FreeWorkSpace(ctrl); + + /* Compute the initial partitioning */ + InitKWayPartitioning(ctrl, cgraph); + + /* Re-allocate the work space */ + AllocateWorkSpace(ctrl, graph); + AllocateRefinementWorkSpace(ctrl, 2*cgraph->nedges); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); + IFSET(ctrl->dbglvl, METIS_DBG_IPART, + printf("Initial %"PRIDX"-way partitioning cut: %"PRIDX"\n", ctrl->nparts, objval)); + + RefineKWay(ctrl, graph, cgraph); + + switch (ctrl->objtype) { + case METIS_OBJTYPE_CUT: + curobj = graph->mincut; + break; + + case METIS_OBJTYPE_VOL: + curobj = graph->minvol; + break; + + default: + gk_errexit(SIGERR, "Unknown objtype: %d\n", ctrl->objtype); + } + + curbal = ComputeLoadImbalanceDiff(graph, ctrl->nparts, ctrl->pijbm, ctrl->ubfactors); + + if (i == 0 + || (curbal <= 0.0005 && bestobj > curobj) + || (bestbal > 0.0005 && curbal < bestbal)) { + icopy(graph->nvtxs, graph->where, part); + bestobj = curobj; + bestbal = curbal; + } + + FreeRData(graph); + + if (bestobj == 0) + break; + } + + FreeGraph(&graph); + + return bestobj; +} + + +/*************************************************************************/ +/*! This function computes the initial k-way partitioning using PMETIS +*/ +/*************************************************************************/ +void InitKWayPartitioning(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, ntrials, options[METIS_NOPTIONS], curobj=0, bestobj=0; + idx_t *bestwhere=NULL; + real_t *ubvec=NULL; + int status; + + METIS_SetDefaultOptions(options); + options[METIS_OPTION_NITER] = 10; + options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_CUT; + options[METIS_OPTION_NO2HOP] = ctrl->no2hop; + + + ubvec = rmalloc(graph->ncon, "InitKWayPartitioning: ubvec"); + for (i=0; incon; i++) + ubvec[i] = (real_t)pow(ctrl->ubfactors[i], 1.0/log(ctrl->nparts)); + + + switch (ctrl->objtype) { + case METIS_OBJTYPE_CUT: + case METIS_OBJTYPE_VOL: + options[METIS_OPTION_NCUTS] = ctrl->nIparts; + status = METIS_PartGraphRecursive(&graph->nvtxs, &graph->ncon, + graph->xadj, graph->adjncy, graph->vwgt, graph->vsize, + graph->adjwgt, &ctrl->nparts, ctrl->tpwgts, ubvec, + options, &curobj, graph->where); + + if (status != METIS_OK) + gk_errexit(SIGERR, "Failed during initial partitioning\n"); + + break; + +#ifdef XXX /* This does not seem to help */ + case METIS_OBJTYPE_VOL: + bestwhere = imalloc(graph->nvtxs, "InitKWayPartitioning: bestwhere"); + options[METIS_OPTION_NCUTS] = 2; + + ntrials = (ctrl->nIparts+1)/2; + for (i=0; invtxs, &graph->ncon, + graph->xadj, graph->adjncy, graph->vwgt, graph->vsize, + graph->adjwgt, &ctrl->nparts, ctrl->tpwgts, ubvec, + options, &curobj, graph->where); + if (status != METIS_OK) + gk_errexit(SIGERR, "Failed during initial partitioning\n"); + + curobj = ComputeVolume(graph, graph->where); + + if (i == 0 || bestobj > curobj) { + bestobj = curobj; + if (i < ntrials-1) + icopy(graph->nvtxs, graph->where, bestwhere); + } + + if (bestobj == 0) + break; + } + if (bestobj != curobj) + icopy(graph->nvtxs, bestwhere, graph->where); + + break; +#endif + + default: + gk_errexit(SIGERR, "Unknown objtype: %d\n", ctrl->objtype); + } + + gk_free((void **)&ubvec, &bestwhere, LTERM); + +} + + diff --git a/src/metis/libmetis/kwayfm.c b/src/metis/libmetis/kwayfm.c new file mode 100644 index 0000000..dedfd39 --- /dev/null +++ b/src/metis/libmetis/kwayfm.c @@ -0,0 +1,1852 @@ +/*! +\file +\brief Routines for k-way refinement + +\date Started 7/28/97 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version $Id: kwayfm.c 10567 2011-07-13 16:17:07Z karypis $ +*/ + +#include "metislib.h" + + + +/*************************************************************************/ +/* Top-level routine for k-way partitioning refinement. This routine just + calls the appropriate refinement routine based on the objectives and + constraints. */ +/*************************************************************************/ +void Greedy_KWayOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode) +{ + switch (ctrl->objtype) { + case METIS_OBJTYPE_CUT: + if (graph->ncon == 1) + Greedy_KWayCutOptimize(ctrl, graph, niter, ffactor, omode); + else + Greedy_McKWayCutOptimize(ctrl, graph, niter, ffactor, omode); + break; + + case METIS_OBJTYPE_VOL: + if (graph->ncon == 1) + Greedy_KWayVolOptimize(ctrl, graph, niter, ffactor, omode); + else + Greedy_McKWayVolOptimize(ctrl, graph, niter, ffactor, omode); + break; + + default: + gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype); + } +} + + +/*************************************************************************/ +/*! K-way partitioning optimization in which the vertices are visited in + decreasing ed/sqrt(nnbrs)-id order. Note this is just an + approximation, as the ed is often split across different subdomains + and the sqrt(nnbrs) is just a crude approximation. + + \param graph is the graph that is being refined. + \param niter is the number of refinement iterations. + \param ffactor is the \em fudge-factor for allowing positive gain moves + to violate the max-pwgt constraint. + \param omode is the type of optimization that will performed among + OMODE_REFINE and OMODE_BALANCE + + +*/ +/**************************************************************************/ +void Greedy_KWayCutOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode) +{ + /* Common variables to all types of kway-refinement/balancing routines */ + idx_t i, ii, iii, j, k, l, pass, nvtxs, nparts, gain; + idx_t from, me, to, oldcut, vwgt; + idx_t *xadj, *adjncy, *adjwgt; + idx_t *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts; + idx_t nmoved, nupd, *vstatus, *updptr, *updind; + idx_t maxndoms, *safetos=NULL, *nads=NULL, *doms=NULL, **adids=NULL, **adwgts=NULL; + idx_t *bfslvl=NULL, *bfsind=NULL, *bfsmrk=NULL; + idx_t bndtype = (omode == OMODE_REFINE ? BNDTYPE_REFINE : BNDTYPE_BALANCE); + + /* Edgecut-specific/different variables */ + idx_t nbnd, oldnnbrs; + rpq_t *queue; + real_t rgain; + ckrinfo_t *myrinfo; + cnbr_t *mynbrs; + + WCOREPUSH; + + /* Link the graph fields */ + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + bndind = graph->bndind; + bndptr = graph->bndptr; + + where = graph->where; + pwgts = graph->pwgts; + + nparts = ctrl->nparts; + + /* Setup the weight intervals of the various subdomains */ + minwgt = iwspacemalloc(ctrl, nparts); + maxwgt = iwspacemalloc(ctrl, nparts); + itpwgts = iwspacemalloc(ctrl, nparts); + + for (i=0; itpwgts[i]*graph->tvwgt[0]; + maxwgt[i] = ctrl->tpwgts[i]*graph->tvwgt[0]*ctrl->ubfactors[0]; + minwgt[i] = ctrl->tpwgts[i]*graph->tvwgt[0]*(1.0/ctrl->ubfactors[0]); + } + + perm = iwspacemalloc(ctrl, nvtxs); + + + /* This stores the valid target subdomains. It is used when ctrl->minconn to + control the subdomains to which moves are allowed to be made. + When ctrl->minconn is false, the default values of 2 allow all moves to + go through and it does not interfere with the zero-gain move selection. */ + safetos = iset(nparts, 2, iwspacemalloc(ctrl, nparts)); + + if (ctrl->minconn) { + ComputeSubDomainGraph(ctrl, graph); + + nads = ctrl->nads; + adids = ctrl->adids; + adwgts = ctrl->adwgts; + doms = iset(nparts, 0, ctrl->pvec1); + } + + + /* Setup updptr, updind like boundary info to keep track of the vertices whose + vstatus's need to be reset at the end of the inner iteration */ + vstatus = iset(nvtxs, VPQSTATUS_NOTPRESENT, iwspacemalloc(ctrl, nvtxs)); + updptr = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs)); + updind = iwspacemalloc(ctrl, nvtxs); + + if (ctrl->contig) { + /* The arrays that will be used for limited check of articulation points */ + bfslvl = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + bfsind = iwspacemalloc(ctrl, nvtxs); + bfsmrk = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + } + + if (ctrl->dbglvl&METIS_DBG_REFINE) { + printf("%s: [%6"PRIDX" %6"PRIDX"]-[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL"," + " Nv-Nb[%6"PRIDX" %6"PRIDX"], Cut: %6"PRIDX, + (omode == OMODE_REFINE ? "GRC" : "GBC"), + pwgts[iargmin(nparts, pwgts)], imax(nparts, pwgts), minwgt[0], maxwgt[0], + ComputeLoadImbalance(graph, nparts, ctrl->pijbm), + graph->nvtxs, graph->nbnd, graph->mincut); + if (ctrl->minconn) + printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1)); + printf("\n"); + } + + queue = rpqCreate(nvtxs); + + /*===================================================================== + * The top-level refinement loop + *======================================================================*/ + for (pass=0; passmincut); + + if (omode == OMODE_BALANCE) { + /* Check to see if things are out of balance, given the tolerance */ + for (i=0; i maxwgt[i]) + break; + } + if (i == nparts) /* Things are balanced. Return right away */ + break; + } + + oldcut = graph->mincut; + nbnd = graph->nbnd; + nupd = 0; + + if (ctrl->minconn) + maxndoms = imax(nparts, nads); + + /* Insert the boundary vertices in the priority queue */ + irandArrayPermute(nbnd, perm, nbnd/4, 1); + for (ii=0; iickrinfo[i].nnbrs > 0 ? + 1.0*graph->ckrinfo[i].ed/sqrt(graph->ckrinfo[i].nnbrs) : 0.0) + - graph->ckrinfo[i].id; + rpqInsert(queue, i, rgain); + vstatus[i] = VPQSTATUS_PRESENT; + ListInsert(nupd, updind, updptr, i); + } + + /* Start extracting vertices from the queue and try to move them */ + for (nmoved=0, iii=0;;iii++) { + if ((i = rpqGetTop(queue)) == -1) + break; + vstatus[i] = VPQSTATUS_EXTRACTED; + + myrinfo = graph->ckrinfo+i; + mynbrs = ctrl->cnbrpool + myrinfo->inbr; + + from = where[i]; + vwgt = graph->vwgt[i]; + + /* Prevent moves that make 'from' domain underbalanced */ + if (omode == OMODE_REFINE) { + if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from]) + continue; + } + else { /* OMODE_BALANCE */ + if (pwgts[from]-vwgt < minwgt[from]) + continue; + } + + if (ctrl->contig && IsArticulationNode(i, xadj, adjncy, where, bfslvl, bfsind, bfsmrk)) + continue; + + if (ctrl->minconn) + SelectSafeTargetSubdomains(myrinfo, mynbrs, nads, adids, maxndoms, safetos, doms); + + /* Find the most promising subdomain to move to */ + if (omode == OMODE_REFINE) { + for (k=myrinfo->nnbrs-1; k>=0; k--) { + if (!safetos[to=mynbrs[k].pid]) + continue; + gain = mynbrs[k].ed-myrinfo->id; + if (gain >= 0 && pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain) + break; + } + if (k < 0) + continue; /* break out if you did not find a candidate */ + + for (j=k-1; j>=0; j--) { + if (!safetos[to=mynbrs[j].pid]) + continue; + gain = mynbrs[j].ed-myrinfo->id; + if ((mynbrs[j].ed > mynbrs[k].ed && pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain) + || + (mynbrs[j].ed == mynbrs[k].ed && + itpwgts[mynbrs[k].pid]*pwgts[to] < itpwgts[to]*pwgts[mynbrs[k].pid])) + k = j; + } + + to = mynbrs[k].pid; + + gain = mynbrs[k].ed-myrinfo->id; + if (!(gain > 0 + || (gain == 0 + && (pwgts[from] >= maxwgt[from] + || itpwgts[to]*pwgts[from] > itpwgts[from]*(pwgts[to]+vwgt) + || (iii%2 == 0 && safetos[to] == 2) + ) + ) + ) + ) + continue; + } + else { /* OMODE_BALANCE */ + for (k=myrinfo->nnbrs-1; k>=0; k--) { + if (!safetos[to=mynbrs[k].pid]) + continue; + if (pwgts[to]+vwgt <= maxwgt[to] || + itpwgts[from]*(pwgts[to]+vwgt) <= itpwgts[to]*pwgts[from]) + break; + } + if (k < 0) + continue; /* break out if you did not find a candidate */ + + for (j=k-1; j>=0; j--) { + if (!safetos[to=mynbrs[j].pid]) + continue; + if (itpwgts[mynbrs[k].pid]*pwgts[to] < itpwgts[to]*pwgts[mynbrs[k].pid]) + k = j; + } + + to = mynbrs[k].pid; + + if (pwgts[from] < maxwgt[from] && pwgts[to] > minwgt[to] && + mynbrs[k].ed-myrinfo->id < 0) + continue; + } + + + + /*===================================================================== + * If we got here, we can now move the vertex from 'from' to 'to' + *======================================================================*/ + graph->mincut -= mynbrs[k].ed-myrinfo->id; + nmoved++; + + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, + printf("\t\tMoving %6"PRIDX" to %3"PRIDX". Gain: %4"PRIDX". Cut: %6"PRIDX"\n", + i, to, mynbrs[k].ed-myrinfo->id, graph->mincut)); + + /* Update the subdomain connectivity information */ + if (ctrl->minconn) { + /* take care of i's move itself */ + UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->id-mynbrs[k].ed, &maxndoms); + + /* take care of the adjancent vertices */ + for (j=xadj[i]; jckrinfo+ii; + + oldnnbrs = myrinfo->nnbrs; + + UpdateAdjacentVertexInfoAndBND(ctrl, ii, xadj[ii+1]-xadj[ii], me, + from, to, myrinfo, adjwgt[j], nbnd, bndptr, bndind, bndtype); + + UpdateQueueInfo(queue, vstatus, ii, me, from, to, myrinfo, oldnnbrs, + nupd, updptr, updind, bndtype); + + ASSERT(myrinfo->nnbrs <= xadj[ii+1]-xadj[ii]); + } + } + + graph->nbnd = nbnd; + + /* Reset the vstatus and associated data structures */ + for (i=0; idbglvl&METIS_DBG_REFINE) { + printf("\t[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL", Nb: %6"PRIDX"." + " Nmoves: %5"PRIDX", Cut: %6"PRIDX", Vol: %6"PRIDX, + pwgts[iargmin(nparts, pwgts)], imax(nparts, pwgts), + ComputeLoadImbalance(graph, nparts, ctrl->pijbm), + graph->nbnd, nmoved, graph->mincut, ComputeVolume(graph, where)); + if (ctrl->minconn) + printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1)); + printf("\n"); + } + + if (nmoved == 0 || (omode == OMODE_REFINE && graph->mincut == oldcut)) + break; + } + + rpqDestroy(queue); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! K-way refinement that minimizes the communication volume. This is a + greedy routine and the vertices are visited in decreasing gv order. + + \param graph is the graph that is being refined. + \param niter is the number of refinement iterations. + \param ffactor is the \em fudge-factor for allowing positive gain moves + to violate the max-pwgt constraint. + +*/ +/**************************************************************************/ +void Greedy_KWayVolOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode) +{ + /* Common variables to all types of kway-refinement/balancing routines */ + idx_t i, ii, iii, j, k, l, pass, nvtxs, nparts, gain; + idx_t from, me, to, oldcut, vwgt; + idx_t *xadj, *adjncy; + idx_t *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts; + idx_t nmoved, nupd, *vstatus, *updptr, *updind; + idx_t maxndoms, *safetos=NULL, *nads=NULL, *doms=NULL, **adids=NULL, **adwgts=NULL; + idx_t *bfslvl=NULL, *bfsind=NULL, *bfsmrk=NULL; + idx_t bndtype = (omode == OMODE_REFINE ? BNDTYPE_REFINE : BNDTYPE_BALANCE); + + /* Volume-specific/different variables */ + ipq_t *queue; + idx_t oldvol, xgain; + idx_t *vmarker, *pmarker, *modind; + vkrinfo_t *myrinfo; + vnbr_t *mynbrs; + + WCOREPUSH; + + /* Link the graph fields */ + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + bndptr = graph->bndptr; + bndind = graph->bndind; + where = graph->where; + pwgts = graph->pwgts; + + nparts = ctrl->nparts; + + /* Setup the weight intervals of the various subdomains */ + minwgt = iwspacemalloc(ctrl, nparts); + maxwgt = iwspacemalloc(ctrl, nparts); + itpwgts = iwspacemalloc(ctrl, nparts); + + for (i=0; itpwgts[i]*graph->tvwgt[0]; + maxwgt[i] = ctrl->tpwgts[i]*graph->tvwgt[0]*ctrl->ubfactors[0]; + minwgt[i] = ctrl->tpwgts[i]*graph->tvwgt[0]*(1.0/ctrl->ubfactors[0]); + } + + perm = iwspacemalloc(ctrl, nvtxs); + + + /* This stores the valid target subdomains. It is used when ctrl->minconn to + control the subdomains to which moves are allowed to be made. + When ctrl->minconn is false, the default values of 2 allow all moves to + go through and it does not interfere with the zero-gain move selection. */ + safetos = iset(nparts, 2, iwspacemalloc(ctrl, nparts)); + + if (ctrl->minconn) { + ComputeSubDomainGraph(ctrl, graph); + + nads = ctrl->nads; + adids = ctrl->adids; + adwgts = ctrl->adwgts; + doms = iset(nparts, 0, ctrl->pvec1); + } + + + /* Setup updptr, updind like boundary info to keep track of the vertices whose + vstatus's need to be reset at the end of the inner iteration */ + vstatus = iset(nvtxs, VPQSTATUS_NOTPRESENT, iwspacemalloc(ctrl, nvtxs)); + updptr = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs)); + updind = iwspacemalloc(ctrl, nvtxs); + + if (ctrl->contig) { + /* The arrays that will be used for limited check of articulation points */ + bfslvl = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + bfsind = iwspacemalloc(ctrl, nvtxs); + bfsmrk = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + } + + /* Vol-refinement specific working arrays */ + modind = iwspacemalloc(ctrl, nvtxs); + vmarker = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + pmarker = iset(nparts, -1, iwspacemalloc(ctrl, nparts)); + + if (ctrl->dbglvl&METIS_DBG_REFINE) { + printf("%s: [%6"PRIDX" %6"PRIDX"]-[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL + ", Nv-Nb[%6"PRIDX" %6"PRIDX"], Cut: %5"PRIDX", Vol: %5"PRIDX, + (omode == OMODE_REFINE ? "GRV" : "GBV"), + pwgts[iargmin(nparts, pwgts)], imax(nparts, pwgts), minwgt[0], maxwgt[0], + ComputeLoadImbalance(graph, nparts, ctrl->pijbm), + graph->nvtxs, graph->nbnd, graph->mincut, graph->minvol); + if (ctrl->minconn) + printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1)); + printf("\n"); + } + + queue = ipqCreate(nvtxs); + + + /*===================================================================== + * The top-level refinement loop + *======================================================================*/ + for (pass=0; passminvol); + + if (omode == OMODE_BALANCE) { + /* Check to see if things are out of balance, given the tolerance */ + for (i=0; i maxwgt[i]) + break; + } + if (i == nparts) /* Things are balanced. Return right away */ + break; + } + + oldcut = graph->mincut; + oldvol = graph->minvol; + nupd = 0; + + if (ctrl->minconn) + maxndoms = imax(nparts, nads); + + /* Insert the boundary vertices in the priority queue */ + irandArrayPermute(graph->nbnd, perm, graph->nbnd/4, 1); + for (ii=0; iinbnd; ii++) { + i = bndind[perm[ii]]; + ipqInsert(queue, i, graph->vkrinfo[i].gv); + vstatus[i] = VPQSTATUS_PRESENT; + ListInsert(nupd, updind, updptr, i); + } + + /* Start extracting vertices from the queue and try to move them */ + for (nmoved=0, iii=0;;iii++) { + if ((i = ipqGetTop(queue)) == -1) + break; + vstatus[i] = VPQSTATUS_EXTRACTED; + + myrinfo = graph->vkrinfo+i; + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + from = where[i]; + vwgt = graph->vwgt[i]; + + /* Prevent moves that make 'from' domain underbalanced */ + if (omode == OMODE_REFINE) { + if (myrinfo->nid > 0 && pwgts[from]-vwgt < minwgt[from]) + continue; + } + else { /* OMODE_BALANCE */ + if (pwgts[from]-vwgt < minwgt[from]) + continue; + } + + if (ctrl->contig && IsArticulationNode(i, xadj, adjncy, where, bfslvl, bfsind, bfsmrk)) + continue; + + if (ctrl->minconn) + SelectSafeTargetSubdomains(myrinfo, mynbrs, nads, adids, maxndoms, safetos, doms); + + xgain = (myrinfo->nid == 0 && myrinfo->ned > 0 ? graph->vsize[i] : 0); + + /* Find the most promising subdomain to move to */ + if (omode == OMODE_REFINE) { + for (k=myrinfo->nnbrs-1; k>=0; k--) { + if (!safetos[to=mynbrs[k].pid]) + continue; + gain = mynbrs[k].gv + xgain; + if (gain >= 0 && pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain) + break; + } + if (k < 0) + continue; /* break out if you did not find a candidate */ + + for (j=k-1; j>=0; j--) { + if (!safetos[to=mynbrs[j].pid]) + continue; + gain = mynbrs[j].gv + xgain; + if ((mynbrs[j].gv > mynbrs[k].gv && + pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain) + || + (mynbrs[j].gv == mynbrs[k].gv && + mynbrs[j].ned > mynbrs[k].ned && + pwgts[to]+vwgt <= maxwgt[to]) + || + (mynbrs[j].gv == mynbrs[k].gv && + mynbrs[j].ned == mynbrs[k].ned && + itpwgts[mynbrs[k].pid]*pwgts[to] < itpwgts[to]*pwgts[mynbrs[k].pid]) + ) + k = j; + } + to = mynbrs[k].pid; + + ASSERT(xgain+mynbrs[k].gv >= 0); + + j = 0; + if (xgain+mynbrs[k].gv > 0 || mynbrs[k].ned-myrinfo->nid > 0) + j = 1; + else if (mynbrs[k].ned-myrinfo->nid == 0) { + if ((iii%2 == 0 && safetos[to] == 2) || + pwgts[from] >= maxwgt[from] || + itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from]) + j = 1; + } + if (j == 0) + continue; + } + else { /* OMODE_BALANCE */ + for (k=myrinfo->nnbrs-1; k>=0; k--) { + if (!safetos[to=mynbrs[k].pid]) + continue; + if (pwgts[to]+vwgt <= maxwgt[to] || + itpwgts[from]*(pwgts[to]+vwgt) <= itpwgts[to]*pwgts[from]) + break; + } + if (k < 0) + continue; /* break out if you did not find a candidate */ + + for (j=k-1; j>=0; j--) { + if (!safetos[to=mynbrs[j].pid]) + continue; + if (itpwgts[mynbrs[k].pid]*pwgts[to] < itpwgts[to]*pwgts[mynbrs[k].pid]) + k = j; + } + to = mynbrs[k].pid; + + if (pwgts[from] < maxwgt[from] && pwgts[to] > minwgt[to] && + (xgain+mynbrs[k].gv < 0 || + (xgain+mynbrs[k].gv == 0 && mynbrs[k].ned-myrinfo->nid < 0)) + ) + continue; + } + + + /*===================================================================== + * If we got here, we can now move the vertex from 'from' to 'to' + *======================================================================*/ + INC_DEC(pwgts[to], pwgts[from], vwgt); + graph->mincut -= mynbrs[k].ned-myrinfo->nid; + graph->minvol -= (xgain+mynbrs[k].gv); + where[i] = to; + nmoved++; + + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, + printf("\t\tMoving %6"PRIDX" from %3"PRIDX" to %3"PRIDX". " + "Gain: [%4"PRIDX" %4"PRIDX"]. Cut: %6"PRIDX", Vol: %6"PRIDX"\n", + i, from, to, xgain+mynbrs[k].gv, mynbrs[k].ned-myrinfo->nid, + graph->mincut, graph->minvol)); + + /* Update the subdomain connectivity information */ + if (ctrl->minconn) { + /* take care of i's move itself */ + UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->nid-mynbrs[k].ned, &maxndoms); + + /* take care of the adjancent vertices */ + for (j=xadj[i]; jdbglvl&METIS_DBG_REFINE) { + printf("\t[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL", Nb: %6"PRIDX"." + " Nmoves: %5"PRIDX", Cut: %6"PRIDX", Vol: %6"PRIDX, + pwgts[iargmin(nparts, pwgts)], imax(nparts, pwgts), + ComputeLoadImbalance(graph, nparts, ctrl->pijbm), + graph->nbnd, nmoved, graph->mincut, graph->minvol); + if (ctrl->minconn) + printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1)); + printf("\n"); + } + + if (nmoved == 0 || + (omode == OMODE_REFINE && graph->minvol == oldvol && graph->mincut == oldcut)) + break; + } + + ipqDestroy(queue); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! K-way partitioning optimization in which the vertices are visited in + decreasing ed/sqrt(nnbrs)-id order. Note this is just an + approximation, as the ed is often split across different subdomains + and the sqrt(nnbrs) is just a crude approximation. + + \param graph is the graph that is being refined. + \param niter is the number of refinement iterations. + \param ffactor is the \em fudge-factor for allowing positive gain moves + to violate the max-pwgt constraint. + \param omode is the type of optimization that will performed among + OMODE_REFINE and OMODE_BALANCE + + +*/ +/**************************************************************************/ +void Greedy_McKWayCutOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode) +{ + /* Common variables to all types of kway-refinement/balancing routines */ + idx_t i, ii, iii, j, k, l, pass, nvtxs, ncon, nparts, gain; + idx_t from, me, to, cto, oldcut; + idx_t *xadj, *vwgt, *adjncy, *adjwgt; + idx_t *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt; + idx_t nmoved, nupd, *vstatus, *updptr, *updind; + idx_t maxndoms, *safetos=NULL, *nads=NULL, *doms=NULL, **adids=NULL, **adwgts=NULL; + idx_t *bfslvl=NULL, *bfsind=NULL, *bfsmrk=NULL; + idx_t bndtype = (omode == OMODE_REFINE ? BNDTYPE_REFINE : BNDTYPE_BALANCE); + real_t *ubfactors, *pijbm; + real_t origbal; + + /* Edgecut-specific/different variables */ + idx_t nbnd, oldnnbrs; + rpq_t *queue; + real_t rgain; + ckrinfo_t *myrinfo; + cnbr_t *mynbrs; + + WCOREPUSH; + + /* Link the graph fields */ + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + bndind = graph->bndind; + bndptr = graph->bndptr; + + where = graph->where; + pwgts = graph->pwgts; + + nparts = ctrl->nparts; + pijbm = ctrl->pijbm; + + + /* Determine the ubfactors. The method used is different based on omode. + When OMODE_BALANCE, the ubfactors are those supplied by the user. + When OMODE_REFINE, the ubfactors are the max of the current partition + and the user-specified ones. */ + ubfactors = rwspacemalloc(ctrl, ncon); + ComputeLoadImbalanceVec(graph, nparts, pijbm, ubfactors); + origbal = rvecmaxdiff(ncon, ubfactors, ctrl->ubfactors); + if (omode == OMODE_BALANCE) { + rcopy(ncon, ctrl->ubfactors, ubfactors); + } + else { + for (i=0; i ctrl->ubfactors[i] ? ubfactors[i] : ctrl->ubfactors[i]); + } + + + /* Setup the weight intervals of the various subdomains */ + minwgt = iwspacemalloc(ctrl, nparts*ncon); + maxwgt = iwspacemalloc(ctrl, nparts*ncon); + + for (i=0; itpwgts[i*ncon+j]*graph->tvwgt[j]*ubfactors[j]; + /*minwgt[i*ncon+j] = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*(.9/ubfactors[j]);*/ + minwgt[i*ncon+j] = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*.2; + } + } + + perm = iwspacemalloc(ctrl, nvtxs); + + + /* This stores the valid target subdomains. It is used when ctrl->minconn to + control the subdomains to which moves are allowed to be made. + When ctrl->minconn is false, the default values of 2 allow all moves to + go through and it does not interfere with the zero-gain move selection. */ + safetos = iset(nparts, 2, iwspacemalloc(ctrl, nparts)); + + if (ctrl->minconn) { + ComputeSubDomainGraph(ctrl, graph); + + nads = ctrl->nads; + adids = ctrl->adids; + adwgts = ctrl->adwgts; + doms = iset(nparts, 0, ctrl->pvec1); + } + + + /* Setup updptr, updind like boundary info to keep track of the vertices whose + vstatus's need to be reset at the end of the inner iteration */ + vstatus = iset(nvtxs, VPQSTATUS_NOTPRESENT, iwspacemalloc(ctrl, nvtxs)); + updptr = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs)); + updind = iwspacemalloc(ctrl, nvtxs); + + if (ctrl->contig) { + /* The arrays that will be used for limited check of articulation points */ + bfslvl = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + bfsind = iwspacemalloc(ctrl, nvtxs); + bfsmrk = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + } + + if (ctrl->dbglvl&METIS_DBG_REFINE) { + printf("%s: [%6"PRIDX" %6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL"(%.3"PRREAL")," + " Nv-Nb[%6"PRIDX" %6"PRIDX"], Cut: %6"PRIDX", (%"PRIDX")", + (omode == OMODE_REFINE ? "GRC" : "GBC"), + imin(nparts*ncon, pwgts), imax(nparts*ncon, pwgts), imax(nparts*ncon, maxwgt), + ComputeLoadImbalance(graph, nparts, pijbm), origbal, + graph->nvtxs, graph->nbnd, graph->mincut, niter); + if (ctrl->minconn) + printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1)); + printf("\n"); + } + + queue = rpqCreate(nvtxs); + + + /*===================================================================== + * The top-level refinement loop + *======================================================================*/ + for (pass=0; passmincut); + + /* In balancing mode, exit as soon as balance is reached */ + if (omode == OMODE_BALANCE && IsBalanced(ctrl, graph, 0)) + break; + + oldcut = graph->mincut; + nbnd = graph->nbnd; + nupd = 0; + + if (ctrl->minconn) + maxndoms = imax(nparts, nads); + + /* Insert the boundary vertices in the priority queue */ + irandArrayPermute(nbnd, perm, nbnd/4, 1); + for (ii=0; iickrinfo[i].nnbrs > 0 ? + 1.0*graph->ckrinfo[i].ed/sqrt(graph->ckrinfo[i].nnbrs) : 0.0) + - graph->ckrinfo[i].id; + rpqInsert(queue, i, rgain); + vstatus[i] = VPQSTATUS_PRESENT; + ListInsert(nupd, updind, updptr, i); + } + + /* Start extracting vertices from the queue and try to move them */ + for (nmoved=0, iii=0;;iii++) { + if ((i = rpqGetTop(queue)) == -1) + break; + vstatus[i] = VPQSTATUS_EXTRACTED; + + myrinfo = graph->ckrinfo+i; + mynbrs = ctrl->cnbrpool + myrinfo->inbr; + + from = where[i]; + + /* Prevent moves that make 'from' domain underbalanced */ + if (omode == OMODE_REFINE) { + if (myrinfo->id > 0 && + !ivecaxpygez(ncon, -1, vwgt+i*ncon, pwgts+from*ncon, minwgt+from*ncon)) + continue; + } + else { /* OMODE_BALANCE */ + if (!ivecaxpygez(ncon, -1, vwgt+i*ncon, pwgts+from*ncon, minwgt+from*ncon)) + continue; + } + + if (ctrl->contig && IsArticulationNode(i, xadj, adjncy, where, bfslvl, bfsind, bfsmrk)) + continue; + + if (ctrl->minconn) + SelectSafeTargetSubdomains(myrinfo, mynbrs, nads, adids, maxndoms, safetos, doms); + + /* Find the most promising subdomain to move to */ + if (omode == OMODE_REFINE) { + for (k=myrinfo->nnbrs-1; k>=0; k--) { + if (!safetos[to=mynbrs[k].pid]) + continue; + gain = mynbrs[k].ed-myrinfo->id; + if (gain >= 0 && ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon)) + break; + } + if (k < 0) + continue; /* break out if you did not find a candidate */ + + cto = to; + for (j=k-1; j>=0; j--) { + if (!safetos[to=mynbrs[j].pid]) + continue; + if ((mynbrs[j].ed > mynbrs[k].ed && + ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon)) + || + (mynbrs[j].ed == mynbrs[k].ed && + BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + 1, pwgts+cto*ncon, pijbm+cto*ncon, + 1, pwgts+to*ncon, pijbm+to*ncon))) { + k = j; + cto = to; + } + } + to = cto; + + gain = mynbrs[k].ed-myrinfo->id; + if (!(gain > 0 + || (gain == 0 + && (BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + -1, pwgts+from*ncon, pijbm+from*ncon, + +1, pwgts+to*ncon, pijbm+to*ncon) + || (iii%2 == 0 && safetos[to] == 2) + ) + ) + ) + ) + continue; + } + else { /* OMODE_BALANCE */ + for (k=myrinfo->nnbrs-1; k>=0; k--) { + if (!safetos[to=mynbrs[k].pid]) + continue; + if (ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon) || + BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + -1, pwgts+from*ncon, pijbm+from*ncon, + +1, pwgts+to*ncon, pijbm+to*ncon)) + break; + } + if (k < 0) + continue; /* break out if you did not find a candidate */ + + cto = to; + for (j=k-1; j>=0; j--) { + if (!safetos[to=mynbrs[j].pid]) + continue; + if (BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + 1, pwgts+cto*ncon, pijbm+cto*ncon, + 1, pwgts+to*ncon, pijbm+to*ncon)) { + k = j; + cto = to; + } + } + to = cto; + + if (mynbrs[k].ed-myrinfo->id < 0 && + !BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + -1, pwgts+from*ncon, pijbm+from*ncon, + +1, pwgts+to*ncon, pijbm+to*ncon)) + continue; + } + + + + /*===================================================================== + * If we got here, we can now move the vertex from 'from' to 'to' + *======================================================================*/ + graph->mincut -= mynbrs[k].ed-myrinfo->id; + nmoved++; + + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, + printf("\t\tMoving %6"PRIDX" to %3"PRIDX". Gain: %4"PRIDX". Cut: %6"PRIDX"\n", + i, to, mynbrs[k].ed-myrinfo->id, graph->mincut)); + + /* Update the subdomain connectivity information */ + if (ctrl->minconn) { + /* take care of i's move itself */ + UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->id-mynbrs[k].ed, &maxndoms); + + /* take care of the adjancent vertices */ + for (j=xadj[i]; jckrinfo+ii; + + oldnnbrs = myrinfo->nnbrs; + + UpdateAdjacentVertexInfoAndBND(ctrl, ii, xadj[ii+1]-xadj[ii], me, + from, to, myrinfo, adjwgt[j], nbnd, bndptr, bndind, bndtype); + + UpdateQueueInfo(queue, vstatus, ii, me, from, to, myrinfo, oldnnbrs, + nupd, updptr, updind, bndtype); + + ASSERT(myrinfo->nnbrs <= xadj[ii+1]-xadj[ii]); + } + } + + graph->nbnd = nbnd; + + /* Reset the vstatus and associated data structures */ + for (i=0; idbglvl&METIS_DBG_REFINE) { + printf("\t[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL", Nb: %6"PRIDX"." + " Nmoves: %5"PRIDX", Cut: %6"PRIDX", Vol: %6"PRIDX, + imin(nparts*ncon, pwgts), imax(nparts*ncon, pwgts), + ComputeLoadImbalance(graph, nparts, pijbm), + graph->nbnd, nmoved, graph->mincut, ComputeVolume(graph, where)); + if (ctrl->minconn) + printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1)); + printf("\n"); + } + + if (nmoved == 0 || (omode == OMODE_REFINE && graph->mincut == oldcut)) + break; + } + + rpqDestroy(queue); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! K-way refinement that minimizes the communication volume. This is a + greedy routine and the vertices are visited in decreasing gv order. + + \param graph is the graph that is being refined. + \param niter is the number of refinement iterations. + \param ffactor is the \em fudge-factor for allowing positive gain moves + to violate the max-pwgt constraint. + +*/ +/**************************************************************************/ +void Greedy_McKWayVolOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode) +{ + /* Common variables to all types of kway-refinement/balancing routines */ + idx_t i, ii, iii, j, k, l, pass, nvtxs, ncon, nparts, gain; + idx_t from, me, to, cto, oldcut; + idx_t *xadj, *vwgt, *adjncy; + idx_t *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt; + idx_t nmoved, nupd, *vstatus, *updptr, *updind; + idx_t maxndoms, *safetos=NULL, *nads=NULL, *doms=NULL, **adids=NULL, **adwgts=NULL; + idx_t *bfslvl=NULL, *bfsind=NULL, *bfsmrk=NULL; + idx_t bndtype = (omode == OMODE_REFINE ? BNDTYPE_REFINE : BNDTYPE_BALANCE); + real_t *ubfactors, *pijbm; + real_t origbal; + + /* Volume-specific/different variables */ + ipq_t *queue; + idx_t oldvol, xgain; + idx_t *vmarker, *pmarker, *modind; + vkrinfo_t *myrinfo; + vnbr_t *mynbrs; + + WCOREPUSH; + + /* Link the graph fields */ + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + bndptr = graph->bndptr; + bndind = graph->bndind; + where = graph->where; + pwgts = graph->pwgts; + + nparts = ctrl->nparts; + pijbm = ctrl->pijbm; + + + /* Determine the ubfactors. The method used is different based on omode. + When OMODE_BALANCE, the ubfactors are those supplied by the user. + When OMODE_REFINE, the ubfactors are the max of the current partition + and the user-specified ones. */ + ubfactors = rwspacemalloc(ctrl, ncon); + ComputeLoadImbalanceVec(graph, nparts, pijbm, ubfactors); + origbal = rvecmaxdiff(ncon, ubfactors, ctrl->ubfactors); + if (omode == OMODE_BALANCE) { + rcopy(ncon, ctrl->ubfactors, ubfactors); + } + else { + for (i=0; i ctrl->ubfactors[i] ? ubfactors[i] : ctrl->ubfactors[i]); + } + + + /* Setup the weight intervals of the various subdomains */ + minwgt = iwspacemalloc(ctrl, nparts*ncon); + maxwgt = iwspacemalloc(ctrl, nparts*ncon); + + for (i=0; itpwgts[i*ncon+j]*graph->tvwgt[j]*ubfactors[j]; + /*minwgt[i*ncon+j] = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*(.9/ubfactors[j]); */ + minwgt[i*ncon+j] = ctrl->tpwgts[i*ncon+j]*graph->tvwgt[j]*.2; + } + } + + perm = iwspacemalloc(ctrl, nvtxs); + + + /* This stores the valid target subdomains. It is used when ctrl->minconn to + control the subdomains to which moves are allowed to be made. + When ctrl->minconn is false, the default values of 2 allow all moves to + go through and it does not interfere with the zero-gain move selection. */ + safetos = iset(nparts, 2, iwspacemalloc(ctrl, nparts)); + + if (ctrl->minconn) { + ComputeSubDomainGraph(ctrl, graph); + + nads = ctrl->nads; + adids = ctrl->adids; + adwgts = ctrl->adwgts; + doms = iset(nparts, 0, ctrl->pvec1); + } + + + /* Setup updptr, updind like boundary info to keep track of the vertices whose + vstatus's need to be reset at the end of the inner iteration */ + vstatus = iset(nvtxs, VPQSTATUS_NOTPRESENT, iwspacemalloc(ctrl, nvtxs)); + updptr = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs)); + updind = iwspacemalloc(ctrl, nvtxs); + + if (ctrl->contig) { + /* The arrays that will be used for limited check of articulation points */ + bfslvl = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + bfsind = iwspacemalloc(ctrl, nvtxs); + bfsmrk = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + } + + /* Vol-refinement specific working arrays */ + modind = iwspacemalloc(ctrl, nvtxs); + vmarker = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + pmarker = iset(nparts, -1, iwspacemalloc(ctrl, nparts)); + + if (ctrl->dbglvl&METIS_DBG_REFINE) { + printf("%s: [%6"PRIDX" %6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL"(%.3"PRREAL")," + ", Nv-Nb[%6"PRIDX" %6"PRIDX"], Cut: %5"PRIDX", Vol: %5"PRIDX", (%"PRIDX")", + (omode == OMODE_REFINE ? "GRV" : "GBV"), + imin(nparts*ncon, pwgts), imax(nparts*ncon, pwgts), imax(nparts*ncon, maxwgt), + ComputeLoadImbalance(graph, nparts, pijbm), origbal, + graph->nvtxs, graph->nbnd, graph->mincut, graph->minvol, niter); + if (ctrl->minconn) + printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1)); + printf("\n"); + } + + queue = ipqCreate(nvtxs); + + + /*===================================================================== + * The top-level refinement loop + *======================================================================*/ + for (pass=0; passminvol); + + /* In balancing mode, exit as soon as balance is reached */ + if (omode == OMODE_BALANCE && IsBalanced(ctrl, graph, 0)) + break; + + oldcut = graph->mincut; + oldvol = graph->minvol; + nupd = 0; + + if (ctrl->minconn) + maxndoms = imax(nparts, nads); + + /* Insert the boundary vertices in the priority queue */ + irandArrayPermute(graph->nbnd, perm, graph->nbnd/4, 1); + for (ii=0; iinbnd; ii++) { + i = bndind[perm[ii]]; + ipqInsert(queue, i, graph->vkrinfo[i].gv); + vstatus[i] = VPQSTATUS_PRESENT; + ListInsert(nupd, updind, updptr, i); + } + + /* Start extracting vertices from the queue and try to move them */ + for (nmoved=0, iii=0;;iii++) { + if ((i = ipqGetTop(queue)) == -1) + break; + vstatus[i] = VPQSTATUS_EXTRACTED; + + myrinfo = graph->vkrinfo+i; + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + from = where[i]; + + /* Prevent moves that make 'from' domain underbalanced */ + if (omode == OMODE_REFINE) { + if (myrinfo->nid > 0 && + !ivecaxpygez(ncon, -1, vwgt+i*ncon, pwgts+from*ncon, minwgt+from*ncon)) + continue; + } + else { /* OMODE_BALANCE */ + if (!ivecaxpygez(ncon, -1, vwgt+i*ncon, pwgts+from*ncon, minwgt+from*ncon)) + continue; + } + + if (ctrl->contig && IsArticulationNode(i, xadj, adjncy, where, bfslvl, bfsind, bfsmrk)) + continue; + + if (ctrl->minconn) + SelectSafeTargetSubdomains(myrinfo, mynbrs, nads, adids, maxndoms, safetos, doms); + + xgain = (myrinfo->nid == 0 && myrinfo->ned > 0 ? graph->vsize[i] : 0); + + /* Find the most promising subdomain to move to */ + if (omode == OMODE_REFINE) { + for (k=myrinfo->nnbrs-1; k>=0; k--) { + if (!safetos[to=mynbrs[k].pid]) + continue; + gain = mynbrs[k].gv + xgain; + if (gain >= 0 && ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon)) + break; + } + if (k < 0) + continue; /* break out if you did not find a candidate */ + + cto = to; + for (j=k-1; j>=0; j--) { + if (!safetos[to=mynbrs[j].pid]) + continue; + gain = mynbrs[j].gv + xgain; + if ((mynbrs[j].gv > mynbrs[k].gv && + ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon)) + || + (mynbrs[j].gv == mynbrs[k].gv && + mynbrs[j].ned > mynbrs[k].ned && + ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon)) + || + (mynbrs[j].gv == mynbrs[k].gv && + mynbrs[j].ned == mynbrs[k].ned && + BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + 1, pwgts+cto*ncon, pijbm+cto*ncon, + 1, pwgts+to*ncon, pijbm+to*ncon))) { + k = j; + cto = to; + } + } + to = cto; + + j = 0; + if (xgain+mynbrs[k].gv > 0 || mynbrs[k].ned-myrinfo->nid > 0) + j = 1; + else if (mynbrs[k].ned-myrinfo->nid == 0) { + if ((iii%2 == 0 && safetos[to] == 2) || + BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + -1, pwgts+from*ncon, pijbm+from*ncon, + +1, pwgts+to*ncon, pijbm+to*ncon)) + j = 1; + } + if (j == 0) + continue; + } + else { /* OMODE_BALANCE */ + for (k=myrinfo->nnbrs-1; k>=0; k--) { + if (!safetos[to=mynbrs[k].pid]) + continue; + if (ivecaxpylez(ncon, 1, vwgt+i*ncon, pwgts+to*ncon, maxwgt+to*ncon) || + BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + -1, pwgts+from*ncon, pijbm+from*ncon, + +1, pwgts+to*ncon, pijbm+to*ncon)) + break; + } + if (k < 0) + continue; /* break out if you did not find a candidate */ + + cto = to; + for (j=k-1; j>=0; j--) { + if (!safetos[to=mynbrs[j].pid]) + continue; + if (BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + 1, pwgts+cto*ncon, pijbm+cto*ncon, + 1, pwgts+to*ncon, pijbm+to*ncon)) { + k = j; + cto = to; + } + } + to = cto; + + if ((xgain+mynbrs[k].gv < 0 || + (xgain+mynbrs[k].gv == 0 && mynbrs[k].ned-myrinfo->nid < 0)) + && + !BetterBalanceKWay(ncon, vwgt+i*ncon, ubfactors, + -1, pwgts+from*ncon, pijbm+from*ncon, + +1, pwgts+to*ncon, pijbm+to*ncon)) + continue; + } + + + /*===================================================================== + * If we got here, we can now move the vertex from 'from' to 'to' + *======================================================================*/ + graph->mincut -= mynbrs[k].ned-myrinfo->nid; + graph->minvol -= (xgain+mynbrs[k].gv); + where[i] = to; + nmoved++; + + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, + printf("\t\tMoving %6"PRIDX" from %3"PRIDX" to %3"PRIDX". " + "Gain: [%4"PRIDX" %4"PRIDX"]. Cut: %6"PRIDX", Vol: %6"PRIDX"\n", + i, from, to, xgain+mynbrs[k].gv, mynbrs[k].ned-myrinfo->nid, + graph->mincut, graph->minvol)); + + /* Update the subdomain connectivity information */ + if (ctrl->minconn) { + /* take care of i's move itself */ + UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->nid-mynbrs[k].ned, &maxndoms); + + /* take care of the adjancent vertices */ + for (j=xadj[i]; jdbglvl&METIS_DBG_REFINE) { + printf("\t[%6"PRIDX" %6"PRIDX"], Bal: %5.3"PRREAL", Nb: %6"PRIDX"." + " Nmoves: %5"PRIDX", Cut: %6"PRIDX", Vol: %6"PRIDX, + imin(nparts*ncon, pwgts), imax(nparts*ncon, pwgts), + ComputeLoadImbalance(graph, nparts, pijbm), + graph->nbnd, nmoved, graph->mincut, graph->minvol); + if (ctrl->minconn) + printf(", Doms: [%3"PRIDX" %4"PRIDX"]", imax(nparts, nads), isum(nparts, nads,1)); + printf("\n"); + } + + if (nmoved == 0 || + (omode == OMODE_REFINE && graph->minvol == oldvol && graph->mincut == oldcut)) + break; + } + + ipqDestroy(queue); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function performs an approximate articulation vertex test. + It assumes that the bfslvl, bfsind, and bfsmrk arrays are initialized + appropriately. */ +/*************************************************************************/ +idx_t IsArticulationNode(idx_t i, idx_t *xadj, idx_t *adjncy, idx_t *where, + idx_t *bfslvl, idx_t *bfsind, idx_t *bfsmrk) +{ + idx_t ii, j, k=0, head, tail, nhits, tnhits, from, BFSDEPTH=5; + + from = where[i]; + + /* Determine if the vertex is safe to move from a contiguity standpoint */ + for (tnhits=0, j=xadj[i]; jxadj; + adjncy = graph->adjncy; + vsize = graph->vsize; + where = graph->where; + + myrinfo = graph->vkrinfo+v; + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + + /*====================================================================== + * Remove the contributions on the gain made by 'v'. + *=====================================================================*/ + for (k=0; knnbrs; k++) + pmarker[mynbrs[k].pid] = k; + pmarker[from] = k; + + myidx = pmarker[to]; /* Keep track of the index in mynbrs of the 'to' domain */ + + for (j=xadj[v]; jvkrinfo+ii; + onbrs = ctrl->vnbrpool + orinfo->inbr; + + if (other == from) { + for (k=0; knnbrs; k++) { + if (pmarker[onbrs[k].pid] == -1) + onbrs[k].gv += vsize[v]; + } + } + else { + ASSERT(pmarker[other] != -1); + + if (mynbrs[pmarker[other]].ned > 1) { + for (k=0; knnbrs; k++) { + if (pmarker[onbrs[k].pid] == -1) + onbrs[k].gv += vsize[v]; + } + } + else { /* There is only one connection */ + for (k=0; knnbrs; k++) { + if (pmarker[onbrs[k].pid] != -1) + onbrs[k].gv -= vsize[v]; + } + } + } + } + + for (k=0; knnbrs; k++) + pmarker[mynbrs[k].pid] = -1; + pmarker[from] = -1; + + + /*====================================================================== + * Update the id/ed of vertex 'v' + *=====================================================================*/ + if (myidx == -1) { + myidx = myrinfo->nnbrs++; + ASSERT(myidx < xadj[v+1]-xadj[v]); + mynbrs[myidx].ned = 0; + } + myrinfo->ned += myrinfo->nid-mynbrs[myidx].ned; + SWAP(myrinfo->nid, mynbrs[myidx].ned, j); + if (mynbrs[myidx].ned == 0) + mynbrs[myidx] = mynbrs[--myrinfo->nnbrs]; + else + mynbrs[myidx].pid = from; + + + /*====================================================================== + * Update the degrees of adjacent vertices and their volume gains + *=====================================================================*/ + vmarker[v] = 1; + modind[0] = v; + nmod = 1; + for (j=xadj[v]; jvkrinfo+ii; + if (myrinfo->inbr == -1) + myrinfo->inbr = vnbrpoolGetNext(ctrl, xadj[ii+1]-xadj[ii]+1); + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + if (me == from) { + INC_DEC(myrinfo->ned, myrinfo->nid, 1); + } + else if (me == to) { + INC_DEC(myrinfo->nid, myrinfo->ned, 1); + } + + /* Remove the edgeweight from the 'pid == from' entry of the vertex */ + if (me != from) { + for (k=0; knnbrs; k++) { + if (mynbrs[k].pid == from) { + if (mynbrs[k].ned == 1) { + mynbrs[k] = mynbrs[--myrinfo->nnbrs]; + vmarker[ii] = 1; /* You do a complete .gv calculation */ + + /* All vertices adjacent to 'ii' need to be updated */ + for (jj=xadj[ii]; jjvkrinfo+u; + onbrs = ctrl->vnbrpool + orinfo->inbr; + + for (kk=0; kknnbrs; kk++) { + if (onbrs[kk].pid == from) { + onbrs[kk].gv -= vsize[ii]; + if (!vmarker[u]) { /* Need to update boundary etc */ + vmarker[u] = 2; + modind[nmod++] = u; + } + break; + } + } + } + } + else { + mynbrs[k].ned--; + + /* Update the gv due to single 'ii' connection to 'from' */ + if (mynbrs[k].ned == 1) { + /* find the vertex 'u' that 'ii' was connected into 'from' */ + for (jj=xadj[ii]; jjvkrinfo+u; + onbrs = ctrl->vnbrpool + orinfo->inbr; + + /* The following is correct because domains in common + between ii and u will lead to a reduction over the + previous gain, whereas domains only in u but not in + ii, will lead to no change as opposed to the earlier + increase */ + for (kk=0; kknnbrs; kk++) + onbrs[kk].gv += vsize[ii]; + + if (!vmarker[u]) { /* Need to update boundary etc */ + vmarker[u] = 2; + modind[nmod++] = u; + } + break; + } + } + } + } + break; + } + } + } + + + /* Add the edgeweight to the 'pid == to' entry of the vertex */ + if (me != to) { + for (k=0; knnbrs; k++) { + if (mynbrs[k].pid == to) { + mynbrs[k].ned++; + + /* Update the gv due to non-single 'ii' connection to 'to' */ + if (mynbrs[k].ned == 2) { + /* find the vertex 'u' that 'ii' was connected into 'to' */ + for (jj=xadj[ii]; jjvkrinfo+u; + onbrs = ctrl->vnbrpool + orinfo->inbr; + for (kk=0; kknnbrs; kk++) + onbrs[kk].gv -= vsize[ii]; + + if (!vmarker[u]) { /* Need to update boundary etc */ + vmarker[u] = 2; + modind[nmod++] = u; + } + break; + } + } + } + break; + } + } + + if (k == myrinfo->nnbrs) { + mynbrs[myrinfo->nnbrs].pid = to; + mynbrs[myrinfo->nnbrs++].ned = 1; + vmarker[ii] = 1; /* You do a complete .gv calculation */ + + /* All vertices adjacent to 'ii' need to be updated */ + for (jj=xadj[ii]; jjvkrinfo+u; + onbrs = ctrl->vnbrpool + orinfo->inbr; + + for (kk=0; kknnbrs; kk++) { + if (onbrs[kk].pid == to) { + onbrs[kk].gv += vsize[ii]; + if (!vmarker[u]) { /* Need to update boundary etc */ + vmarker[u] = 2; + modind[nmod++] = u; + } + break; + } + } + } + } + } + + ASSERT(myrinfo->nnbrs <= xadj[ii+1]-xadj[ii]); + } + + + /*====================================================================== + * Add the contributions on the volume gain due to 'v' + *=====================================================================*/ + myrinfo = graph->vkrinfo+v; + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + for (k=0; knnbrs; k++) + pmarker[mynbrs[k].pid] = k; + pmarker[to] = k; + + for (j=xadj[v]; jvkrinfo+ii; + onbrs = ctrl->vnbrpool + orinfo->inbr; + + if (other == to) { + for (k=0; knnbrs; k++) { + if (pmarker[onbrs[k].pid] == -1) + onbrs[k].gv -= vsize[v]; + } + } + else { + ASSERT(pmarker[other] != -1); + + if (mynbrs[pmarker[other]].ned > 1) { + for (k=0; knnbrs; k++) { + if (pmarker[onbrs[k].pid] == -1) + onbrs[k].gv -= vsize[v]; + } + } + else { /* There is only one connection */ + for (k=0; knnbrs; k++) { + if (pmarker[onbrs[k].pid] != -1) + onbrs[k].gv += vsize[v]; + } + } + } + } + for (k=0; knnbrs; k++) + pmarker[mynbrs[k].pid] = -1; + pmarker[to] = -1; + + + /*====================================================================== + * Recompute the volume information of the 'hard' nodes, and update the + * max volume gain for all the modified vertices and the priority queue + *=====================================================================*/ + for (iii=0; iiivkrinfo+i; + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + if (vmarker[i] == 1) { /* Only complete gain updates go through */ + for (k=0; knnbrs; k++) + mynbrs[k].gv = 0; + + for (j=xadj[i]; jvkrinfo+ii; + onbrs = ctrl->vnbrpool + orinfo->inbr; + + for (kk=0; kknnbrs; kk++) + pmarker[onbrs[kk].pid] = kk; + pmarker[other] = 1; + + if (me == other) { + /* Find which domains 'i' is connected and 'ii' is not and update their gain */ + for (k=0; knnbrs; k++) { + if (pmarker[mynbrs[k].pid] == -1) + mynbrs[k].gv -= vsize[ii]; + } + } + else { + ASSERT(pmarker[me] != -1); + + /* I'm the only connection of 'ii' in 'me' */ + if (onbrs[pmarker[me]].ned == 1) { + /* Increase the gains for all the common domains between 'i' and 'ii' */ + for (k=0; knnbrs; k++) { + if (pmarker[mynbrs[k].pid] != -1) + mynbrs[k].gv += vsize[ii]; + } + } + else { + /* Find which domains 'i' is connected and 'ii' is not and update their gain */ + for (k=0; knnbrs; k++) { + if (pmarker[mynbrs[k].pid] == -1) + mynbrs[k].gv -= vsize[ii]; + } + } + } + + for (kk=0; kknnbrs; kk++) + pmarker[onbrs[kk].pid] = -1; + pmarker[other] = -1; + + } + } + + /* Compute the overall gv for that node */ + myrinfo->gv = IDX_MIN; + for (k=0; knnbrs; k++) { + if (mynbrs[k].gv > myrinfo->gv) + myrinfo->gv = mynbrs[k].gv; + } + + /* Add the xtra gain due to id == 0 */ + if (myrinfo->ned > 0 && myrinfo->nid == 0) + myrinfo->gv += vsize[i]; + + + /*====================================================================== + * Maintain a consistent boundary + *=====================================================================*/ + if (bndtype == BNDTYPE_REFINE) { + if (myrinfo->gv >= 0 && graph->bndptr[i] == -1) + BNDInsert(graph->nbnd, graph->bndind, graph->bndptr, i); + + if (myrinfo->gv < 0 && graph->bndptr[i] != -1) + BNDDelete(graph->nbnd, graph->bndind, graph->bndptr, i); + } + else { + if (myrinfo->ned > 0 && graph->bndptr[i] == -1) + BNDInsert(graph->nbnd, graph->bndind, graph->bndptr, i); + + if (myrinfo->ned == 0 && graph->bndptr[i] != -1) + BNDDelete(graph->nbnd, graph->bndind, graph->bndptr, i); + } + + + /*====================================================================== + * Update the priority queue appropriately (if allowed) + *=====================================================================*/ + if (queue != NULL) { + if (vstatus[i] != VPQSTATUS_EXTRACTED) { + if (graph->bndptr[i] != -1) { /* In-boundary vertex */ + if (vstatus[i] == VPQSTATUS_PRESENT) { + ipqUpdate(queue, i, myrinfo->gv); + } + else { + ipqInsert(queue, i, myrinfo->gv); + vstatus[i] = VPQSTATUS_PRESENT; + ListInsert(*r_nupd, updind, updptr, i); + } + } + else { /* Off-boundary vertex */ + if (vstatus[i] == VPQSTATUS_PRESENT) { + ipqDelete(queue, i); + vstatus[i] = VPQSTATUS_NOTPRESENT; + ListDelete(*r_nupd, updind, updptr, i); + } + } + } + } + + vmarker[i] = 0; + } +} + diff --git a/src/metis/libmetis/kwayrefine.c b/src/metis/libmetis/kwayrefine.c new file mode 100644 index 0000000..0e3c6db --- /dev/null +++ b/src/metis/libmetis/kwayrefine.c @@ -0,0 +1,672 @@ +/*! +\file +\brief Driving routines for multilevel k-way refinement + +\date Started 7/28/1997 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version $Id: kwayrefine.c 10737 2011-09-13 13:37:25Z karypis $ +*/ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function is the entry point of cut-based refinement */ +/*************************************************************************/ +void RefineKWay(ctrl_t *ctrl, graph_t *orggraph, graph_t *graph) +{ + idx_t i, nlevels, contig=ctrl->contig; + graph_t *ptr; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); + + /* Determine how many levels are there */ + for (ptr=graph, nlevels=0; ptr!=orggraph; ptr=ptr->finer, nlevels++); + + /* Compute the parameters of the coarsest graph */ + ComputeKWayPartitionParams(ctrl, graph); + + /* Try to minimize the sub-domain connectivity */ + if (ctrl->minconn) + EliminateSubDomainEdges(ctrl, graph); + + /* Deal with contiguity constraints at the beginning */ + if (contig && FindPartitionInducedComponents(graph, graph->where, NULL, NULL) > ctrl->nparts) { + EliminateComponents(ctrl, graph); + + ComputeKWayBoundary(ctrl, graph, BNDTYPE_BALANCE); + Greedy_KWayOptimize(ctrl, graph, 5, 0, OMODE_BALANCE); + + ComputeKWayBoundary(ctrl, graph, BNDTYPE_REFINE); + Greedy_KWayOptimize(ctrl, graph, ctrl->niter, 0, OMODE_REFINE); + + ctrl->contig = 0; + } + + /* Refine each successively finer graph */ + for (i=0; ;i++) { + if (ctrl->minconn && i == nlevels/2) + EliminateSubDomainEdges(ctrl, graph); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->RefTmr)); + + if (2*i >= nlevels && !IsBalanced(ctrl, graph, .02)) { + ComputeKWayBoundary(ctrl, graph, BNDTYPE_BALANCE); + Greedy_KWayOptimize(ctrl, graph, 1, 0, OMODE_BALANCE); + ComputeKWayBoundary(ctrl, graph, BNDTYPE_REFINE); + } + + Greedy_KWayOptimize(ctrl, graph, ctrl->niter, 5.0, OMODE_REFINE); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); + + /* Deal with contiguity constraints in the middle */ + if (contig && i == nlevels/2) { + if (FindPartitionInducedComponents(graph, graph->where, NULL, NULL) > ctrl->nparts) { + EliminateComponents(ctrl, graph); + + if (!IsBalanced(ctrl, graph, .02)) { + ctrl->contig = 1; + ComputeKWayBoundary(ctrl, graph, BNDTYPE_BALANCE); + Greedy_KWayOptimize(ctrl, graph, 5, 0, OMODE_BALANCE); + + ComputeKWayBoundary(ctrl, graph, BNDTYPE_REFINE); + Greedy_KWayOptimize(ctrl, graph, ctrl->niter, 0, OMODE_REFINE); + ctrl->contig = 0; + } + } + } + + if (graph == orggraph) + break; + + graph = graph->finer; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); + ASSERT(graph->vwgt != NULL); + + ProjectKWayPartition(ctrl, graph); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); + } + + /* Deal with contiguity requirement at the end */ + ctrl->contig = contig; + if (contig && FindPartitionInducedComponents(graph, graph->where, NULL, NULL) > ctrl->nparts) + EliminateComponents(ctrl, graph); + + if (!IsBalanced(ctrl, graph, 0.0)) { + ComputeKWayBoundary(ctrl, graph, BNDTYPE_BALANCE); + Greedy_KWayOptimize(ctrl, graph, 10, 0, OMODE_BALANCE); + + ComputeKWayBoundary(ctrl, graph, BNDTYPE_REFINE); + Greedy_KWayOptimize(ctrl, graph, ctrl->niter, 0, OMODE_REFINE); + } + + if (ctrl->contig) + ASSERT(FindPartitionInducedComponents(graph, graph->where, NULL, NULL) == ctrl->nparts); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); +} + + +/*************************************************************************/ +/*! This function allocates memory for the k-way cut-based refinement */ +/*************************************************************************/ +void AllocateKWayPartitionMemory(ctrl_t *ctrl, graph_t *graph) +{ + + graph->pwgts = imalloc(ctrl->nparts*graph->ncon, "AllocateKWayPartitionMemory: pwgts"); + graph->where = imalloc(graph->nvtxs, "AllocateKWayPartitionMemory: where"); + graph->bndptr = imalloc(graph->nvtxs, "AllocateKWayPartitionMemory: bndptr"); + graph->bndind = imalloc(graph->nvtxs, "AllocateKWayPartitionMemory: bndind"); + + switch (ctrl->objtype) { + case METIS_OBJTYPE_CUT: + graph->ckrinfo = (ckrinfo_t *)gk_malloc(graph->nvtxs*sizeof(ckrinfo_t), + "AllocateKWayPartitionMemory: ckrinfo"); + break; + + case METIS_OBJTYPE_VOL: + graph->vkrinfo = (vkrinfo_t *)gk_malloc(graph->nvtxs*sizeof(vkrinfo_t), + "AllocateKWayVolPartitionMemory: vkrinfo"); + + /* This is to let the cut-based -minconn and -contig large-scale graph + changes to go through */ + graph->ckrinfo = (ckrinfo_t *)graph->vkrinfo; + break; + + default: + gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype); + } + +} + + +/*************************************************************************/ +/*! This function computes the initial id/ed for cut-based partitioning */ +/**************************************************************************/ +void ComputeKWayPartitionParams(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, j, k, l, nvtxs, ncon, nparts, nbnd, mincut, me, other; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *pwgts, *where, *bndind, *bndptr; + + nparts = ctrl->nparts; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + where = graph->where; + pwgts = iset(nparts*ncon, 0, graph->pwgts); + bndind = graph->bndind; + bndptr = iset(nvtxs, -1, graph->bndptr); + + nbnd = mincut = 0; + + /* Compute pwgts */ + if (ncon == 1) { + for (i=0; i= 0 && where[i] < nparts); + pwgts[where[i]] += vwgt[i]; + } + } + else { + for (i=0; iobjtype) { + case METIS_OBJTYPE_CUT: + { + ckrinfo_t *myrinfo; + cnbr_t *mynbrs; + + memset(graph->ckrinfo, 0, sizeof(ckrinfo_t)*nvtxs); + cnbrpoolReset(ctrl); + + for (i=0; ickrinfo+i; + + for (j=xadj[i]; jid += adjwgt[j]; + else + myrinfo->ed += adjwgt[j]; + } + + /* Time to compute the particular external degrees */ + if (myrinfo->ed > 0) { + mincut += myrinfo->ed; + + myrinfo->inbr = cnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1); + mynbrs = ctrl->cnbrpool + myrinfo->inbr; + + for (j=xadj[i]; jnnbrs; k++) { + if (mynbrs[k].pid == other) { + mynbrs[k].ed += adjwgt[j]; + break; + } + } + if (k == myrinfo->nnbrs) { + mynbrs[k].pid = other; + mynbrs[k].ed = adjwgt[j]; + myrinfo->nnbrs++; + } + } + } + + ASSERT(myrinfo->nnbrs <= xadj[i+1]-xadj[i]); + + /* Only ed-id>=0 nodes are considered to be in the boundary */ + if (myrinfo->ed-myrinfo->id >= 0) + BNDInsert(nbnd, bndind, bndptr, i); + } + else { + myrinfo->inbr = -1; + } + } + + graph->mincut = mincut/2; + graph->nbnd = nbnd; + + } + ASSERT(CheckBnd2(graph)); + break; + + case METIS_OBJTYPE_VOL: + { + vkrinfo_t *myrinfo; + vnbr_t *mynbrs; + + memset(graph->vkrinfo, 0, sizeof(vkrinfo_t)*nvtxs); + vnbrpoolReset(ctrl); + + /* Compute now the id/ed degrees */ + for (i=0; ivkrinfo+i; + + for (j=xadj[i]; jnid++; + else + myrinfo->ned++; + } + + /* Time to compute the particular external degrees */ + if (myrinfo->ned > 0) { + mincut += myrinfo->ned; + + myrinfo->inbr = vnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1); + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + for (j=xadj[i]; jnnbrs; k++) { + if (mynbrs[k].pid == other) { + mynbrs[k].ned++; + break; + } + } + if (k == myrinfo->nnbrs) { + mynbrs[k].gv = 0; + mynbrs[k].pid = other; + mynbrs[k].ned = 1; + myrinfo->nnbrs++; + } + } + } + ASSERT(myrinfo->nnbrs <= xadj[i+1]-xadj[i]); + } + else { + myrinfo->inbr = -1; + } + } + graph->mincut = mincut/2; + + ComputeKWayVolGains(ctrl, graph); + } + ASSERT(graph->minvol == ComputeVolume(graph, graph->where)); + break; + default: + gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype); + } + +} + + +/*************************************************************************/ +/*! This function projects a partition, and at the same time computes the + parameters for refinement. */ +/*************************************************************************/ +void ProjectKWayPartition(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, j, k, nvtxs, nbnd, nparts, me, other, istart, iend, tid, ted; + idx_t *xadj, *adjncy, *adjwgt; + idx_t *cmap, *where, *bndptr, *bndind, *cwhere, *htable; + graph_t *cgraph; + + WCOREPUSH; + + nparts = ctrl->nparts; + + cgraph = graph->coarser; + cwhere = cgraph->where; + + nvtxs = graph->nvtxs; + cmap = graph->cmap; + xadj = graph->xadj; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + AllocateKWayPartitionMemory(ctrl, graph); + + where = graph->where; + bndind = graph->bndind; + bndptr = iset(nvtxs, -1, graph->bndptr); + + htable = iset(nparts, -1, iwspacemalloc(ctrl, nparts)); + + /* Compute the required info for refinement */ + switch (ctrl->objtype) { + case METIS_OBJTYPE_CUT: + ASSERT(CheckBnd2(cgraph)); + { + ckrinfo_t *myrinfo; + cnbr_t *mynbrs; + + /* go through and project partition and compute id/ed for the nodes */ + for (i=0; ickrinfo[k].ed; /* For optimization */ + } + + memset(graph->ckrinfo, 0, sizeof(ckrinfo_t)*nvtxs); + cnbrpoolReset(ctrl); + + for (nbnd=0, i=0; ickrinfo+i; + + if (cmap[i] == 0) { /* Interior node. Note that cmap[i] = crinfo[cmap[i]].ed */ + for (tid=0, j=istart; jid = tid; + myrinfo->inbr = -1; + } + else { /* Potentially an interface node */ + myrinfo->inbr = cnbrpoolGetNext(ctrl, iend-istart+1); + mynbrs = ctrl->cnbrpool + myrinfo->inbr; + + me = where[i]; + for (tid=0, ted=0, j=istart; jnnbrs; + mynbrs[myrinfo->nnbrs].pid = other; + mynbrs[myrinfo->nnbrs++].ed = adjwgt[j]; + } + else { + mynbrs[k].ed += adjwgt[j]; + } + } + } + myrinfo->id = tid; + myrinfo->ed = ted; + + /* Remove space for edegrees if it was interior */ + if (ted == 0) { + ctrl->nbrpoolcpos -= iend-istart+1; + myrinfo->inbr = -1; + } + else { + if (ted-tid >= 0) + BNDInsert(nbnd, bndind, bndptr, i); + + for (j=0; jnnbrs; j++) + htable[mynbrs[j].pid] = -1; + } + } + } + + graph->nbnd = nbnd; + + } + ASSERT(CheckBnd2(graph)); + break; + + case METIS_OBJTYPE_VOL: + { + vkrinfo_t *myrinfo; + vnbr_t *mynbrs; + + ASSERT(cgraph->minvol == ComputeVolume(cgraph, cgraph->where)); + + /* go through and project partition and compute id/ed for the nodes */ + for (i=0; ivkrinfo[k].ned; /* For optimization */ + } + + memset(graph->vkrinfo, 0, sizeof(vkrinfo_t)*nvtxs); + vnbrpoolReset(ctrl); + + for (i=0; ivkrinfo+i; + + if (cmap[i] == 0) { /* Note that cmap[i] = crinfo[cmap[i]].ed */ + myrinfo->nid = iend-istart; + myrinfo->inbr = -1; + } + else { /* Potentially an interface node */ + myrinfo->inbr = vnbrpoolGetNext(ctrl, iend-istart+1); + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + me = where[i]; + for (tid=0, ted=0, j=istart; jnnbrs; + mynbrs[myrinfo->nnbrs].gv = 0; + mynbrs[myrinfo->nnbrs].pid = other; + mynbrs[myrinfo->nnbrs++].ned = 1; + } + else { + mynbrs[k].ned++; + } + } + } + myrinfo->nid = tid; + myrinfo->ned = ted; + + /* Remove space for edegrees if it was interior */ + if (ted == 0) { + ctrl->nbrpoolcpos -= iend-istart+1; + myrinfo->inbr = -1; + } + else { + for (j=0; jnnbrs; j++) + htable[mynbrs[j].pid] = -1; + } + } + } + + ComputeKWayVolGains(ctrl, graph); + + ASSERT(graph->minvol == ComputeVolume(graph, graph->where)); + } + break; + + default: + gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype); + } + + graph->mincut = cgraph->mincut; + icopy(nparts*graph->ncon, cgraph->pwgts, graph->pwgts); + + FreeGraph(&graph->coarser); + graph->coarser = NULL; + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function computes the boundary definition for balancing. */ +/*************************************************************************/ +void ComputeKWayBoundary(ctrl_t *ctrl, graph_t *graph, idx_t bndtype) +{ + idx_t i, nvtxs, nbnd; + idx_t *bndind, *bndptr; + + nvtxs = graph->nvtxs; + bndind = graph->bndind; + bndptr = iset(nvtxs, -1, graph->bndptr); + + nbnd = 0; + + switch (ctrl->objtype) { + case METIS_OBJTYPE_CUT: + /* Compute the boundary */ + if (bndtype == BNDTYPE_REFINE) { + for (i=0; ickrinfo[i].ed-graph->ckrinfo[i].id >= 0) + BNDInsert(nbnd, bndind, bndptr, i); + } + } + else { /* BNDTYPE_BALANCE */ + for (i=0; ickrinfo[i].ed > 0) + BNDInsert(nbnd, bndind, bndptr, i); + } + } + break; + + case METIS_OBJTYPE_VOL: + /* Compute the boundary */ + if (bndtype == BNDTYPE_REFINE) { + for (i=0; ivkrinfo[i].gv >= 0) + BNDInsert(nbnd, bndind, bndptr, i); + } + } + else { /* BNDTYPE_BALANCE */ + for (i=0; ivkrinfo[i].ned > 0) + BNDInsert(nbnd, bndind, bndptr, i); + } + } + break; + + default: + gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype); + } + + graph->nbnd = nbnd; +} + + +/*************************************************************************/ +/*! This function computes the initial gains in the communication volume */ +/*************************************************************************/ +void ComputeKWayVolGains(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, ii, j, k, l, nvtxs, nparts, me, other, pid; + idx_t *xadj, *vsize, *adjncy, *adjwgt, *where, + *bndind, *bndptr, *ophtable; + vkrinfo_t *myrinfo, *orinfo; + vnbr_t *mynbrs, *onbrs; + + WCOREPUSH; + + nparts = ctrl->nparts; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vsize = graph->vsize; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + where = graph->where; + bndind = graph->bndind; + bndptr = iset(nvtxs, -1, graph->bndptr); + + ophtable = iset(nparts, -1, iwspacemalloc(ctrl, nparts)); + + /* Compute the volume gains */ + graph->minvol = graph->nbnd = 0; + for (i=0; ivkrinfo+i; + myrinfo->gv = IDX_MIN; + + if (myrinfo->nnbrs > 0) { + me = where[i]; + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + graph->minvol += myrinfo->nnbrs*vsize[i]; + + for (j=xadj[i]; jvkrinfo+ii; + onbrs = ctrl->vnbrpool + orinfo->inbr; + + for (k=0; knnbrs; k++) + ophtable[onbrs[k].pid] = k; + ophtable[other] = 1; /* this is to simplify coding */ + + if (me == other) { + /* Find which domains 'i' is connected to but 'ii' is not + and update their gain */ + for (k=0; knnbrs; k++) { + if (ophtable[mynbrs[k].pid] == -1) + mynbrs[k].gv -= vsize[ii]; + } + } + else { + ASSERT(ophtable[me] != -1); + + if (onbrs[ophtable[me]].ned == 1) { + /* I'm the only connection of 'ii' in 'me' */ + /* Increase the gains for all the common domains between 'i' and 'ii' */ + for (k=0; knnbrs; k++) { + if (ophtable[mynbrs[k].pid] != -1) + mynbrs[k].gv += vsize[ii]; + } + } + else { + /* Find which domains 'i' is connected to and 'ii' is not + and update their gain */ + for (k=0; knnbrs; k++) { + if (ophtable[mynbrs[k].pid] == -1) + mynbrs[k].gv -= vsize[ii]; + } + } + } + + /* Reset the marker vector */ + for (k=0; knnbrs; k++) + ophtable[onbrs[k].pid] = -1; + ophtable[other] = -1; + } + + /* Compute the max vgain */ + for (k=0; knnbrs; k++) { + if (mynbrs[k].gv > myrinfo->gv) + myrinfo->gv = mynbrs[k].gv; + } + + /* Add the extra gain due to id == 0 */ + if (myrinfo->ned > 0 && myrinfo->nid == 0) + myrinfo->gv += vsize[i]; + } + + if (myrinfo->gv >= 0) + BNDInsert(graph->nbnd, bndind, bndptr, i); + } + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function checks if the partition weights are within the balance +contraints */ +/*************************************************************************/ +int IsBalanced(ctrl_t *ctrl, graph_t *graph, real_t ffactor) +{ + return + (ComputeLoadImbalanceDiff(graph, ctrl->nparts, ctrl->pijbm, ctrl->ubfactors) + <= ffactor); +} + diff --git a/src/metis/libmetis/macros.h b/src/metis/libmetis/macros.h new file mode 100644 index 0000000..3f6f7d9 --- /dev/null +++ b/src/metis/libmetis/macros.h @@ -0,0 +1,258 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * macros.h + * + * This file contains macros used in multilevel + * + * Started 9/25/94 + * George + * + * $Id: macros.h 10060 2011-06-02 18:56:30Z karypis $ + * + */ + +#ifndef _LIBMETIS_MACROS_H_ +#define _LIBMETIS_MACROS_H_ + +/************************************************************************* +* The following macro returns a random number in the specified range +**************************************************************************/ +#define AND(a, b) ((a) < 0 ? ((-(a))&(b)) : ((a)&(b))) +#define OR(a, b) ((a) < 0 ? -((-(a))|(b)) : ((a)|(b))) +#define XOR(a, b) ((a) < 0 ? -((-(a))^(b)) : ((a)^(b))) + +//#define icopy(n, a, b) (idx_t *)memcpy((void *)(b), (void *)(a), sizeof(idx_t)*(n)) + +#define HASHFCT(key, size) ((key)%(size)) +#define SWAP gk_SWAP + +/* gets the appropriate option value */ +#define GETOPTION(options, idx, defval) \ + ((options) == NULL || (options)[idx] == -1 ? defval : (options)[idx]) + +/* converts a user provided ufactor into a real ubfactor */ +#define I2RUBFACTOR(ufactor) (1.0+0.001*(ufactor)) + +/* set/reset the current workspace core */ +#define WCOREPUSH wspacepush(ctrl) +#define WCOREPOP wspacepop(ctrl) + + + +/************************************************************************* +* These macros insert and remove nodes from a Direct Access list +**************************************************************************/ +#define ListInsert(n, lind, lptr, i) \ + do { \ + ASSERT(lptr[i] == -1); \ + lind[n] = i; \ + lptr[i] = (n)++;\ + } while(0) + +#define ListDelete(n, lind, lptr, i) \ + do { \ + ASSERT(lptr[i] != -1); \ + lind[lptr[i]] = lind[--(n)]; \ + lptr[lind[n]] = lptr[i]; \ + lptr[i] = -1; \ + } while(0) + + +/************************************************************************* +* These macros insert and remove nodes from the boundary list +**************************************************************************/ +#define BNDInsert(nbnd, bndind, bndptr, vtx) \ + ListInsert(nbnd, bndind, bndptr, vtx) + +#define BNDDelete(nbnd, bndind, bndptr, vtx) \ + ListDelete(nbnd, bndind, bndptr, vtx) + + +/************************************************************************* +* These macros deal with id/ed updating during k-way refinement +**************************************************************************/ +#define UpdateMovedVertexInfoAndBND(i, from, k, to, myrinfo, mynbrs, where, \ + nbnd, bndptr, bndind, bndtype) \ + do { \ + where[i] = to; \ + myrinfo->ed += myrinfo->id-mynbrs[k].ed; \ + SWAP(myrinfo->id, mynbrs[k].ed, j); \ + if (mynbrs[k].ed == 0) \ + mynbrs[k] = mynbrs[--myrinfo->nnbrs]; \ + else \ + mynbrs[k].pid = from; \ + \ + /* Update the boundary information. Both deletion and addition is \ + allowed as this routine can be used for moving arbitrary nodes. */ \ + if (bndtype == BNDTYPE_REFINE) { \ + if (bndptr[i] != -1 && myrinfo->ed - myrinfo->id < 0) \ + BNDDelete(nbnd, bndind, bndptr, i); \ + if (bndptr[i] == -1 && myrinfo->ed - myrinfo->id >= 0) \ + BNDInsert(nbnd, bndind, bndptr, i); \ + } \ + else { \ + if (bndptr[i] != -1 && myrinfo->ed <= 0) \ + BNDDelete(nbnd, bndind, bndptr, i); \ + if (bndptr[i] == -1 && myrinfo->ed > 0) \ + BNDInsert(nbnd, bndind, bndptr, i); \ + } \ + } while(0) + + +#define UpdateAdjacentVertexInfoAndBND(ctrl, vid, adjlen, me, from, to, \ + myrinfo, ewgt, nbnd, bndptr, bndind, bndtype) \ + do { \ + idx_t k; \ + cnbr_t *mynbrs; \ + \ + if (myrinfo->inbr == -1) { \ + myrinfo->inbr = cnbrpoolGetNext(ctrl, adjlen+1); \ + myrinfo->nnbrs = 0; \ + } \ + ASSERT(CheckRInfo(ctrl, myrinfo)); \ + \ + mynbrs = ctrl->cnbrpool + myrinfo->inbr; \ + \ + /* Update global ID/ED and boundary */ \ + if (me == from) { \ + INC_DEC(myrinfo->ed, myrinfo->id, (ewgt)); \ + if (bndtype == BNDTYPE_REFINE) { \ + if (myrinfo->ed-myrinfo->id >= 0 && bndptr[(vid)] == -1) \ + BNDInsert(nbnd, bndind, bndptr, (vid)); \ + } \ + else { \ + if (myrinfo->ed > 0 && bndptr[(vid)] == -1) \ + BNDInsert(nbnd, bndind, bndptr, (vid)); \ + } \ + } \ + else if (me == to) { \ + INC_DEC(myrinfo->id, myrinfo->ed, (ewgt)); \ + if (bndtype == BNDTYPE_REFINE) { \ + if (myrinfo->ed-myrinfo->id < 0 && bndptr[(vid)] != -1) \ + BNDDelete(nbnd, bndind, bndptr, (vid)); \ + } \ + else { \ + if (myrinfo->ed <= 0 && bndptr[(vid)] != -1) \ + BNDDelete(nbnd, bndind, bndptr, (vid)); \ + } \ + } \ + \ + /* Remove contribution from the .ed of 'from' */ \ + if (me != from) { \ + for (k=0; knnbrs; k++) { \ + if (mynbrs[k].pid == from) { \ + if (mynbrs[k].ed == (ewgt)) \ + mynbrs[k] = mynbrs[--myrinfo->nnbrs]; \ + else \ + mynbrs[k].ed -= (ewgt); \ + break; \ + } \ + } \ + } \ + \ + /* Add contribution to the .ed of 'to' */ \ + if (me != to) { \ + for (k=0; knnbrs; k++) { \ + if (mynbrs[k].pid == to) { \ + mynbrs[k].ed += (ewgt); \ + break; \ + } \ + } \ + if (k == myrinfo->nnbrs) { \ + mynbrs[k].pid = to; \ + mynbrs[k].ed = (ewgt); \ + myrinfo->nnbrs++; \ + } \ + } \ + \ + ASSERT(CheckRInfo(ctrl, myrinfo));\ + } while(0) + + +#define UpdateQueueInfo(queue, vstatus, vid, me, from, to, myrinfo, oldnnbrs, \ + nupd, updptr, updind, bndtype) \ + do { \ + real_t rgain; \ + \ + if (me == to || me == from || oldnnbrs != myrinfo->nnbrs) { \ + rgain = (myrinfo->nnbrs > 0 ? \ + 1.0*myrinfo->ed/sqrt(myrinfo->nnbrs) : 0.0) - myrinfo->id; \ + \ + if (bndtype == BNDTYPE_REFINE) { \ + if (vstatus[(vid)] == VPQSTATUS_PRESENT) { \ + if (myrinfo->ed-myrinfo->id >= 0) \ + rpqUpdate(queue, (vid), rgain); \ + else { \ + rpqDelete(queue, (vid)); \ + vstatus[(vid)] = VPQSTATUS_NOTPRESENT; \ + ListDelete(nupd, updind, updptr, (vid)); \ + } \ + } \ + else if (vstatus[(vid)] == VPQSTATUS_NOTPRESENT && myrinfo->ed-myrinfo->id >= 0) { \ + rpqInsert(queue, (vid), rgain); \ + vstatus[(vid)] = VPQSTATUS_PRESENT; \ + ListInsert(nupd, updind, updptr, (vid)); \ + } \ + } \ + else { \ + if (vstatus[(vid)] == VPQSTATUS_PRESENT) { \ + if (myrinfo->ed > 0) \ + rpqUpdate(queue, (vid), rgain); \ + else { \ + rpqDelete(queue, (vid)); \ + vstatus[(vid)] = VPQSTATUS_NOTPRESENT; \ + ListDelete(nupd, updind, updptr, (vid)); \ + } \ + } \ + else if (vstatus[(vid)] == VPQSTATUS_NOTPRESENT && myrinfo->ed > 0) { \ + rpqInsert(queue, (vid), rgain); \ + vstatus[(vid)] = VPQSTATUS_PRESENT; \ + ListInsert(nupd, updind, updptr, (vid)); \ + } \ + } \ + } \ + } while(0) + + + +/*************************************************************************/ +/*! This macro determines the set of subdomains that a vertex can move to + without increasins the maxndoms. */ +/*************************************************************************/ +#define SelectSafeTargetSubdomains(myrinfo, mynbrs, nads, adids, maxndoms, safetos, vtmp) \ + do { \ + idx_t j, k, l, nadd, to; \ + for (j=0; jnnbrs; j++) { \ + safetos[to = mynbrs[j].pid] = 0; \ + \ + /* uncompress the connectivity info for the 'to' subdomain */ \ + for (k=0; knnbrs; k++) { \ + if (k == j) \ + continue; \ + \ + l = mynbrs[k].pid; \ + if (vtmp[l] == 0) { \ + if (nads[l] > maxndoms-1) { \ + nadd = maxndoms; \ + break; \ + } \ + nadd++; \ + } \ + } \ + if (nads[to]+nadd <= maxndoms) \ + safetos[to] = 1; \ + if (nadd == 0) \ + safetos[to] = 2; \ + \ + /* cleanup the connectivity info due to the 'to' subdomain */ \ + for (k=0; k=0; n--) { + if (x[n] > y[n]) + return 0; + } + + return 1; +} + + +/*************************************************************************/ +/*! This function compares two vectors x & y and returns true + if \forall i, x[i] >= y[i]. +*/ +/**************************************************************************/ +int rvecge(idx_t n, real_t *x, real_t *y) +{ + for (n--; n>=0; n--) { + if (x[n] < y[n]) + return 0; + } + + return 1; +} + + +/*************************************************************************/ +/*! This function compares vectors x1+x2 against y and returns true + if \forall i, x1[i]+x2[i] <= y[i]. +*/ +/**************************************************************************/ +int rvecsumle(idx_t n, real_t *x1, real_t *x2, real_t *y) +{ + for (n--; n>=0; n--) { + if (x1[n]+x2[n] > y[n]) + return 0; + } + + return 1; +} + + +/*************************************************************************/ +/*! This function returns max_i(x[i]-y[i]) */ +/**************************************************************************/ +real_t rvecmaxdiff(idx_t n, real_t *x, real_t *y) +{ + real_t max; + + max = x[0]-y[0]; + + for (n--; n>0; n--) { + if (max < x[n]-y[n]) + max = x[n]-y[n]; + } + + return max; +} + + +/*************************************************************************/ +/*! This function returns true if \forall i, x[i] <= z[i]. */ +/**************************************************************************/ +int ivecle(idx_t n, idx_t *x, idx_t *z) +{ + for (n--; n>=0; n--) { + if (x[n] > z[n]) + return 0; + } + + return 1; +} + + +/*************************************************************************/ +/*! This function returns true if \forall i, x[i] >= z[i]. */ +/**************************************************************************/ +int ivecge(idx_t n, idx_t *x, idx_t *z) +{ + for (n--; n>=0; n--) { + if (x[n] < z[n]) + return 0; + } + + return 1; +} + + +/*************************************************************************/ +/*! This function returns true if \forall i, a*x[i]+y[i] <= z[i]. */ +/**************************************************************************/ +int ivecaxpylez(idx_t n, idx_t a, idx_t *x, idx_t *y, idx_t *z) +{ + for (n--; n>=0; n--) { + if (a*x[n]+y[n] > z[n]) + return 0; + } + + return 1; +} + + +/*************************************************************************/ +/*! This function returns true if \forall i, a*x[i]+y[i] >= z[i]. */ +/**************************************************************************/ +int ivecaxpygez(idx_t n, idx_t a, idx_t *x, idx_t *y, idx_t *z) +{ + for (n--; n>=0; n--) { + if (a*x[n]+y[n] < z[n]) + return 0; + } + + return 1; +} + + +/*************************************************************************/ +/*! This function checks if v+u2 provides a better balance in the weight + vector that v+u1 */ +/*************************************************************************/ +int BetterVBalance(idx_t ncon, real_t *invtvwgt, idx_t *v_vwgt, idx_t *u1_vwgt, + idx_t *u2_vwgt) +{ + idx_t i; + real_t sum1=0.0, sum2=0.0, diff1=0.0, diff2=0.0; + + for (i=0; i= 0); +} + + +/*************************************************************************/ +/*! This function takes two ubfactor-centered load imbalance vectors x & y, + and returns true if y is better balanced than x. */ +/*************************************************************************/ +int BetterBalance2Way(idx_t n, real_t *x, real_t *y) +{ + real_t nrm1=0.0, nrm2=0.0; + + for (--n; n>=0; n--) { + if (x[n] > 0) nrm1 += x[n]*x[n]; + if (y[n] > 0) nrm2 += y[n]*y[n]; + } + return nrm2 < nrm1; +} + + +/*************************************************************************/ +/*! Given a vertex and two weights, this function returns 1, if the second + partition will be more balanced than the first after the weighted + additional of that vertex. + The balance determination takes into account the ideal target weights + of the two partitions. +*/ +/*************************************************************************/ +int BetterBalanceKWay(idx_t ncon, idx_t *vwgt, real_t *ubvec, + idx_t a1, idx_t *pt1, real_t *bm1, + idx_t a2, idx_t *pt2, real_t *bm2) +{ + idx_t i; + real_t tmp, nrm1=0.0, nrm2=0.0, max1=0.0, max2=0.0; + + for (i=0; i max1 ? tmp : max1); + + tmp = bm2[i]*(pt2[i]+a2*vwgt[i]) - ubvec[i]; + //printf("%+.4f ", (float)tmp); + nrm2 += tmp*tmp; + max2 = (tmp > max2 ? tmp : max2); + + //printf("%4d %4d %4d %4d %4d %4d %4d %.2f\n", + // (int)vwgt[i], + // (int)a1, (int)pt1[i], (int)tpt1[i], + // (int)a2, (int)pt2[i], (int)tpt2[i], ubvec[i]); + } + //printf(" %.3f %.3f %.3f %.3f\n", (float)max1, (float)nrm1, (float)max2, (float)nrm2); + + if (max2 < max1) + return 1; + + if (max2 == max1 && nrm2 < nrm1) + return 1; + + return 0; +} + + +/*************************************************************************/ +/*! Computes the maximum load imbalance of a partitioning solution over + all the constraints. */ +/**************************************************************************/ +real_t ComputeLoadImbalance(graph_t *graph, idx_t nparts, real_t *pijbm) +{ + idx_t i, j, ncon, *pwgts; + real_t max, cur; + + ncon = graph->ncon; + pwgts = graph->pwgts; + + max = 1.0; + for (i=0; i max) + max = cur; + } + } + + return max; +} + + +/*************************************************************************/ +/*! Computes the maximum load imbalance difference of a partitioning + solution over all the constraints. + The difference is defined with respect to the allowed maximum + unbalance for the respective constraint. + */ +/**************************************************************************/ +real_t ComputeLoadImbalanceDiff(graph_t *graph, idx_t nparts, real_t *pijbm, + real_t *ubvec) +{ + idx_t i, j, ncon, *pwgts; + real_t max, cur; + + ncon = graph->ncon; + pwgts = graph->pwgts; + + max = -1.0; + for (i=0; i max) + max = cur; + } + } + + return max; +} + + +/*************************************************************************/ +/*! Computes the difference between load imbalance of each constraint across + the partitions minus the desired upper bound on the load imabalnce. + It also returns the maximum load imbalance across the partitions & + constraints. */ +/**************************************************************************/ +real_t ComputeLoadImbalanceDiffVec(graph_t *graph, idx_t nparts, real_t *pijbm, + real_t *ubfactors, real_t *diffvec) +{ + idx_t i, j, ncon, *pwgts; + real_t cur, max; + + ncon = graph->ncon; + pwgts = graph->pwgts; + + for (max=-1.0, i=0; i diffvec[i]) + diffvec[i] = cur; + } + if (max < diffvec[i]) + max = diffvec[i]; + } + + return max; +} + + +/*************************************************************************/ +/*! Computes the load imbalance of each constraint across the partitions. */ +/**************************************************************************/ +void ComputeLoadImbalanceVec(graph_t *graph, idx_t nparts, real_t *pijbm, + real_t *lbvec) +{ + idx_t i, j, ncon, *pwgts; + real_t cur; + + ncon = graph->ncon; + pwgts = graph->pwgts; + + for (i=0; i lbvec[i]) + lbvec[i] = cur; + } + } +} + + diff --git a/src/metis/libmetis/mesh.c b/src/metis/libmetis/mesh.c new file mode 100644 index 0000000..3c52612 --- /dev/null +++ b/src/metis/libmetis/mesh.c @@ -0,0 +1,412 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * mesh.c + * + * This file contains routines for converting 3D and 4D finite element + * meshes into dual or nodal graphs + * + * Started 8/18/97 + * George + * + * $Id: mesh.c 13804 2013-03-04 23:49:08Z karypis $ + * + */ + +#include "metislib.h" + + +/*****************************************************************************/ +/*! This function creates a graph corresponding to the dual of a finite element + mesh. + + \param ne is the number of elements in the mesh. + \param nn is the number of nodes in the mesh. + \param eptr is an array of size ne+1 used to mark the start and end + locations in the nind array. + \param eind is an array that stores for each element the set of node IDs + (indices) that it is made off. The length of this array is equal + to the total number of nodes over all the mesh elements. + \param ncommon is the minimum number of nodes that two elements must share + in order to be connected via an edge in the dual graph. + \param numflag is either 0 or 1 indicating if the numbering of the nodes + starts from 0 or 1, respectively. The same numbering is used for the + returned graph as well. + \param r_xadj indicates where the adjacency list of each vertex is stored + in r_adjncy. The memory for this array is allocated by this routine. + It can be freed by calling METIS_free(). + \param r_adjncy stores the adjacency list of each vertex in the generated + dual graph. The memory for this array is allocated by this routine. + It can be freed by calling METIS_free(). + +*/ +/*****************************************************************************/ +int METIS_MeshToDual(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, + idx_t *ncommon, idx_t *numflag, idx_t **r_xadj, idx_t **r_adjncy) +{ + int sigrval=0, renumber=0; + + /* set up malloc cleaning code and signal catchers */ + if (!gk_malloc_init()) + return METIS_ERROR_MEMORY; + + gk_sigtrap(); + + if ((sigrval = gk_sigcatch()) != 0) + goto SIGTHROW; + + + /* renumber the mesh */ + if (*numflag == 1) { + ChangeMesh2CNumbering(*ne, eptr, eind); + renumber = 1; + } + + /* create dual graph */ + *r_xadj = *r_adjncy = NULL; + CreateGraphDual(*ne, *nn, eptr, eind, *ncommon, r_xadj, r_adjncy); + + +SIGTHROW: + if (renumber) + ChangeMesh2FNumbering(*ne, eptr, eind, *ne, *r_xadj, *r_adjncy); + + gk_siguntrap(); + gk_malloc_cleanup(0); + + if (sigrval != 0) { + if (*r_xadj != NULL) + free(*r_xadj); + if (*r_adjncy != NULL) + free(*r_adjncy); + *r_xadj = *r_adjncy = NULL; + } + + return metis_rcode(sigrval); +} + + +/*****************************************************************************/ +/*! This function creates a graph corresponding to (almost) the nodal of a + finite element mesh. In the nodal graph, each node is connected to the + nodes corresponding to the union of nodes present in all the elements + in which that node belongs. + + \param ne is the number of elements in the mesh. + \param nn is the number of nodes in the mesh. + \param eptr is an array of size ne+1 used to mark the start and end + locations in the nind array. + \param eind is an array that stores for each element the set of node IDs + (indices) that it is made off. The length of this array is equal + to the total number of nodes over all the mesh elements. + \param numflag is either 0 or 1 indicating if the numbering of the nodes + starts from 0 or 1, respectively. The same numbering is used for the + returned graph as well. + \param r_xadj indicates where the adjacency list of each vertex is stored + in r_adjncy. The memory for this array is allocated by this routine. + It can be freed by calling METIS_free(). + \param r_adjncy stores the adjacency list of each vertex in the generated + dual graph. The memory for this array is allocated by this routine. + It can be freed by calling METIS_free(). + +*/ +/*****************************************************************************/ +int METIS_MeshToNodal(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, + idx_t *numflag, idx_t **r_xadj, idx_t **r_adjncy) +{ + int sigrval=0, renumber=0; + + /* set up malloc cleaning code and signal catchers */ + if (!gk_malloc_init()) + return METIS_ERROR_MEMORY; + + gk_sigtrap(); + + if ((sigrval = gk_sigcatch()) != 0) + goto SIGTHROW; + + + /* renumber the mesh */ + if (*numflag == 1) { + ChangeMesh2CNumbering(*ne, eptr, eind); + renumber = 1; + } + + /* create nodal graph */ + *r_xadj = *r_adjncy = NULL; + CreateGraphNodal(*ne, *nn, eptr, eind, r_xadj, r_adjncy); + + +SIGTHROW: + if (renumber) + ChangeMesh2FNumbering(*ne, eptr, eind, *nn, *r_xadj, *r_adjncy); + + gk_siguntrap(); + gk_malloc_cleanup(0); + + if (sigrval != 0) { + if (*r_xadj != NULL) + free(*r_xadj); + if (*r_adjncy != NULL) + free(*r_adjncy); + *r_xadj = *r_adjncy = NULL; + } + + return metis_rcode(sigrval); +} + + +/*****************************************************************************/ +/*! This function creates the dual of a finite element mesh */ +/*****************************************************************************/ +void CreateGraphDual(idx_t ne, idx_t nn, idx_t *eptr, idx_t *eind, idx_t ncommon, + idx_t **r_xadj, idx_t **r_adjncy) +{ + idx_t i, j, nnbrs; + idx_t *nptr, *nind; + idx_t *xadj, *adjncy; + idx_t *marker, *nbrs; + + if (ncommon < 1) { + printf(" Increased ncommon to 1, as it was initially %"PRIDX"\n", ncommon); + ncommon = 1; + } + + /* construct the node-element list first */ + nptr = ismalloc(nn+1, 0, "CreateGraphDual: nptr"); + nind = imalloc(eptr[ne], "CreateGraphDual: nind"); + + for (i=0; i= ncommon || + overlap >= elen-1 || + overlap >= eptr[l+1]-eptr[l]-1) + nbrs[j++] = l; + marker[l] = 0; + } + + return j; +} + + +/*****************************************************************************/ +/*! This function creates the (almost) nodal of a finite element mesh */ +/*****************************************************************************/ +void CreateGraphNodal(idx_t ne, idx_t nn, idx_t *eptr, idx_t *eind, + idx_t **r_xadj, idx_t **r_adjncy) +{ + idx_t i, j, nnbrs; + idx_t *nptr, *nind; + idx_t *xadj, *adjncy; + idx_t *marker, *nbrs; + + + /* construct the node-element list first */ + nptr = ismalloc(nn+1, 0, "CreateGraphNodal: nptr"); + nind = imalloc(eptr[ne], "CreateGraphNodal: nind"); + + for (i=0; ieptr, &mesh->eind, &mesh->ewgt, &mesh, LTERM); + + *r_mesh = NULL; +} + diff --git a/src/metis/libmetis/meshpart.c b/src/metis/libmetis/meshpart.c new file mode 100644 index 0000000..a66d106 --- /dev/null +++ b/src/metis/libmetis/meshpart.c @@ -0,0 +1,262 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * meshpart.c + * + * This file contains routines for partitioning finite element meshes. + * + * Started 9/29/97 + * George + * + * $Id: meshpart.c 13931 2013-03-29 16:48:48Z karypis $ + * + */ + +#include "metislib.h" + + +/************************************************************************* +* This function partitions a finite element mesh by partitioning its nodal +* graph using KMETIS and then assigning elements in a load balanced fashion. +**************************************************************************/ +int METIS_PartMeshNodal(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, + idx_t *vwgt, idx_t *vsize, idx_t *nparts, real_t *tpwgts, + idx_t *options, idx_t *objval, idx_t *epart, idx_t *npart) +{ + int sigrval=0, renumber=0, ptype; + idx_t *xadj=NULL, *adjncy=NULL; + idx_t ncon=1, pnumflag=0; + int rstatus=METIS_OK; + + /* set up malloc cleaning code and signal catchers */ + if (!gk_malloc_init()) + return METIS_ERROR_MEMORY; + + gk_sigtrap(); + + if ((sigrval = gk_sigcatch()) != 0) + goto SIGTHROW; + + renumber = GETOPTION(options, METIS_OPTION_NUMBERING, 0); + ptype = GETOPTION(options, METIS_OPTION_PTYPE, METIS_PTYPE_KWAY); + + /* renumber the mesh */ + if (renumber) { + ChangeMesh2CNumbering(*ne, eptr, eind); + options[METIS_OPTION_NUMBERING] = 0; + } + + /* get the nodal graph */ + rstatus = METIS_MeshToNodal(ne, nn, eptr, eind, &pnumflag, &xadj, &adjncy); + if (rstatus != METIS_OK) + raise(SIGERR); + + /* partition the graph */ + if (ptype == METIS_PTYPE_KWAY) + rstatus = METIS_PartGraphKway(nn, &ncon, xadj, adjncy, vwgt, vsize, NULL, + nparts, tpwgts, NULL, options, objval, npart); + else + rstatus = METIS_PartGraphRecursive(nn, &ncon, xadj, adjncy, vwgt, vsize, NULL, + nparts, tpwgts, NULL, options, objval, npart); + + if (rstatus != METIS_OK) + raise(SIGERR); + + /* partition the other side of the mesh */ + InduceRowPartFromColumnPart(*ne, eptr, eind, epart, npart, *nparts, tpwgts); + + +SIGTHROW: + if (renumber) { + ChangeMesh2FNumbering2(*ne, *nn, eptr, eind, epart, npart); + options[METIS_OPTION_NUMBERING] = 1; + } + + METIS_Free(xadj); + METIS_Free(adjncy); + + gk_siguntrap(); + gk_malloc_cleanup(0); + + return metis_rcode(sigrval); +} + + + +/************************************************************************* +* This function partitions a finite element mesh by partitioning its dual +* graph using KMETIS and then assigning nodes in a load balanced fashion. +**************************************************************************/ +int METIS_PartMeshDual(idx_t *ne, idx_t *nn, idx_t *eptr, idx_t *eind, + idx_t *vwgt, idx_t *vsize, idx_t *ncommon, idx_t *nparts, + real_t *tpwgts, idx_t *options, idx_t *objval, idx_t *epart, + idx_t *npart) +{ + int sigrval=0, renumber=0, ptype; + idx_t i, j; + idx_t *xadj=NULL, *adjncy=NULL, *nptr=NULL, *nind=NULL; + idx_t ncon=1, pnumflag=0; + int rstatus = METIS_OK; + + /* set up malloc cleaning code and signal catchers */ + if (!gk_malloc_init()) + return METIS_ERROR_MEMORY; + + gk_sigtrap(); + + if ((sigrval = gk_sigcatch()) != 0) + goto SIGTHROW; + + renumber = GETOPTION(options, METIS_OPTION_NUMBERING, 0); + ptype = GETOPTION(options, METIS_OPTION_PTYPE, METIS_PTYPE_KWAY); + + /* renumber the mesh */ + if (renumber) { + ChangeMesh2CNumbering(*ne, eptr, eind); + options[METIS_OPTION_NUMBERING] = 0; + } + + /* get the dual graph */ + rstatus = METIS_MeshToDual(ne, nn, eptr, eind, ncommon, &pnumflag, &xadj, &adjncy); + if (rstatus != METIS_OK) + raise(SIGERR); + + /* partition the graph */ + if (ptype == METIS_PTYPE_KWAY) + rstatus = METIS_PartGraphKway(ne, &ncon, xadj, adjncy, vwgt, vsize, NULL, + nparts, tpwgts, NULL, options, objval, epart); + else + rstatus = METIS_PartGraphRecursive(ne, &ncon, xadj, adjncy, vwgt, vsize, NULL, + nparts, tpwgts, NULL, options, objval, epart); + + if (rstatus != METIS_OK) + raise(SIGERR); + + + /* construct the node-element list */ + nptr = ismalloc(*nn+1, 0, "METIS_PartMeshDual: nptr"); + nind = imalloc(eptr[*ne], "METIS_PartMeshDual: nind"); + + for (i=0; i<*ne; i++) { + for (j=eptr[i]; j 0); + + /* assign it first to the domain with most things in common */ + rpart[i] = nbrdom[iargmax(nnbrs, nbrwgt)]; + + /* if overweight, assign it to the light domain */ + if (pwgts[rpart[i]] > itpwgts[rpart[i]]) { + for (j=0; j + +#if defined(ENABLE_OPENMP) + #include +#endif + + +#include +#include +#include + +#include +#include +#include +#include + + +#if defined(COMPILER_MSC) +#if defined(rint) + #undef rint +#endif +#define rint(x) ((idx_t)((x)+0.5)) /* MSC does not have rint() function */ +#endif + +#endif diff --git a/src/metis/libmetis/minconn.c b/src/metis/libmetis/minconn.c new file mode 100644 index 0000000..86dc90f --- /dev/null +++ b/src/metis/libmetis/minconn.c @@ -0,0 +1,729 @@ +/*! +\file +\brief Functions that deal with prunning the number of adjacent subdomains in kmetis + +\date Started 7/15/98 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version $Id: minconn.c 10513 2011-07-07 22:06:03Z karypis $ +*/ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function computes the subdomain graph storing the result in the + pre-allocated worspace arrays */ +/*************************************************************************/ +void ComputeSubDomainGraph(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, ii, j, pid, other, nparts, nvtxs, nnbrs; + idx_t *xadj, *adjncy, *adjwgt, *where; + idx_t *pptr, *pind; + idx_t nads=0, *vadids, *vadwgts; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + where = graph->where; + + nparts = ctrl->nparts; + + vadids = ctrl->pvec1; + vadwgts = iset(nparts, 0, ctrl->pvec2); + + pptr = iwspacemalloc(ctrl, nparts+1); + pind = iwspacemalloc(ctrl, nvtxs); + iarray2csr(nvtxs, nparts, where, pptr, pind); + + for (pid=0; pidobjtype) { + case METIS_OBJTYPE_CUT: + { + ckrinfo_t *rinfo; + cnbr_t *nbrs; + + rinfo = graph->ckrinfo; + for (nads=0, ii=pptr[pid]; ii 0) { + nnbrs = rinfo[i].nnbrs; + nbrs = ctrl->cnbrpool + rinfo[i].inbr; + + for (j=0; jvkrinfo; + for (nads=0, ii=pptr[pid]; ii 0) { + nnbrs = rinfo[i].nnbrs; + nbrs = ctrl->vnbrpool + rinfo[i].inbr; + + for (j=0; jobjtype); + } + + /* See if you have enough memory to store the adjacent info for that subdomain */ + if (ctrl->maxnads[pid] < nads) { + ctrl->maxnads[pid] = 2*nads; + ctrl->adids[pid] = irealloc(ctrl->adids[pid], ctrl->maxnads[pid], + "ComputeSubDomainGraph: adids[pid]"); + ctrl->adwgts[pid] = irealloc(ctrl->adwgts[pid], ctrl->maxnads[pid], + "ComputeSubDomainGraph: adids[pid]"); + } + + ctrl->nads[pid] = nads; + for (j=0; jadids[pid][j] = vadids[j]; + ctrl->adwgts[pid][j] = vadwgts[vadids[j]]; + + vadwgts[vadids[j]] = 0; + } + } + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function updates the weight of an edge in the subdomain graph by + adding to it the value of ewgt. The update can either increase or + decrease the weight of the subdomain edge based on the value of ewgt. + + \param u is the ID of one of the incident subdomains to the edge + \param v is the ID of the other incident subdomains to the edge + \param ewgt is the weight to be added to the subdomain edge + \param nparts is the number of subdomains + \param r_maxndoms is the maximum number of adjacent subdomains and is + updated as necessary. The update is skipped if a NULL value is + supplied. +*/ +/*************************************************************************/ +void UpdateEdgeSubDomainGraph(ctrl_t *ctrl, idx_t u, idx_t v, idx_t ewgt, + idx_t *r_maxndoms) +{ + idx_t i, j, nads; + + if (ewgt == 0) + return; + + for (i=0; i<2; i++) { + nads = ctrl->nads[u]; + /* Find the edge */ + for (j=0; jadids[u][j] == v) { + ctrl->adwgts[u][j] += ewgt; + break; + } + } + + if (j == nads) { + /* Deal with the case in which the edge was not found */ + ASSERT(ewgt > 0); + if (ctrl->maxnads[u] == nads) { + ctrl->maxnads[u] = 2*(nads+1); + ctrl->adids[u] = irealloc(ctrl->adids[u], ctrl->maxnads[u], + "IncreaseEdgeSubDomainGraph: adids[pid]"); + ctrl->adwgts[u] = irealloc(ctrl->adwgts[u], ctrl->maxnads[u], + "IncreaseEdgeSubDomainGraph: adids[pid]"); + } + ctrl->adids[u][nads] = v; + ctrl->adwgts[u][nads] = ewgt; + nads++; + if (r_maxndoms != NULL && nads > *r_maxndoms) { + printf("You just increased the maxndoms: %"PRIDX" %"PRIDX"\n", + nads, *r_maxndoms); + *r_maxndoms = nads; + } + } + else { + /* See if the updated edge becomes 0 */ + ASSERT(ctrl->adwgts[u][j] >= 0); + if (ctrl->adwgts[u][j] == 0) { + ctrl->adids[u][j] = ctrl->adids[u][nads-1]; + ctrl->adwgts[u][j] = ctrl->adwgts[u][nads-1]; + nads--; + if (r_maxndoms != NULL && nads+1 == *r_maxndoms) + *r_maxndoms = ctrl->nads[iargmax(ctrl->nparts, ctrl->nads)]; + } + } + ctrl->nads[u] = nads; + + SWAP(u, v, j); + } +} + + +/*************************************************************************/ +/*! This function computes the subdomain graph */ +/*************************************************************************/ +void EliminateSubDomainEdges(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, ii, j, k, ncon, nparts, scheme, pid_from, pid_to, me, other, nvtxs, + total, max, avg, totalout, nind=0, ncand=0, ncand2, target, target2, + nadd, bestnadd=0; + idx_t min, move, *cpwgt; + idx_t *xadj, *adjncy, *vwgt, *adjwgt, *pwgts, *where, *maxpwgt, + *mypmat, *otherpmat, *kpmat, *ind; + idx_t *nads, **adids, **adwgts; + ikv_t *cand, *cand2; + ipq_t queue; + real_t *tpwgts, badfactor=1.4; + idx_t *pptr, *pind; + idx_t *vmarker=NULL, *pmarker=NULL, *modind=NULL; /* volume specific work arrays */ + + WCOREPUSH; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + adjwgt = (ctrl->objtype == METIS_OBJTYPE_VOL ? NULL : graph->adjwgt); + + where = graph->where; + pwgts = graph->pwgts; /* We assume that this is properly initialized */ + + nparts = ctrl->nparts; + tpwgts = ctrl->tpwgts; + + cpwgt = iwspacemalloc(ctrl, ncon); + maxpwgt = iwspacemalloc(ctrl, nparts*ncon); + ind = iwspacemalloc(ctrl, nvtxs); + otherpmat = iset(nparts, 0, iwspacemalloc(ctrl, nparts)); + + cand = ikvwspacemalloc(ctrl, nparts); + cand2 = ikvwspacemalloc(ctrl, nparts); + + pptr = iwspacemalloc(ctrl, nparts+1); + pind = iwspacemalloc(ctrl, nvtxs); + iarray2csr(nvtxs, nparts, where, pptr, pind); + + if (ctrl->objtype == METIS_OBJTYPE_VOL) { + /* Vol-refinement specific working arrays */ + modind = iwspacemalloc(ctrl, nvtxs); + vmarker = iset(nvtxs, 0, iwspacemalloc(ctrl, nvtxs)); + pmarker = iset(nparts, -1, iwspacemalloc(ctrl, nparts)); + } + + + /* Compute the pmat matrix and ndoms */ + ComputeSubDomainGraph(ctrl, graph); + + nads = ctrl->nads; + adids = ctrl->adids; + adwgts = ctrl->adwgts; + + mypmat = iset(nparts, 0, ctrl->pvec1); + kpmat = iset(nparts, 0, ctrl->pvec2); + + /* Compute the maximum allowed weight for each domain */ + for (i=0; itvwgt[j]*ctrl->ubfactors[j]; + } + + ipqInit(&queue, nparts); + + /* Get into the loop eliminating subdomain connections */ + while (1) { + total = isum(nparts, nads, 1); + avg = total/nparts; + max = nads[iargmax(nparts, nads)]; + + IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO, + printf("Adjacent Subdomain Stats: Total: %3"PRIDX", " + "Max: %3"PRIDX"[%zu], Avg: %3"PRIDX"\n", + total, max, iargmax(nparts, nads), avg)); + + if (max < badfactor*avg) + break; + + /* Add the subdomains that you will try to reduce their connectivity */ + ipqReset(&queue); + for (i=0; i= avg + (max-avg)/2) + ipqInsert(&queue, i, nads[i]); + } + + move = 0; + while ((me = ipqGetTop(&queue)) != -1) { + totalout = isum(nads[me], adwgts[me], 1); + + for (ncand2=0, i=0; idbglvl, METIS_DBG_CONNINFO, + printf("Me: %"PRIDX", Degree: %4"PRIDX", TotalOut: %"PRIDX",\n", + me, nads[me], totalout)); + + /* Sort the connections according to their cut */ + ikvsorti(ncand2, cand2); + + /* Two schemes are used for eliminating subdomain edges. + The first, tries to eliminate subdomain edges by moving remote groups + of vertices to subdomains that 'me' is already connected to. + The second, tries to eliminate subdomain edges by moving entire sets of + my vertices that connect to the 'other' subdomain to a subdomain that + I'm already connected to. + These two schemes are applied in sequence. */ + target = target2 = -1; + for (scheme=0; scheme<2; scheme++) { + for (min=0; min 0); + } + + ikvsortd(ncand, cand); + + IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO, + printf("\tMinOut: %4"PRIDX", to: %3"PRIDX", TtlWgt: %5"PRIDX"[#:%"PRIDX"]\n", + mypmat[other], other, isum(ncon, cpwgt, 1), nind)); + + /* Go through and select the first domain that is common with 'me', and does + not increase the nads[target] higher than nads[me], subject to the maxpwgt + constraint. Traversal is done from the mostly connected to the least. */ + for (i=0; i 0) { + /* Check if balance will go off */ + if (!ivecaxpylez(ncon, 1, cpwgt, pwgts+k*ncon, maxpwgt+k*ncon)) + continue; + + /* get a dense vector out of k's connectivity */ + for (j=0; j 0 && kpmat[j] == 0 && nads[j]+1 >= nads[me]) + break; + } + + /* There were no bad second level effects. See if you can find a + subdomain to move to. */ + if (j == nparts) { + for (nadd=0, j=0; j 0 && kpmat[j] == 0) + nadd++; + } + + IFSET(ctrl->dbglvl, METIS_DBG_CONNINFO, + printf("\t\tto=%"PRIDX", nadd=%"PRIDX", %"PRIDX"\n", k, nadd, nads[k])); + + if (nads[k]+nadd < nads[me]) { + if (target2 == -1 || nads[target2]+bestnadd > nads[k]+nadd || + (nads[target2]+bestnadd == nads[k]+nadd && bestnadd > nadd)) { + target2 = k; + bestnadd = nadd; + } + } + + if (nadd == 0) + target = k; + } + + /* reset kpmat for the next iteration */ + for (j=0; jdbglvl, METIS_DBG_CONNINFO, + printf("\t\tScheme: %"PRIDX". Moving to %"PRIDX"\n", scheme, target)); + move = 1; + break; + } + } + + if (target != -1) + break; /* A move was found. No need to try the other scheme */ + } + + /* reset the mypmat for next iteration */ + for (i=0; iobjtype) { + case METIS_OBJTYPE_CUT: + MoveGroupMinConnForCut(ctrl, graph, target, nind, ind); + break; + case METIS_OBJTYPE_VOL: + MoveGroupMinConnForVol(ctrl, graph, target, nind, ind, vmarker, + pmarker, modind); + break; + default: + gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype); + } + + /* Update the csr representation of the partitioning vector */ + iarray2csr(nvtxs, nparts, where, pptr, pind); + } + } + + if (move == 0) + break; + } + + ipqFree(&queue); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function moves a collection of vertices and updates their rinfo */ +/*************************************************************************/ +void MoveGroupMinConnForCut(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t nind, + idx_t *ind) +{ + idx_t i, ii, j, jj, k, l, nvtxs, nbnd, from, me; + idx_t *xadj, *adjncy, *adjwgt, *where, *bndptr, *bndind; + ckrinfo_t *myrinfo; + cnbr_t *mynbrs; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + where = graph->where; + bndptr = graph->bndptr; + bndind = graph->bndind; + + nbnd = graph->nbnd; + + while (--nind>=0) { + i = ind[nind]; + from = where[i]; + + myrinfo = graph->ckrinfo+i; + if (myrinfo->inbr == -1) { + myrinfo->inbr = cnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1); + myrinfo->nnbrs = 0; + } + mynbrs = ctrl->cnbrpool + myrinfo->inbr; + + /* find the location of 'to' in myrinfo or create it if it is not there */ + for (k=0; knnbrs; k++) { + if (mynbrs[k].pid == to) + break; + } + if (k == myrinfo->nnbrs) { + ASSERT(k < xadj[i+1]-xadj[i]); + mynbrs[k].pid = to; + mynbrs[k].ed = 0; + myrinfo->nnbrs++; + } + + /* Update pwgts */ + iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1); + iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1); + + /* Update mincut */ + graph->mincut -= mynbrs[k].ed-myrinfo->id; + + /* Update subdomain connectivity graph to reflect the move of 'i' */ + UpdateEdgeSubDomainGraph(ctrl, from, to, myrinfo->id-mynbrs[k].ed, NULL); + + /* Update ID/ED and BND related information for the moved vertex */ + UpdateMovedVertexInfoAndBND(i, from, k, to, myrinfo, mynbrs, where, nbnd, + bndptr, bndind, BNDTYPE_REFINE); + + /* Update the degrees of adjacent vertices */ + for (j=xadj[i]; jckrinfo+ii; + + UpdateAdjacentVertexInfoAndBND(ctrl, ii, xadj[ii+1]-xadj[ii], me, + from, to, myrinfo, adjwgt[j], nbnd, bndptr, bndind, BNDTYPE_REFINE); + + /* Update subdomain graph to reflect the move of 'i' for domains other + than 'from' and 'to' */ + if (me != from && me != to) { + UpdateEdgeSubDomainGraph(ctrl, from, me, -adjwgt[j], NULL); + UpdateEdgeSubDomainGraph(ctrl, to, me, adjwgt[j], NULL); + } + } + } + + ASSERT(ComputeCut(graph, where) == graph->mincut); + + graph->nbnd = nbnd; + +} + + +/*************************************************************************/ +/*! This function moves a collection of vertices and updates their rinfo */ +/*************************************************************************/ +void MoveGroupMinConnForVol(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t nind, + idx_t *ind, idx_t *vmarker, idx_t *pmarker, idx_t *modind) +{ + idx_t i, ii, j, jj, k, l, nvtxs, from, me, other, xgain, ewgt; + idx_t *xadj, *vsize, *adjncy, *where; + vkrinfo_t *myrinfo, *orinfo; + vnbr_t *mynbrs, *onbrs; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vsize = graph->vsize; + adjncy = graph->adjncy; + where = graph->where; + + while (--nind>=0) { + i = ind[nind]; + from = where[i]; + + myrinfo = graph->vkrinfo+i; + if (myrinfo->inbr == -1) { + myrinfo->inbr = vnbrpoolGetNext(ctrl, xadj[i+1]-xadj[i]+1); + myrinfo->nnbrs = 0; + } + mynbrs = ctrl->vnbrpool + myrinfo->inbr; + + xgain = (myrinfo->nid == 0 && myrinfo->ned > 0 ? vsize[i] : 0); + + //printf("Moving %"PRIDX" from %"PRIDX" to %"PRIDX" [vsize: %"PRIDX"] [xgain: %"PRIDX"]\n", + // i, from, to, vsize[i], xgain); + + /* find the location of 'to' in myrinfo or create it if it is not there */ + for (k=0; knnbrs; k++) { + if (mynbrs[k].pid == to) + break; + } + + if (k == myrinfo->nnbrs) { + //printf("Missing neighbor\n"); + + if (myrinfo->nid > 0) + xgain -= vsize[i]; + + /* determine the volume gain resulting from that move */ + for (j=xadj[i]; jvkrinfo+ii; + onbrs = ctrl->vnbrpool + orinfo->inbr; + ASSERT(other != to) + + //printf(" %8d %8d %3d\n", (int)ii, (int)vsize[ii], (int)other); + + if (from == other) { + /* Same subdomain vertex: Decrease the gain if 'to' is a new neighbor. */ + for (l=0; lnnbrs; l++) { + if (onbrs[l].pid == to) + break; + } + if (l == orinfo->nnbrs) + xgain -= vsize[ii]; + } + else { + /* Remote vertex: increase if 'to' is a new subdomain */ + for (l=0; lnnbrs; l++) { + if (onbrs[l].pid == to) + break; + } + if (l == orinfo->nnbrs) + xgain -= vsize[ii]; + + /* Remote vertex: decrease if i is the only connection to 'from' */ + for (l=0; lnnbrs; l++) { + if (onbrs[l].pid == from && onbrs[l].ned == 1) { + xgain += vsize[ii]; + break; + } + } + } + } + graph->minvol -= xgain; + graph->mincut -= -myrinfo->nid; + ewgt = myrinfo->nid; + } + else { + graph->minvol -= (xgain + mynbrs[k].gv); + graph->mincut -= mynbrs[k].ned-myrinfo->nid; + ewgt = myrinfo->nid-mynbrs[k].ned; + } + + /* Update where and pwgts */ + where[i] = to; + iaxpy(graph->ncon, 1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+to*graph->ncon, 1); + iaxpy(graph->ncon, -1, graph->vwgt+i*graph->ncon, 1, graph->pwgts+from*graph->ncon, 1); + + /* Update subdomain connectivity graph to reflect the move of 'i' */ + UpdateEdgeSubDomainGraph(ctrl, from, to, ewgt, NULL); + + /* Update the subdomain connectivity of the adjacent vertices */ + for (j=xadj[i]; jmincut); + ASSERTP(ComputeVolume(graph, where) == graph->minvol, + ("%"PRIDX" %"PRIDX"\n", ComputeVolume(graph, where), graph->minvol)); + +} + + +/*************************************************************************/ +/*! This function computes the subdomain graph. For deubuging purposes. */ +/*************************************************************************/ +void PrintSubDomainGraph(graph_t *graph, idx_t nparts, idx_t *where) +{ + idx_t i, j, k, me, nvtxs, total, max; + idx_t *xadj, *adjncy, *adjwgt, *pmat; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + pmat = ismalloc(nparts*nparts, 0, "ComputeSubDomainGraph: pmat"); + + for (i=0; i 0) + k++; + } + total += k; + + if (k > max) + max = k; +/* + printf("%2"PRIDX" -> %2"PRIDX" ", i, k); + for (j=0; j 0) + printf("[%2"PRIDX" %4"PRIDX"] ", j, pmat[i*nparts+j]); + } + printf("\n"); +*/ + } + printf("Total adjacent subdomains: %"PRIDX", Max: %"PRIDX"\n", total, max); + + gk_free((void **)&pmat, LTERM); +} + + diff --git a/src/metis/mincover.c b/src/metis/libmetis/mincover.c similarity index 79% rename from src/metis/mincover.c rename to src/metis/libmetis/mincover.c index 81a9485..ed437ff 100644 --- a/src/metis/mincover.c +++ b/src/metis/libmetis/mincover.c @@ -8,10 +8,10 @@ * Started 8/1/97 * George * - * $Id: mincover.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ + * $Id: mincover.c 9942 2011-05-17 22:09:52Z karypis $ */ -#include +#include "metislib.h" /************************************************************************* * Constants used by mincover algorithm @@ -39,18 +39,18 @@ * cover : the actual cover (array) * csize : the size of the cover **************************************************************************/ -void MinCover(idxtype *xadj, idxtype *adjncy, idxtype asize, idxtype bsize, idxtype *cover, idxtype *csize) +void MinCover(idx_t *xadj, idx_t *adjncy, idx_t asize, idx_t bsize, idx_t *cover, idx_t *csize) { - idxtype i, j; - idxtype *mate, *queue, *flag, *level, *lst; - idxtype fptr, rptr, lstptr; - idxtype row, maxlevel, col; + idx_t i, j; + idx_t *mate, *queue, *flag, *level, *lst; + idx_t fptr, rptr, lstptr; + idx_t row, maxlevel, col; - mate = idxsmalloc(bsize, -1, "MinCover: mate"); - flag = idxmalloc(bsize, "MinCover: flag"); - level = idxmalloc(bsize, "MinCover: level"); - queue = idxmalloc(bsize, "MinCover: queue"); - lst = idxmalloc(bsize, "MinCover: lst"); + mate = ismalloc(bsize, -1, "MinCover: mate"); + flag = imalloc(bsize, "MinCover: flag"); + level = imalloc(bsize, "MinCover: level"); + queue = imalloc(bsize, "MinCover: queue"); + lst = imalloc(bsize, "MinCover: lst"); /* Get a cheap matching */ for (i=0; i +#include "metislib.h" /************************************************************************* @@ -50,11 +50,11 @@ * marker -- a temporary marker vector. * Subroutines used -- mmdelm, mmdint, mmdnum, mmdupd. **************************************************************************/ -void genmmd(idxtype neqns, idxtype *xadj, idxtype *adjncy, idxtype *invp, idxtype *perm, - idxtype delta, idxtype *head, idxtype *qsize, idxtype *list, idxtype *marker, - idxtype maxint, idxtype *ncsub) +void genmmd(idx_t neqns, idx_t *xadj, idx_t *adjncy, idx_t *invp, idx_t *perm, + idx_t delta, idx_t *head, idx_t *qsize, idx_t *list, idx_t *marker, + idx_t maxint, idx_t *ncsub) { - idxtype ehead, i, mdeg, mdlmt, mdeg_node, nextmd, num, tag; + idx_t ehead, i, mdeg, mdlmt, mdeg_node, nextmd, num, tag; if (neqns <= 0) return; @@ -168,10 +168,10 @@ n1000: * marker -- marker vector. * list -- temporary linked list of eliminated nabors. ***************************************************************************/ -void mmdelm(idxtype mdeg_node, idxtype *xadj, idxtype *adjncy, idxtype *head, idxtype *forward, - idxtype *backward, idxtype *qsize, idxtype *list, idxtype *marker, idxtype maxint, idxtype tag) +void mmdelm(idx_t mdeg_node, idx_t *xadj, idx_t *adjncy, idx_t *head, idx_t *forward, + idx_t *backward, idx_t *qsize, idx_t *list, idx_t *marker, idx_t maxint, idx_t tag) { - idxtype element, i, istop, istart, j, + idx_t element, i, istop, istart, j, jstop, jstart, link, nabor, node, npv, nqnbrs, nxnode, pvnode, rlmt, rloc, rnode, xqnbr; @@ -302,10 +302,10 @@ n1100: * list -- linked list. * marker -- marker vector. ****************************************************************************/ -idxtype mmdint(idxtype neqns, idxtype *xadj, idxtype *adjncy, idxtype *head, idxtype *forward, - idxtype *backward, idxtype *qsize, idxtype *list, idxtype *marker) +idx_t mmdint(idx_t neqns, idx_t *xadj, idx_t *adjncy, idx_t *head, idx_t *forward, + idx_t *backward, idx_t *qsize, idx_t *list, idx_t *marker) { - idxtype fnode, ndeg, node; + idx_t fnode, ndeg, node; for ( node = 1; node <= neqns; node++ ) { head[node] = 0; @@ -345,9 +345,9 @@ idxtype mmdint(idxtype neqns, idxtype *xadj, idxtype *adjncy, idxtype *head, id * output parameters -- * perm -- the permutation vector. ****************************************************************************/ -void mmdnum(idxtype neqns, idxtype *perm, idxtype *invp, idxtype *qsize) +void mmdnum(idx_t neqns, idx_t *perm, idx_t *invp, idx_t *qsize) { - idxtype father, nextf, node, nqsize, num, root; + idx_t father, nextf, node, nqsize, num, root; for ( node = 1; node <= neqns; node++ ) { nqsize = qsize[node]; @@ -409,11 +409,11 @@ void mmdnum(idxtype neqns, idxtype *perm, idxtype *invp, idxtype *qsize) * list -- marker vector for degree update. * *tag -- tag value. ****************************************************************************/ -void mmdupd(idxtype ehead, idxtype neqns, idxtype *xadj, idxtype *adjncy, idxtype delta, idxtype *mdeg, - idxtype *head, idxtype *forward, idxtype *backward, idxtype *qsize, idxtype *list, - idxtype *marker, idxtype maxint, idxtype *tag) +void mmdupd(idx_t ehead, idx_t neqns, idx_t *xadj, idx_t *adjncy, idx_t delta, idx_t *mdeg, + idx_t *head, idx_t *forward, idx_t *backward, idx_t *qsize, idx_t *list, + idx_t *marker, idx_t maxint, idx_t *tag) { - idxtype deg, deg0, element, enode, fnode, i, iq2, istop, + idx_t deg, deg0, element, enode, fnode, i, iq2, istop, istart, j, jstop, jstart, link, mdeg0, mtag, nabor, node, q2head, qxhead; diff --git a/src/metis/libmetis/ometis.c b/src/metis/libmetis/ometis.c new file mode 100644 index 0000000..51e3975 --- /dev/null +++ b/src/metis/libmetis/ometis.c @@ -0,0 +1,701 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * ometis.c + * + * This file contains the top level routines for the multilevel recursive + * bisection algorithm PMETIS. + * + * Started 7/24/97 + * George + * + * $Id: ometis.c 10513 2011-07-07 22:06:03Z karypis $ + * + */ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function is the entry point for the multilevel nested dissection + ordering code. At each bisection, a node-separator is computed using + a node-based refinement approach. + + \param nvtxs is the number of vertices in the graph. + \param xadj is of length nvtxs+1 marking the start of the adjancy + list of each vertex in adjncy. + \param adjncy stores the adjacency lists of the vertices. The adjnacy + list of a vertex should not contain the vertex itself. + \param vwgt is an array of size nvtxs storing the weight of each + vertex. If vwgt is NULL, then the vertices are considered + to have unit weight. + \param numflag is either 0 or 1 indicating that the numbering of + the vertices starts from 0 or 1, respectively. + \param options is an array of size METIS_NOPTIONS used to pass + various options impacting the of the algorithm. A NULL + value indicates use of default options. + \param perm is an array of size nvtxs such that if A and A' are + the original and permuted matrices, then A'[i] = A[perm[i]]. + \param iperm is an array of size nvtxs such that if A and A' are + the original and permuted matrices, then A[i] = A'[iperm[i]]. +*/ +/*************************************************************************/ +int METIS_NodeND(idx_t *nvtxs, idx_t *xadj, idx_t *adjncy, idx_t *vwgt, + idx_t *options, idx_t *perm, idx_t *iperm) +{ + int sigrval=0, renumber=0; + idx_t i, ii, j, l, nnvtxs=0; + graph_t *graph=NULL; + ctrl_t *ctrl; + idx_t *cptr, *cind, *piperm; + int numflag = 0; + + /* set up malloc cleaning code and signal catchers */ + if (!gk_malloc_init()) + return METIS_ERROR_MEMORY; + + gk_sigtrap(); + + if ((sigrval = gk_sigcatch()) != 0) + goto SIGTHROW; + + + /* set up the run time parameters */ + ctrl = SetupCtrl(METIS_OP_OMETIS, options, 1, 3, NULL, NULL); + if (!ctrl) { + gk_siguntrap(); + return METIS_ERROR_INPUT; + } + + /* if required, change the numbering to 0 */ + if (ctrl->numflag == 1) { + Change2CNumbering(*nvtxs, xadj, adjncy); + renumber = 1; + } + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, InitTimers(ctrl)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->TotalTmr)); + + /* prune the dense columns */ + if (ctrl->pfactor > 0.0) { + piperm = imalloc(*nvtxs, "OMETIS: piperm"); + + graph = PruneGraph(ctrl, *nvtxs, xadj, adjncy, vwgt, piperm, ctrl->pfactor); + if (graph == NULL) { + /* if there was no prunning, cleanup the pfactor */ + gk_free((void **)&piperm, LTERM); + ctrl->pfactor = 0.0; + } + else { + nnvtxs = graph->nvtxs; + ctrl->compress = 0; /* disable compression if prunning took place */ + } + } + + /* compress the graph; note that compression only happens if not prunning + has taken place. */ + if (ctrl->compress) { + cptr = imalloc(*nvtxs+1, "OMETIS: cptr"); + cind = imalloc(*nvtxs, "OMETIS: cind"); + + graph = CompressGraph(ctrl, *nvtxs, xadj, adjncy, vwgt, cptr, cind); + if (graph == NULL) { + /* if there was no compression, cleanup the compress flag */ + gk_free((void **)&cptr, &cind, LTERM); + ctrl->compress = 0; + } + else { + nnvtxs = graph->nvtxs; + ctrl->cfactor = 1.0*(*nvtxs)/nnvtxs; + if (ctrl->cfactor > 1.5 && ctrl->nseps == 1) + ctrl->nseps = 2; + //ctrl->nseps = (idx_t)(ctrl->cfactor*ctrl->nseps); + } + } + + /* if no prunning and no compression, setup the graph in the normal way. */ + if (ctrl->pfactor == 0.0 && ctrl->compress == 0) + graph = SetupGraph(ctrl, *nvtxs, 1, xadj, adjncy, vwgt, NULL, NULL); + + ASSERT(CheckGraph(graph, ctrl->numflag, 1)); + + /* allocate workspace memory */ + AllocateWorkSpace(ctrl, graph); + + /* do the nested dissection ordering */ + if (ctrl->ccorder) + MlevelNestedDissectionCC(ctrl, graph, iperm, graph->nvtxs); + else + MlevelNestedDissection(ctrl, graph, iperm, graph->nvtxs); + + + if (ctrl->pfactor > 0.0) { /* Order any prunned vertices */ + icopy(nnvtxs, iperm, perm); /* Use perm as an auxiliary array */ + for (i=0; icompress) { /* Uncompress the ordering */ + /* construct perm from iperm */ + for (i=0; idbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->TotalTmr)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, PrintTimers(ctrl)); + + /* clean up */ + FreeCtrl(&ctrl); + +SIGTHROW: + /* if required, change the numbering back to 1 */ + if (renumber) + Change2FNumberingOrder(*nvtxs, xadj, adjncy, perm, iperm); + + gk_siguntrap(); + gk_malloc_cleanup(0); + + return metis_rcode(sigrval); +} + + +/*************************************************************************/ +/*! This is the driver for the recursive tri-section of a graph into the + left, separator, and right partitions. The graphs correspond to the + left and right parts are further tri-sected in a recursive fashion. + The nodes in the separator are ordered at the end of the left & right + nodes. + */ +/*************************************************************************/ +void MlevelNestedDissection(ctrl_t *ctrl, graph_t *graph, idx_t *order, + idx_t lastvtx) +{ + idx_t i, j, nvtxs, nbnd; + idx_t *label, *bndind; + graph_t *lgraph, *rgraph; + + nvtxs = graph->nvtxs; + + MlevelNodeBisectionMultiple(ctrl, graph); + + IFSET(ctrl->dbglvl, METIS_DBG_SEPINFO, + printf("Nvtxs: %6"PRIDX", [%6"PRIDX" %6"PRIDX" %6"PRIDX"]\n", + graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2])); + + + /* Order the nodes in the separator */ + nbnd = graph->nbnd; + bndind = graph->bndind; + label = graph->label; + for (i=0; invtxs, which + will not be defined upon return from MlevelNestedDissection. */ + if (lgraph->nvtxs > MMDSWITCH && lgraph->nedges > 0) + MlevelNestedDissection(ctrl, lgraph, order, lastvtx-rgraph->nvtxs); + else { + MMDOrder(ctrl, lgraph, order, lastvtx-rgraph->nvtxs); + FreeGraph(&lgraph); + } + if (rgraph->nvtxs > MMDSWITCH && rgraph->nedges > 0) + MlevelNestedDissection(ctrl, rgraph, order, lastvtx); + else { + MMDOrder(ctrl, rgraph, order, lastvtx); + FreeGraph(&rgraph); + } +} + + +/*************************************************************************/ +/*! This routine is similar to its non 'CC' counterpart. The difference is + that after each tri-section, the connected components of the original + graph that result after removing the separator vertises are ordered + independently (i.e., this may lead to more than just the left and + the right subgraphs). +*/ +/*************************************************************************/ +void MlevelNestedDissectionCC(ctrl_t *ctrl, graph_t *graph, idx_t *order, + idx_t lastvtx) +{ + idx_t i, j, nvtxs, nbnd, ncmps, rnvtxs, snvtxs; + idx_t *label, *bndind; + idx_t *cptr, *cind; + graph_t **sgraphs; + + nvtxs = graph->nvtxs; + + MlevelNodeBisectionMultiple(ctrl, graph); + + IFSET(ctrl->dbglvl, METIS_DBG_SEPINFO, + printf("Nvtxs: %6"PRIDX", [%6"PRIDX" %6"PRIDX" %6"PRIDX"]\n", + graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2])); + + /* Order the nodes in the separator */ + nbnd = graph->nbnd; + bndind = graph->bndind; + label = graph->label; + for (i=0; idbglvl&METIS_DBG_INFO) { + if (ncmps > 2) + printf(" Bisection resulted in %"PRIDX" connected components\n", ncmps); + } + + sgraphs = SplitGraphOrderCC(ctrl, graph, ncmps, cptr, cind); + + WCOREPOP; + + /* Free the memory of the top level graph */ + FreeGraph(&graph); + + /* Go and process the subgraphs */ + for (rnvtxs=i=0; invtxs; + + if (sgraphs[i]->nvtxs > MMDSWITCH && sgraphs[i]->nedges > 0) { + MlevelNestedDissectionCC(ctrl, sgraphs[i], order, lastvtx-rnvtxs); + } + else { + MMDOrder(ctrl, sgraphs[i], order, lastvtx-rnvtxs); + FreeGraph(&sgraphs[i]); + } + rnvtxs += snvtxs; + } + + gk_free((void **)&sgraphs, LTERM); +} + + +/*************************************************************************/ +/*! This function performs multilevel node bisection (i.e., tri-section). + It performs multiple bisections and selects the best. */ +/*************************************************************************/ +void MlevelNodeBisectionMultiple(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, mincut; + idx_t *bestwhere; + + /* if the graph is small, just find a single vertex separator */ + if (ctrl->nseps == 1 || graph->nvtxs < (ctrl->compress ? 1000 : 2000)) { + MlevelNodeBisectionL2(ctrl, graph, LARGENIPARTS); + return; + } + + WCOREPUSH; + + bestwhere = iwspacemalloc(ctrl, graph->nvtxs); + + mincut = graph->tvwgt[0]; + for (i=0; inseps; i++) { + MlevelNodeBisectionL2(ctrl, graph, LARGENIPARTS); + + if (i == 0 || graph->mincut < mincut) { + mincut = graph->mincut; + if (i < ctrl->nseps-1) + icopy(graph->nvtxs, graph->where, bestwhere); + } + + if (mincut == 0) + break; + + if (i < ctrl->nseps-1) + FreeRData(graph); + } + + if (mincut != graph->mincut) { + icopy(graph->nvtxs, bestwhere, graph->where); + Compute2WayNodePartitionParams(ctrl, graph); + } + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function performs multilevel node bisection (i.e., tri-section). + It performs multiple bisections and selects the best. */ +/*************************************************************************/ +void MlevelNodeBisectionL2(ctrl_t *ctrl, graph_t *graph, idx_t niparts) +{ + idx_t i, mincut, nruns=5; + graph_t *cgraph; + idx_t *bestwhere; + + /* if the graph is small, just find a single vertex separator */ + if (graph->nvtxs < 5000) { + MlevelNodeBisectionL1(ctrl, graph, niparts); + return; + } + + WCOREPUSH; + + ctrl->CoarsenTo = gk_max(100, graph->nvtxs/30); + + cgraph = CoarsenGraphNlevels(ctrl, graph, 4); + + bestwhere = iwspacemalloc(ctrl, cgraph->nvtxs); + + mincut = graph->tvwgt[0]; + for (i=0; imincut < mincut) { + mincut = cgraph->mincut; + if (i < nruns-1) + icopy(cgraph->nvtxs, cgraph->where, bestwhere); + } + + if (mincut == 0) + break; + + if (i < nruns-1) + FreeRData(cgraph); + } + + if (mincut != cgraph->mincut) + icopy(cgraph->nvtxs, bestwhere, cgraph->where); + + WCOREPOP; + + Refine2WayNode(ctrl, graph, cgraph); + +} + + +/*************************************************************************/ +/*! The top-level routine of the actual multilevel node bisection */ +/*************************************************************************/ +void MlevelNodeBisectionL1(ctrl_t *ctrl, graph_t *graph, idx_t niparts) +{ + graph_t *cgraph; + + ctrl->CoarsenTo = graph->nvtxs/8; + if (ctrl->CoarsenTo > 100) + ctrl->CoarsenTo = 100; + else if (ctrl->CoarsenTo < 40) + ctrl->CoarsenTo = 40; + + cgraph = CoarsenGraph(ctrl, graph); + + niparts = gk_max(1, (cgraph->nvtxs <= ctrl->CoarsenTo ? niparts/2: niparts)); + /*niparts = (cgraph->nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS);*/ + InitSeparator(ctrl, cgraph, niparts); + + Refine2WayNode(ctrl, graph, cgraph); +} + + +/*************************************************************************/ +/*! This function takes a graph and a tri-section (left, right, separator) + and splits it into two graphs. + + This function relies on the fact that adjwgt is all equal to 1. +*/ +/*************************************************************************/ +void SplitGraphOrder(ctrl_t *ctrl, graph_t *graph, graph_t **r_lgraph, + graph_t **r_rgraph) +{ + idx_t i, ii, j, k, l, istart, iend, mypart, nvtxs, snvtxs[3], snedges[3]; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *label, *where, *bndptr, *bndind; + idx_t *sxadj[2], *svwgt[2], *sadjncy[2], *sadjwgt[2], *slabel[2]; + idx_t *rename; + idx_t *auxadjncy; + graph_t *lgraph, *rgraph; + + WCOREPUSH; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->SplitTmr)); + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + label = graph->label; + where = graph->where; + bndptr = graph->bndptr; + bndind = graph->bndind; + ASSERT(bndptr != NULL); + + rename = iwspacemalloc(ctrl, nvtxs); + + snvtxs[0] = snvtxs[1] = snvtxs[2] = snedges[0] = snedges[1] = snedges[2] = 0; + for (i=0; ixadj; + svwgt[0] = lgraph->vwgt; + sadjncy[0] = lgraph->adjncy; + sadjwgt[0] = lgraph->adjwgt; + slabel[0] = lgraph->label; + + rgraph = SetupSplitGraph(graph, snvtxs[1], snedges[1]); + sxadj[1] = rgraph->xadj; + svwgt[1] = rgraph->vwgt; + sadjncy[1] = rgraph->adjncy; + sadjwgt[1] = rgraph->adjwgt; + slabel[1] = rgraph->label; + + /* Go and use bndptr to also mark the boundary nodes in the two partitions */ + for (ii=0; iinbnd; ii++) { + i = bndind[ii]; + for (j=xadj[i]; jnvtxs = snvtxs[0]; + lgraph->nedges = snedges[0]; + rgraph->nvtxs = snvtxs[1]; + rgraph->nedges = snedges[1]; + + SetupGraph_tvwgt(lgraph); + SetupGraph_tvwgt(rgraph); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->SplitTmr)); + + *r_lgraph = lgraph; + *r_rgraph = rgraph; + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function takes a graph and generates a set of graphs, each of + which is a connected component in the original graph. + + This function relies on the fact that adjwgt is all equal to 1. + + \param ctrl stores run state info. + \param graph is the graph to be split. + \param ncmps is the number of connected components. + \param cptr is an array of size ncmps+1 that marks the start and end + locations of the vertices in cind that make up the respective + components (i.e., cptr, cind is in CSR format). + \param cind is an array of size equal to the number of vertices in + the original graph and stores the vertices that belong to each + connected component. + + \returns an array of subgraphs corresponding to the extracted subgraphs. +*/ +/*************************************************************************/ +graph_t **SplitGraphOrderCC(ctrl_t *ctrl, graph_t *graph, idx_t ncmps, + idx_t *cptr, idx_t *cind) +{ + idx_t i, ii, iii, j, k, l, istart, iend, mypart, nvtxs, snvtxs, snedges; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *label, *where, *bndptr, *bndind; + idx_t *sxadj, *svwgt, *sadjncy, *sadjwgt, *slabel; + idx_t *rename; + idx_t *auxadjncy; + graph_t **sgraphs; + + WCOREPUSH; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->SplitTmr)); + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + label = graph->label; + where = graph->where; + bndptr = graph->bndptr; + bndind = graph->bndind; + ASSERT(bndptr != NULL); + + /* Go and use bndptr to also mark the boundary nodes in the two partitions */ + for (ii=0; iinbnd; ii++) { + i = bndind[ii]; + for (j=xadj[i]; jxadj; + svwgt = sgraphs[iii]->vwgt; + sadjncy = sgraphs[iii]->adjncy; + sadjwgt = sgraphs[iii]->adjwgt; + slabel = sgraphs[iii]->label; + + snvtxs = snedges = sxadj[0] = 0; + for (ii=cptr[iii]; iinvtxs = snvtxs; + sgraphs[iii]->nedges = snedges; + + SetupGraph_tvwgt(sgraphs[iii]); + } + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->SplitTmr)); + + WCOREPOP; + + return sgraphs; +} + + +/*************************************************************************/ +/*! This function uses MMD to order the graph. The vertices are numbered + from lastvtx downwards. */ +/*************************************************************************/ +void MMDOrder(ctrl_t *ctrl, graph_t *graph, idx_t *order, idx_t lastvtx) +{ + idx_t i, j, k, nvtxs, nofsub, firstvtx; + idx_t *xadj, *adjncy, *label; + idx_t *perm, *iperm, *head, *qsize, *list, *marker; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + /* Relabel the vertices so that it starts from 1 */ + k = xadj[nvtxs]; + for (i=0; ilabel; + firstvtx = lastvtx-nvtxs; + for (i=0; iobjtype = GETOPTION(options, METIS_OPTION_OBJTYPE, METIS_OBJTYPE_CUT); + ctrl->rtype = METIS_RTYPE_FM; + ctrl->ncuts = GETOPTION(options, METIS_OPTION_NCUTS, 1); + ctrl->niter = GETOPTION(options, METIS_OPTION_NITER, 10); + + if (ncon == 1) { + ctrl->iptype = GETOPTION(options, METIS_OPTION_IPTYPE, METIS_IPTYPE_GROW); + ctrl->ufactor = GETOPTION(options, METIS_OPTION_UFACTOR, PMETIS_DEFAULT_UFACTOR); + ctrl->CoarsenTo = 20; + } + else { + ctrl->iptype = GETOPTION(options, METIS_OPTION_IPTYPE, METIS_IPTYPE_RANDOM); + ctrl->ufactor = GETOPTION(options, METIS_OPTION_UFACTOR, MCPMETIS_DEFAULT_UFACTOR); + ctrl->CoarsenTo = 100; + } + + break; + + + case METIS_OP_KMETIS: + ctrl->objtype = GETOPTION(options, METIS_OPTION_OBJTYPE, METIS_OBJTYPE_CUT); + ctrl->iptype = METIS_IPTYPE_METISRB; + ctrl->rtype = METIS_RTYPE_GREEDY; + ctrl->ncuts = GETOPTION(options, METIS_OPTION_NCUTS, 1); + ctrl->niter = GETOPTION(options, METIS_OPTION_NITER, 10); + ctrl->ufactor = GETOPTION(options, METIS_OPTION_UFACTOR, KMETIS_DEFAULT_UFACTOR); + ctrl->minconn = GETOPTION(options, METIS_OPTION_MINCONN, 0); + ctrl->contig = GETOPTION(options, METIS_OPTION_CONTIG, 0); + break; + + + case METIS_OP_OMETIS: + ctrl->objtype = GETOPTION(options, METIS_OPTION_OBJTYPE, METIS_OBJTYPE_NODE); + ctrl->rtype = GETOPTION(options, METIS_OPTION_RTYPE, METIS_RTYPE_SEP1SIDED); + ctrl->iptype = GETOPTION(options, METIS_OPTION_IPTYPE, METIS_IPTYPE_EDGE); + ctrl->nseps = GETOPTION(options, METIS_OPTION_NSEPS, 1); + ctrl->niter = GETOPTION(options, METIS_OPTION_NITER, 10); + ctrl->ufactor = GETOPTION(options, METIS_OPTION_UFACTOR, OMETIS_DEFAULT_UFACTOR); + ctrl->compress = GETOPTION(options, METIS_OPTION_COMPRESS, 1); + ctrl->ccorder = GETOPTION(options, METIS_OPTION_CCORDER, 0); + ctrl->pfactor = 0.1*GETOPTION(options, METIS_OPTION_PFACTOR, 0); + + ctrl->CoarsenTo = 100; + break; + + default: + gk_errexit(SIGERR, "Unknown optype of %d\n", optype); + } + + /* common options */ + ctrl->ctype = GETOPTION(options, METIS_OPTION_CTYPE, METIS_CTYPE_SHEM); + ctrl->no2hop = GETOPTION(options, METIS_OPTION_NO2HOP, 0); + ctrl->seed = GETOPTION(options, METIS_OPTION_SEED, -1); + ctrl->dbglvl = GETOPTION(options, METIS_OPTION_DBGLVL, 0); + ctrl->numflag = GETOPTION(options, METIS_OPTION_NUMBERING, 0); + + /* set non-option information */ + ctrl->optype = optype; + ctrl->ncon = ncon; + ctrl->nparts = nparts; + ctrl->maxvwgt = ismalloc(ncon, 0, "SetupCtrl: maxvwgt"); + + /* setup the target partition weights */ + if (ctrl->optype != METIS_OP_OMETIS) { + ctrl->tpwgts = rmalloc(nparts*ncon, "SetupCtrl: ctrl->tpwgts"); + if (tpwgts) { + rcopy(nparts*ncon, tpwgts, ctrl->tpwgts); + } + else { + for (i=0; itpwgts[i*ncon+j] = 1.0/nparts; + } + } + } + else { /* METIS_OP_OMETIS */ + /* this is required to allow the pijbm to be defined properly for + the edge-based refinement during initial partitioning */ + ctrl->tpwgts = rsmalloc(2, .5, "SetupCtrl: ctrl->tpwgts"); + } + + + /* setup the ubfactors */ + ctrl->ubfactors = rsmalloc(ctrl->ncon, I2RUBFACTOR(ctrl->ufactor), "SetupCtrl: ubfactors"); + if (ubvec) + rcopy(ctrl->ncon, ubvec, ctrl->ubfactors); + for (i=0; incon; i++) + ctrl->ubfactors[i] += 0.0000499; + + /* Allocate memory for balance multipliers. + Note that for PMETIS/OMETIS routines the memory allocated is more + than required as balance multipliers for 2 parts is sufficient. */ + ctrl->pijbm = rmalloc(nparts*ncon, "SetupCtrl: ctrl->pijbm"); + + InitRandom(ctrl->seed); + + IFSET(ctrl->dbglvl, METIS_DBG_INFO, PrintCtrl(ctrl)); + + if (!CheckParams(ctrl)) { + FreeCtrl(&ctrl); + return NULL; + } + else { + return ctrl; + } +} + + +/*************************************************************************/ +/*! Computes the per-partition/constraint balance multipliers */ +/*************************************************************************/ +void SetupKWayBalMultipliers(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, j; + + for (i=0; inparts; i++) { + for (j=0; jncon; j++) + ctrl->pijbm[i*graph->ncon+j] = graph->invtvwgt[j]/ctrl->tpwgts[i*graph->ncon+j]; + } +} + + +/*************************************************************************/ +/*! Computes the per-partition/constraint balance multipliers */ +/*************************************************************************/ +void Setup2WayBalMultipliers(ctrl_t *ctrl, graph_t *graph, real_t *tpwgts) +{ + idx_t i, j; + + for (i=0; i<2; i++) { + for (j=0; jncon; j++) + ctrl->pijbm[i*graph->ncon+j] = graph->invtvwgt[j]/tpwgts[i*graph->ncon+j]; + } +} + + +/*************************************************************************/ +/*! This function prints the various control fields */ +/*************************************************************************/ +void PrintCtrl(ctrl_t *ctrl) +{ + idx_t i, j, modnum; + + printf(" Runtime parameters:\n"); + + printf(" Objective type: "); + switch (ctrl->objtype) { + case METIS_OBJTYPE_CUT: + printf("METIS_OBJTYPE_CUT\n"); + break; + case METIS_OBJTYPE_VOL: + printf("METIS_OBJTYPE_VOL\n"); + break; + case METIS_OBJTYPE_NODE: + printf("METIS_OBJTYPE_NODE\n"); + break; + default: + printf("Unknown!\n"); + } + + printf(" Coarsening type: "); + switch (ctrl->ctype) { + case METIS_CTYPE_RM: + printf("METIS_CTYPE_RM\n"); + break; + case METIS_CTYPE_SHEM: + printf("METIS_CTYPE_SHEM\n"); + break; + default: + printf("Unknown!\n"); + } + + printf(" Initial partitioning type: "); + switch (ctrl->iptype) { + case METIS_IPTYPE_GROW: + printf("METIS_IPTYPE_GROW\n"); + break; + case METIS_IPTYPE_RANDOM: + printf("METIS_IPTYPE_RANDOM\n"); + break; + case METIS_IPTYPE_EDGE: + printf("METIS_IPTYPE_EDGE\n"); + break; + case METIS_IPTYPE_NODE: + printf("METIS_IPTYPE_NODE\n"); + break; + case METIS_IPTYPE_METISRB: + printf("METIS_IPTYPE_METISRB\n"); + break; + default: + printf("Unknown!\n"); + } + + printf(" Refinement type: "); + switch (ctrl->rtype) { + case METIS_RTYPE_FM: + printf("METIS_RTYPE_FM\n"); + break; + case METIS_RTYPE_GREEDY: + printf("METIS_RTYPE_GREEDY\n"); + break; + case METIS_RTYPE_SEP2SIDED: + printf("METIS_RTYPE_SEP2SIDED\n"); + break; + case METIS_RTYPE_SEP1SIDED: + printf("METIS_RTYPE_SEP1SIDED\n"); + break; + default: + printf("Unknown!\n"); + } + + printf(" Perform a 2-hop matching: %s\n", (ctrl->no2hop ? "Yes" : "No")); + + printf(" Number of balancing constraints: %"PRIDX"\n", ctrl->ncon); + printf(" Number of refinement iterations: %"PRIDX"\n", ctrl->niter); + printf(" Random number seed: %"PRIDX"\n", ctrl->seed); + + if (ctrl->optype == METIS_OP_OMETIS) { + printf(" Number of separators: %"PRIDX"\n", ctrl->nseps); + printf(" Compress graph prior to ordering: %s\n", (ctrl->compress ? "Yes" : "No")); + printf(" Detect & order connected components separately: %s\n", (ctrl->ccorder ? "Yes" : "No")); + printf(" Prunning factor for high degree vertices: %"PRREAL"\n", ctrl->pfactor); + } + else { + printf(" Number of partitions: %"PRIDX"\n", ctrl->nparts); + printf(" Number of cuts: %"PRIDX"\n", ctrl->ncuts); + printf(" User-supplied ufactor: %"PRIDX"\n", ctrl->ufactor); + + if (ctrl->optype == METIS_OP_KMETIS) { + printf(" Minimize connectivity: %s\n", (ctrl->minconn ? "Yes" : "No")); + printf(" Create contigous partitions: %s\n", (ctrl->contig ? "Yes" : "No")); + } + + modnum = (ctrl->ncon==1 ? 5 : (ctrl->ncon==2 ? 3 : (ctrl->ncon==3 ? 2 : 1))); + printf(" Target partition weights: "); + for (i=0; inparts; i++) { + if (i%modnum == 0) + printf("\n "); + printf("%4"PRIDX"=[", i); + for (j=0; jncon; j++) + printf("%s%.2e", (j==0 ? "" : " "), (double)ctrl->tpwgts[i*ctrl->ncon+j]); + printf("]"); + } + printf("\n"); + } + + printf(" Allowed maximum load imbalance: "); + for (i=0; incon; i++) + printf("%.3"PRREAL" ", ctrl->ubfactors[i]); + printf("\n"); + + printf("\n"); +} + + +/*************************************************************************/ +/*! This function checks the validity of user-supplied parameters */ +/*************************************************************************/ +int CheckParams(ctrl_t *ctrl) +{ + idx_t i, j; + real_t sum; + mdbglvl_et dbglvl=METIS_DBG_INFO; + + switch (ctrl->optype) { + case METIS_OP_PMETIS: + if (ctrl->objtype != METIS_OBJTYPE_CUT) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect objective type.\n")); + return 0; + } + if (ctrl->ctype != METIS_CTYPE_RM && ctrl->ctype != METIS_CTYPE_SHEM) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect coarsening scheme.\n")); + return 0; + } + if (ctrl->iptype != METIS_IPTYPE_GROW && ctrl->iptype != METIS_IPTYPE_RANDOM) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect initial partitioning scheme.\n")); + return 0; + } + if (ctrl->rtype != METIS_RTYPE_FM) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect refinement scheme.\n")); + return 0; + } + if (ctrl->ncuts <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect ncuts.\n")); + return 0; + } + if (ctrl->niter <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect niter.\n")); + return 0; + } + if (ctrl->ufactor <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect ufactor.\n")); + return 0; + } + if (ctrl->numflag != 0 && ctrl->numflag != 1) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect numflag.\n")); + return 0; + } + if (ctrl->nparts <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect nparts.\n")); + return 0; + } + if (ctrl->ncon <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect ncon.\n")); + return 0; + } + + for (i=0; incon; i++) { + sum = rsum(ctrl->nparts, ctrl->tpwgts+i, ctrl->ncon); + if (sum < 0.99 || sum > 1.01) { + IFSET(dbglvl, METIS_DBG_INFO, + printf("Input Error: Incorrect sum of %"PRREAL" for tpwgts for constraint %"PRIDX".\n", sum, i)); + return 0; + } + } + for (i=0; incon; i++) { + for (j=0; jnparts; j++) { + if (ctrl->tpwgts[j*ctrl->ncon+i] <= 0.0) { + IFSET(dbglvl, METIS_DBG_INFO, + printf("Input Error: Incorrect tpwgts for partition %"PRIDX" and constraint %"PRIDX".\n", j, i)); + return 0; + } + } + } + + for (i=0; incon; i++) { + if (ctrl->ubfactors[i] <= 1.0) { + IFSET(dbglvl, METIS_DBG_INFO, + printf("Input Error: Incorrect ubfactor for constraint %"PRIDX".\n", i)); + return 0; + } + } + + break; + + case METIS_OP_KMETIS: + if (ctrl->objtype != METIS_OBJTYPE_CUT && ctrl->objtype != METIS_OBJTYPE_VOL) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect objective type.\n")); + return 0; + } + if (ctrl->ctype != METIS_CTYPE_RM && ctrl->ctype != METIS_CTYPE_SHEM) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect coarsening scheme.\n")); + return 0; + } + if (ctrl->iptype != METIS_IPTYPE_METISRB) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect initial partitioning scheme.\n")); + return 0; + } + if (ctrl->rtype != METIS_RTYPE_GREEDY) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect refinement scheme.\n")); + return 0; + } + if (ctrl->ncuts <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect ncuts.\n")); + return 0; + } + if (ctrl->niter <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect niter.\n")); + return 0; + } + if (ctrl->ufactor <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect ufactor.\n")); + return 0; + } + if (ctrl->numflag != 0 && ctrl->numflag != 1) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect numflag.\n")); + return 0; + } + if (ctrl->nparts <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect nparts.\n")); + return 0; + } + if (ctrl->ncon <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect ncon.\n")); + return 0; + } + if (ctrl->contig != 0 && ctrl->contig != 1) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect contig.\n")); + return 0; + } + if (ctrl->minconn != 0 && ctrl->minconn != 1) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect minconn.\n")); + return 0; + } + + for (i=0; incon; i++) { + sum = rsum(ctrl->nparts, ctrl->tpwgts+i, ctrl->ncon); + if (sum < 0.99 || sum > 1.01) { + IFSET(dbglvl, METIS_DBG_INFO, + printf("Input Error: Incorrect sum of %"PRREAL" for tpwgts for constraint %"PRIDX".\n", sum, i)); + return 0; + } + } + for (i=0; incon; i++) { + for (j=0; jnparts; j++) { + if (ctrl->tpwgts[j*ctrl->ncon+i] <= 0.0) { + IFSET(dbglvl, METIS_DBG_INFO, + printf("Input Error: Incorrect tpwgts for partition %"PRIDX" and constraint %"PRIDX".\n", j, i)); + return 0; + } + } + } + + for (i=0; incon; i++) { + if (ctrl->ubfactors[i] <= 1.0) { + IFSET(dbglvl, METIS_DBG_INFO, + printf("Input Error: Incorrect ubfactor for constraint %"PRIDX".\n", i)); + return 0; + } + } + + break; + + + + case METIS_OP_OMETIS: + if (ctrl->objtype != METIS_OBJTYPE_NODE) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect objective type.\n")); + return 0; + } + if (ctrl->ctype != METIS_CTYPE_RM && ctrl->ctype != METIS_CTYPE_SHEM) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect coarsening scheme.\n")); + return 0; + } + if (ctrl->iptype != METIS_IPTYPE_EDGE && ctrl->iptype != METIS_IPTYPE_NODE) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect initial partitioning scheme.\n")); + return 0; + } + if (ctrl->rtype != METIS_RTYPE_SEP1SIDED && ctrl->rtype != METIS_RTYPE_SEP2SIDED) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect refinement scheme.\n")); + return 0; + } + if (ctrl->nseps <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect nseps.\n")); + return 0; + } + if (ctrl->niter <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect niter.\n")); + return 0; + } + if (ctrl->ufactor <= 0) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect ufactor.\n")); + return 0; + } + if (ctrl->numflag != 0 && ctrl->numflag != 1) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect numflag.\n")); + return 0; + } + if (ctrl->nparts != 3) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect nparts.\n")); + return 0; + } + if (ctrl->ncon != 1) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect ncon.\n")); + return 0; + } + if (ctrl->compress != 0 && ctrl->compress != 1) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect compress.\n")); + return 0; + } + if (ctrl->ccorder != 0 && ctrl->ccorder != 1) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect ccorder.\n")); + return 0; + } + if (ctrl->pfactor < 0.0 ) { + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect pfactor.\n")); + return 0; + } + + for (i=0; incon; i++) { + if (ctrl->ubfactors[i] <= 1.0) { + IFSET(dbglvl, METIS_DBG_INFO, + printf("Input Error: Incorrect ubfactor for constraint %"PRIDX".\n", i)); + return 0; + } + } + + break; + + default: + IFSET(dbglvl, METIS_DBG_INFO, printf("Input Error: Incorrect optype\n")); + return 0; + } + + return 1; +} + + +/*************************************************************************/ +/*! This function frees the memory associated with a ctrl_t */ +/*************************************************************************/ +void FreeCtrl(ctrl_t **r_ctrl) +{ + ctrl_t *ctrl = *r_ctrl; + + FreeWorkSpace(ctrl); + + gk_free((void **)&ctrl->tpwgts, &ctrl->pijbm, + &ctrl->ubfactors, &ctrl->maxvwgt, &ctrl, LTERM); + + *r_ctrl = NULL; +} + + diff --git a/src/metis/libmetis/parmetis.c b/src/metis/libmetis/parmetis.c new file mode 100644 index 0000000..631d811 --- /dev/null +++ b/src/metis/libmetis/parmetis.c @@ -0,0 +1,723 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * parmetis.c + * + * This file contains top level routines that are used by ParMETIS + * + * Started 10/14/97 + * George + * + * $Id: parmetis.c 10481 2011-07-05 18:01:23Z karypis $ + * + */ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function is the entry point for the node ND code for ParMETIS. + The difference between this routine and the standard METIS_NodeND are + the following + + - It performs at least log2(npes) levels of nested dissection. + - It stores the size of the log2(npes) top-level separators in the + sizes array. +*/ +/*************************************************************************/ +int METIS_NodeNDP(idx_t nvtxs, idx_t *xadj, idx_t *adjncy, idx_t *vwgt, + idx_t npes, idx_t *options, idx_t *perm, idx_t *iperm, idx_t *sizes) +{ + idx_t i, ii, j, l, nnvtxs=0; + graph_t *graph; + ctrl_t *ctrl; + idx_t *cptr, *cind; + + ctrl = SetupCtrl(METIS_OP_OMETIS, options, 1, 3, NULL, NULL); + if (!ctrl) return METIS_ERROR_INPUT; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, InitTimers(ctrl)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->TotalTmr)); + + /* compress the graph; not that compression only happens if not prunning + has taken place. */ + if (ctrl->compress) { + cptr = imalloc(nvtxs+1, "OMETIS: cptr"); + cind = imalloc(nvtxs, "OMETIS: cind"); + + graph = CompressGraph(ctrl, nvtxs, xadj, adjncy, vwgt, cptr, cind); + if (graph == NULL) { + /* if there was no compression, cleanup the compress flag */ + gk_free((void **)&cptr, &cind, LTERM); + ctrl->compress = 0; + } + else { + nnvtxs = graph->nvtxs; + } + } + + /* if no compression, setup the graph in the normal way. */ + if (ctrl->compress == 0) + graph = SetupGraph(ctrl, nvtxs, 1, xadj, adjncy, vwgt, NULL, NULL); + + + /* allocate workspace memory */ + AllocateWorkSpace(ctrl, graph); + + + /* do the nested dissection ordering */ + iset(2*npes-1, 0, sizes); + MlevelNestedDissectionP(ctrl, graph, iperm, graph->nvtxs, npes, 0, sizes); + + + /* Uncompress the ordering */ + if (ctrl->compress) { + /* construct perm from iperm */ + for (i=0; idbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->TotalTmr)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, PrintTimers(ctrl)); + + /* clean up */ + FreeCtrl(&ctrl); + + return METIS_OK; +} + + +/*************************************************************************/ +/*! This function is similar to MlevelNestedDissection with the difference + that it also records separator sizes for the top log2(npes) levels */ +/**************************************************************************/ +void MlevelNestedDissectionP(ctrl_t *ctrl, graph_t *graph, idx_t *order, + idx_t lastvtx, idx_t npes, idx_t cpos, idx_t *sizes) +{ + idx_t i, j, nvtxs, nbnd; + idx_t *label, *bndind; + graph_t *lgraph, *rgraph; + + nvtxs = graph->nvtxs; + + if (nvtxs == 0) { + FreeGraph(&graph); + return; + } + + MlevelNodeBisectionMultiple(ctrl, graph); + + IFSET(ctrl->dbglvl, METIS_DBG_SEPINFO, + printf("Nvtxs: %6"PRIDX", [%6"PRIDX" %6"PRIDX" %6"PRIDX"]\n", + graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2])); + + if (cpos < npes-1) { + sizes[2*npes-2-cpos] = graph->pwgts[2]; + sizes[2*npes-2-(2*cpos+1)] = graph->pwgts[1]; + sizes[2*npes-2-(2*cpos+2)] = graph->pwgts[0]; + } + + /* Order the nodes in the separator */ + nbnd = graph->nbnd; + bndind = graph->bndind; + label = graph->label; + for (i=0; invtxs > MMDSWITCH || 2*cpos+2 < npes-1) && lgraph->nedges > 0) + MlevelNestedDissectionP(ctrl, lgraph, order, lastvtx-rgraph->nvtxs, npes, 2*cpos+2, sizes); + else { + MMDOrder(ctrl, lgraph, order, lastvtx-rgraph->nvtxs); + FreeGraph(&lgraph); + } + if ((rgraph->nvtxs > MMDSWITCH || 2*cpos+1 < npes-1) && rgraph->nedges > 0) + MlevelNestedDissectionP(ctrl, rgraph, order, lastvtx, npes, 2*cpos+1, sizes); + else { + MMDOrder(ctrl, rgraph, order, lastvtx); + FreeGraph(&rgraph); + } +} + + +/*************************************************************************/ +/*! This function bisects a graph by computing a vertex separator */ +/**************************************************************************/ +int METIS_ComputeVertexSeparator(idx_t *nvtxs, idx_t *xadj, idx_t *adjncy, + idx_t *vwgt, idx_t *options, idx_t *r_sepsize, idx_t *part) +{ + idx_t i, j; + graph_t *graph; + ctrl_t *ctrl; + + if ((ctrl = SetupCtrl(METIS_OP_OMETIS, options, 1, 3, NULL, NULL)) == NULL) + return METIS_ERROR_INPUT; + + InitRandom(ctrl->seed); + + graph = SetupGraph(ctrl, *nvtxs, 1, xadj, adjncy, vwgt, NULL, NULL); + + AllocateWorkSpace(ctrl, graph); + + /*============================================================ + * Perform the bisection + *============================================================*/ + ctrl->CoarsenTo = 100; + + MlevelNodeBisectionMultiple(ctrl, graph); + + *r_sepsize = graph->pwgts[2]; + icopy(*nvtxs, graph->where, part); + + FreeGraph(&graph); + + FreeCtrl(&ctrl); + + return METIS_OK; +} + + +/*************************************************************************/ +/*! This function is the entry point of a node-based separator refinement + of the nodes with an hmarker[] of 0. */ +/*************************************************************************/ +int METIS_NodeRefine(idx_t nvtxs, idx_t *xadj, idx_t *vwgt, idx_t *adjncy, + idx_t *where, idx_t *hmarker, real_t ubfactor) +{ + graph_t *graph; + ctrl_t *ctrl; + + /* set up the run time parameters */ + ctrl = SetupCtrl(METIS_OP_OMETIS, NULL, 1, 3, NULL, NULL); + if (!ctrl) return METIS_ERROR_INPUT; + + /* set up the graph */ + graph = SetupGraph(ctrl, nvtxs, 1, xadj, adjncy, vwgt, NULL, NULL); + + /* allocate workspace memory */ + AllocateWorkSpace(ctrl, graph); + + /* set up the memory and the input partition */ + Allocate2WayNodePartitionMemory(ctrl, graph); + icopy(nvtxs, where, graph->where); + + Compute2WayNodePartitionParams(ctrl, graph); + + FM_2WayNodeRefine1SidedP(ctrl, graph, hmarker, ubfactor, 10); + /* FM_2WayNodeRefine2SidedP(ctrl, graph, hmarker, ubfactor, 10); */ + + icopy(nvtxs, graph->where, where); + + FreeGraph(&graph); + FreeCtrl(&ctrl); + + return METIS_OK; +} + + +/*************************************************************************/ +/*! This function performs a node-based 1-sided FM refinement that moves + only nodes whose hmarker[] == -1. It is used by Parmetis. */ +/*************************************************************************/ +void FM_2WayNodeRefine1SidedP(ctrl_t *ctrl, graph_t *graph, + idx_t *hmarker, real_t ubfactor, idx_t npasses) +{ + idx_t i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind, nbad, qsize; + idx_t *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; + idx_t *mptr, *mind, *swaps, *inqueue; + rpq_t *queue; + nrinfo_t *rinfo; + idx_t higain, oldgain, mincut, initcut, mincutorder; + idx_t pass, from, to, limit; + idx_t badmaxpwgt, mindiff, newdiff; + + WCOREPUSH; + + ASSERT(graph->mincut == graph->pwgts[2]); + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + + bndind = graph->bndind; + bndptr = graph->bndptr; + where = graph->where; + pwgts = graph->pwgts; + rinfo = graph->nrinfo; + + queue = rpqCreate(nvtxs); + + inqueue = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs)); + swaps = iwspacemalloc(ctrl, nvtxs); + mptr = iwspacemalloc(ctrl, nvtxs+1); + mind = iwspacemalloc(ctrl, 2*nvtxs); + + badmaxpwgt = (idx_t)(ubfactor*gk_max(pwgts[0], pwgts[1])); + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("Partitions-N1: [%6"PRIDX" %6"PRIDX"] Nv-Nb[%6"PRIDX" %6"PRIDX"] " + "MaxPwgt[%6"PRIDX"]. ISep: %6"PRIDX"\n", + pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, badmaxpwgt, + graph->mincut)); + + to = (pwgts[0] < pwgts[1] ? 1 : 0); + for (pass=0; passmincut; + nbnd = graph->nbnd; + + /* use the swaps array in place of the traditional perm array to save memory */ + irandArrayPermute(nbnd, swaps, nbnd, 1); + for (ii=0; ii= 2*nvtxs-1) + break; + + inqueue[higain] = -1; + + if (pwgts[to]+vwgt[higain] > badmaxpwgt) { /* Skip this vertex */ + if (nbad++ > limit) + break; + else { + nswaps--; + continue; + } + } + + pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[from]); + + newdiff = abs(pwgts[to]+vwgt[higain] - (pwgts[from]-rinfo[higain].edegrees[from])); + if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) { + mincut = pwgts[2]; + mincutorder = nswaps; + mindiff = newdiff; + nbad = 0; + } + else { + if (nbad++ > limit) { + pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[from]); + break; /* No further improvement, break out */ + } + } + + BNDDelete(nbnd, bndind, bndptr, higain); + pwgts[to] += vwgt[higain]; + where[higain] = to; + swaps[nswaps] = higain; + + + /********************************************************** + * Update the degrees of the affected nodes + ***********************************************************/ + for (j=xadj[higain]; jdbglvl, METIS_DBG_MOVEINFO, + printf("Moved %6"PRIDX" to %3"PRIDX", Gain: %5"PRIDX" [%5"PRIDX"] \t[%5"PRIDX" %5"PRIDX" %5"PRIDX"] [%3"PRIDX" %2"PRIDX"]\n", + higain, to, (vwgt[higain]-rinfo[higain].edegrees[from]), + vwgt[higain], pwgts[0], pwgts[1], pwgts[2], nswaps, limit)); + + } + + + /**************************************************************** + * Roll back computation + *****************************************************************/ + for (nswaps--; nswaps>mincutorder; nswaps--) { + higain = swaps[nswaps]; + + ASSERT(CheckNodePartitionParams(graph)); + ASSERT(where[higain] == to); + + INC_DEC(pwgts[2], pwgts[to], vwgt[higain]); + where[higain] = 2; + BNDInsert(nbnd, bndind, bndptr, higain); + + edegrees = rinfo[higain].edegrees; + edegrees[0] = edegrees[1] = 0; + for (j=xadj[higain]; jdbglvl, METIS_DBG_REFINE, + printf("\tMinimum sep: %6"PRIDX" at %5"PRIDX", PWGTS: [%6"PRIDX" %6"PRIDX"], NBND: %6"PRIDX", QSIZE: %6"PRIDX"\n", + mincut, mincutorder, pwgts[0], pwgts[1], nbnd, qsize)); + + graph->mincut = mincut; + graph->nbnd = nbnd; + + if (pass%2 == 1 && (mincutorder == -1 || mincut >= initcut)) + break; + } + + rpqDestroy(queue); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function performs a node-based (two-sided) FM refinement that + moves only nodes whose hmarker[] == -1. It is used by Parmetis. */ +/*************************************************************************/ +void FM_2WayNodeRefine2SidedP(ctrl_t *ctrl, graph_t *graph, + idx_t *hmarker, real_t ubfactor, idx_t npasses) +{ + idx_t i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind; + idx_t *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; + idx_t *mptr, *mind, *moved, *swaps; + rpq_t *queues[2]; + nrinfo_t *rinfo; + idx_t higain, oldgain, mincut, initcut, mincutorder; + idx_t pass, to, other, limit; + idx_t badmaxpwgt, mindiff, newdiff; + idx_t u[2], g[2]; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + + bndind = graph->bndind; + bndptr = graph->bndptr; + where = graph->where; + pwgts = graph->pwgts; + rinfo = graph->nrinfo; + + queues[0] = rpqCreate(nvtxs); + queues[1] = rpqCreate(nvtxs); + + moved = iwspacemalloc(ctrl, nvtxs); + swaps = iwspacemalloc(ctrl, nvtxs); + mptr = iwspacemalloc(ctrl, nvtxs+1); + mind = iwspacemalloc(ctrl, 2*nvtxs); + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("Partitions: [%6"PRIDX" %6"PRIDX"] Nv-Nb[%6"PRIDX" %6"PRIDX"]. ISep: %6"PRIDX"\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); + + badmaxpwgt = (idx_t)(ubfactor*gk_max(pwgts[0], pwgts[1])); + + for (pass=0; passmincut; + nbnd = graph->nbnd; + + /* use the swaps array in place of the traditional perm array to save memory */ + irandArrayPermute(nbnd, swaps, nbnd, 1); + for (ii=0; ii g[1] ? 0 : (g[0] < g[1] ? 1 : pass%2)); + + if (pwgts[to]+vwgt[u[to]] > badmaxpwgt) + to = (to+1)%2; + } + else if (u[0] == -1 && u[1] == -1) { + break; + } + else if (u[0] != -1 && pwgts[0]+vwgt[u[0]] <= badmaxpwgt) { + to = 0; + } + else if (u[1] != -1 && pwgts[1]+vwgt[u[1]] <= badmaxpwgt) { + to = 1; + } + else + break; + + other = (to+1)%2; + + higain = rpqGetTop(queues[to]); + + /* Delete its matching entry in the other queue */ + if (moved[higain] == -5) + rpqDelete(queues[other], higain); + + ASSERT(bndptr[higain] != -1); + + /* The following check is to ensure we break out if there is a posibility + of over-running the mind array. */ + if (nmind + xadj[higain+1]-xadj[higain] >= 2*nvtxs-1) + break; + + pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]); + + newdiff = abs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other])); + if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) { + mincut = pwgts[2]; + mincutorder = nswaps; + mindiff = newdiff; + } + else { + if (nswaps - mincutorder > limit) { + pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]); + break; /* No further improvement, break out */ + } + } + + BNDDelete(nbnd, bndind, bndptr, higain); + pwgts[to] += vwgt[higain]; + where[higain] = to; + moved[higain] = nswaps; + swaps[nswaps] = higain; + + + /********************************************************** + * Update the degrees of the affected nodes + ***********************************************************/ + for (j=xadj[higain]; jdbglvl, METIS_DBG_MOVEINFO, + printf("Moved %6"PRIDX" to %3"PRIDX", Gain: %5"PRIDX" [%5"PRIDX"] " + "[%4"PRIDX" %4"PRIDX"] \t[%5"PRIDX" %5"PRIDX" %5"PRIDX"]\n", + higain, to, g[to], g[other], vwgt[u[to]], vwgt[u[other]], + pwgts[0], pwgts[1], pwgts[2])); + + } + + + /**************************************************************** + * Roll back computation + *****************************************************************/ + for (nswaps--; nswaps>mincutorder; nswaps--) { + higain = swaps[nswaps]; + + ASSERT(CheckNodePartitionParams(graph)); + + to = where[higain]; + other = (to+1)%2; + INC_DEC(pwgts[2], pwgts[to], vwgt[higain]); + where[higain] = 2; + BNDInsert(nbnd, bndind, bndptr, higain); + + edegrees = rinfo[higain].edegrees; + edegrees[0] = edegrees[1] = 0; + for (j=xadj[higain]; jdbglvl, METIS_DBG_REFINE, + printf("\tMinimum sep: %6"PRIDX" at %5"PRIDX", PWGTS: [%6"PRIDX" %6"PRIDX"], NBND: %6"PRIDX"\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd)); + + graph->mincut = mincut; + graph->nbnd = nbnd; + + if (mincutorder == -1 || mincut >= initcut) + break; + } + + rpqDestroy(queues[0]); + rpqDestroy(queues[1]); + + WCOREPOP; +} + diff --git a/src/metis/libmetis/pmetis.c b/src/metis/libmetis/pmetis.c new file mode 100644 index 0000000..d32e849 --- /dev/null +++ b/src/metis/libmetis/pmetis.c @@ -0,0 +1,387 @@ +/** +\file +\brief This file contains the top level routines for the multilevel recursive bisection + algorithm PMETIS. + +\date Started 7/24/1997 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version\verbatim $Id: pmetis.c 10513 2011-07-07 22:06:03Z karypis $ \endverbatim +*/ + + +#include "metislib.h" + + +/*************************************************************************/ +/*! \ingroup api + \brief Recursive partitioning routine. + + This function computes a partitioning of a graph based on multilevel + recursive bisection. It can be used to partition a graph into \e k + parts. The objective of the partitioning is to minimize the edgecut + subject to one or more balancing constraints. + + \param[in] nvtxs is the number of vertices in the graph. + + \param[in] ncon is the number of balancing constraints. For the standard + partitioning problem in which each vertex is either unweighted + or has a single weight, ncon should be 1. + + \param[in] xadj is an array of size nvtxs+1 used to specify the starting + positions of the adjacency structure of the vertices in the + adjncy array. + + \param[in] adjncy is an array of size to the sum of the degrees of the + graph that stores for each vertex the set of vertices that + is adjancent to. + + \param[in] vwgt is an array of size nvtxs*ncon that stores the weights + of the vertices for each constraint. The ncon weights for the + ith vertex are stored in the ncon consecutive locations starting + at vwgt[i*ncon]. When ncon==1, a NULL value can be passed indicating + that all the vertices in the graph have the same weight. + + \param[in] adjwgt is an array of size equal to adjncy, specifying the weight + for each edge (i.e., adjwgt[j] corresponds to the weight of the + edge stored in adjncy[j]). + A NULL value can be passed indicating that all the edges in the + graph have the same weight. + + \param[in] nparts is the number of desired partitions. + + \param[in] tpwgts is an array of size nparts*ncon that specifies the + desired weight for each part and constraint. The \e{target partition + weight} for the ith part and jth constraint is specified + at tpwgts[i*ncon+j] (the numbering of i and j starts from 0). + For each constraint, the sum of the tpwgts[] entries must be + 1.0 (i.e., \f$ \sum_i tpwgts[i*ncon+j] = 1.0 \f$). + A NULL value can be passed indicating that the graph should + be equally divided among the parts. + + \param[in] ubvec is an array of size ncon that specifies the allowed + load imbalance tolerance for each constraint. + For the ith part and jth constraint the allowed weight is the + ubvec[j]*tpwgts[i*ncon+j] fraction of the jth's constraint total + weight. The load imbalances must be greater than 1.0. + A NULL value can be passed indicating that the load imbalance + tolerance for each constraint should be 1.001 (for ncon==1) + or 1.01 (for ncon>1). + + \params[in] options is the array for passing additional parameters + in order to customize the behaviour of the partitioning + algorithm. + + \params[out] edgecut stores the cut of the partitioning. + + \params[out] part is an array of size nvtxs used to store the + computed partitioning. The partition number for the ith + vertex is stored in part[i]. Based on the numflag parameter, + the numbering of the parts starts from either 0 or 1. + + + \returns + \retval METIS_OK indicates that the function returned normally. + \retval METIS_ERROR_INPUT indicates an input error. + \retval METIS_ERROR_MEMORY indicates that it could not allocate + the required memory. + +*/ +/*************************************************************************/ +int METIS_PartGraphRecursive(idx_t *nvtxs, idx_t *ncon, idx_t *xadj, + idx_t *adjncy, idx_t *vwgt, idx_t *vsize, idx_t *adjwgt, + idx_t *nparts, real_t *tpwgts, real_t *ubvec, idx_t *options, + idx_t *objval, idx_t *part) +{ + int sigrval=0, renumber=0; + graph_t *graph; + ctrl_t *ctrl; + + /* set up malloc cleaning code and signal catchers */ + if (!gk_malloc_init()) + return METIS_ERROR_MEMORY; + + gk_sigtrap(); + + if ((sigrval = gk_sigcatch()) != 0) + goto SIGTHROW; + + + /* set up the run parameters */ + ctrl = SetupCtrl(METIS_OP_PMETIS, options, *ncon, *nparts, tpwgts, ubvec); + if (!ctrl) { + gk_siguntrap(); + return METIS_ERROR_INPUT; + } + + /* if required, change the numbering to 0 */ + if (ctrl->numflag == 1) { + Change2CNumbering(*nvtxs, xadj, adjncy); + renumber = 1; + } + + /* set up the graph */ + graph = SetupGraph(ctrl, *nvtxs, *ncon, xadj, adjncy, vwgt, vsize, adjwgt); + + /* allocate workspace memory */ + AllocateWorkSpace(ctrl, graph); + + /* start the partitioning */ + IFSET(ctrl->dbglvl, METIS_DBG_TIME, InitTimers(ctrl)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->TotalTmr)); + + *objval = MlevelRecursiveBisection(ctrl, graph, *nparts, part, ctrl->tpwgts, 0); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->TotalTmr)); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, PrintTimers(ctrl)); + + /* clean up */ + FreeCtrl(&ctrl); + +SIGTHROW: + /* if required, change the numbering back to 1 */ + if (renumber) + Change2FNumbering(*nvtxs, xadj, adjncy, part); + + gk_siguntrap(); + gk_malloc_cleanup(0); + + return metis_rcode(sigrval); +} + + +/*************************************************************************/ +/*! This function is the top-level driver of the recursive bisection + routine. */ +/*************************************************************************/ +idx_t MlevelRecursiveBisection(ctrl_t *ctrl, graph_t *graph, idx_t nparts, + idx_t *part, real_t *tpwgts, idx_t fpart) +{ + idx_t i, j, nvtxs, ncon, objval; + idx_t *label, *where; + graph_t *lgraph, *rgraph; + real_t wsum, *tpwgts2; + + if ((nvtxs = graph->nvtxs) == 0) { + printf("\t***Cannot bisect a graph with 0 vertices!\n" + "\t***You are trying to partition a graph into too many parts!\n"); + return 0; + } + + ncon = graph->ncon; + + /* determine the weights of the two partitions as a function of the weight of the + target partition weights */ + WCOREPUSH; + tpwgts2 = rwspacemalloc(ctrl, 2*ncon); + for (i=0; i>1), tpwgts+i, ncon); + tpwgts2[ncon+i] = 1.0 - tpwgts2[i]; + } + + /* perform the bisection */ + objval = MultilevelBisect(ctrl, graph, tpwgts2); + + WCOREPOP; + + label = graph->label; + where = graph->where; + for (i=0; i 2) + SplitGraphPart(ctrl, graph, &lgraph, &rgraph); + + /* Free the memory of the top level graph */ + FreeGraph(&graph); + + /* Scale the fractions in the tpwgts according to the true weight */ + for (i=0; i>1), tpwgts+i, ncon); + rscale((nparts>>1), 1.0/wsum, tpwgts+i, ncon); + rscale(nparts-(nparts>>1), 1.0/(1.0-wsum), tpwgts+(nparts>>1)*ncon+i, ncon); + } + + /* Do the recursive call */ + if (nparts > 3) { + objval += MlevelRecursiveBisection(ctrl, lgraph, (nparts>>1), part, + tpwgts, fpart); + objval += MlevelRecursiveBisection(ctrl, rgraph, nparts-(nparts>>1), part, + tpwgts+(nparts>>1)*ncon, fpart+(nparts>>1)); + } + else if (nparts == 3) { + FreeGraph(&lgraph); + objval += MlevelRecursiveBisection(ctrl, rgraph, nparts-(nparts>>1), part, + tpwgts+(nparts>>1)*ncon, fpart+(nparts>>1)); + } + + + return objval; +} + + +/*************************************************************************/ +/*! This function performs a multilevel bisection */ +/*************************************************************************/ +idx_t MultilevelBisect(ctrl_t *ctrl, graph_t *graph, real_t *tpwgts) +{ + idx_t i, niparts, bestobj=0, curobj=0, *bestwhere=NULL; + graph_t *cgraph; + real_t bestbal=0.0, curbal=0.0; + + Setup2WayBalMultipliers(ctrl, graph, tpwgts); + + WCOREPUSH; + + if (ctrl->ncuts > 1) + bestwhere = iwspacemalloc(ctrl, graph->nvtxs); + + for (i=0; incuts; i++) { + cgraph = CoarsenGraph(ctrl, graph); + + niparts = (cgraph->nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS); + Init2WayPartition(ctrl, cgraph, tpwgts, niparts); + + Refine2Way(ctrl, graph, cgraph, tpwgts); + + curobj = graph->mincut; + curbal = ComputeLoadImbalanceDiff(graph, 2, ctrl->pijbm, ctrl->ubfactors); + + if (i == 0 + || (curbal <= 0.0005 && bestobj > curobj) + || (bestbal > 0.0005 && curbal < bestbal)) { + bestobj = curobj; + bestbal = curbal; + if (i < ctrl->ncuts-1) + icopy(graph->nvtxs, graph->where, bestwhere); + } + + if (bestobj == 0) + break; + + if (i < ctrl->ncuts-1) + FreeRData(graph); + } + + if (bestobj != curobj) { + icopy(graph->nvtxs, bestwhere, graph->where); + Compute2WayPartitionParams(ctrl, graph); + } + + WCOREPOP; + + return bestobj; +} + + +/*************************************************************************/ +/*! This function splits a graph into two based on its bisection */ +/*************************************************************************/ +void SplitGraphPart(ctrl_t *ctrl, graph_t *graph, graph_t **r_lgraph, + graph_t **r_rgraph) +{ + idx_t i, j, k, l, istart, iend, mypart, nvtxs, ncon, snvtxs[2], snedges[2]; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *label, *where, *bndptr; + idx_t *sxadj[2], *svwgt[2], *sadjncy[2], *sadjwgt[2], *slabel[2]; + idx_t *rename; + idx_t *auxadjncy, *auxadjwgt; + graph_t *lgraph, *rgraph; + + WCOREPUSH; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->SplitTmr)); + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + label = graph->label; + where = graph->where; + bndptr = graph->bndptr; + + ASSERT(bndptr != NULL); + + rename = iwspacemalloc(ctrl, nvtxs); + + snvtxs[0] = snvtxs[1] = snedges[0] = snedges[1] = 0; + for (i=0; ixadj; + svwgt[0] = lgraph->vwgt; + sadjncy[0] = lgraph->adjncy; + sadjwgt[0] = lgraph->adjwgt; + slabel[0] = lgraph->label; + + rgraph = SetupSplitGraph(graph, snvtxs[1], snedges[1]); + sxadj[1] = rgraph->xadj; + svwgt[1] = rgraph->vwgt; + sadjncy[1] = rgraph->adjncy; + sadjwgt[1] = rgraph->adjwgt; + slabel[1] = rgraph->label; + + snvtxs[0] = snvtxs[1] = snedges[0] = snedges[1] = 0; + sxadj[0][0] = sxadj[1][0] = 0; + for (i=0; inedges = snedges[0]; + rgraph->nedges = snedges[1]; + + SetupGraph_tvwgt(lgraph); + SetupGraph_tvwgt(rgraph); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->SplitTmr)); + + *r_lgraph = lgraph; + *r_rgraph = rgraph; + + WCOREPOP; +} + diff --git a/src/metis/libmetis/proto.h b/src/metis/libmetis/proto.h new file mode 100644 index 0000000..f852ff5 --- /dev/null +++ b/src/metis/libmetis/proto.h @@ -0,0 +1,348 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * proto.h + * + * This file contains header files + * + * Started 10/19/95 + * George + * + * $Id: proto.h 13933 2013-03-29 22:20:46Z karypis $ + * + */ + +#ifndef _LIBMETIS_PROTO_H_ +#define _LIBMETIS_PROTO_H_ + +/* auxapi.c */ + +/* balance.c */ +void Balance2Way(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts); +void Bnd2WayBalance(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts); +void General2WayBalance(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts); +void McGeneral2WayBalance(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts); + + +/* bucketsort.c */ +void BucketSortKeysInc(ctrl_t *ctrl, idx_t n, idx_t max, idx_t *keys, + idx_t *tperm, idx_t *perm); + + +/* checkgraph.c */ +int CheckGraph(graph_t *graph, int numflag, int verbose); +int CheckInputGraphWeights(idx_t nvtxs, idx_t ncon, idx_t *xadj, idx_t *adjncy, + idx_t *vwgt, idx_t *vsize, idx_t *adjwgt); +graph_t *FixGraph(graph_t *graph); + + +/* coarsen.c */ +graph_t *CoarsenGraph(ctrl_t *ctrl, graph_t *graph); +graph_t *CoarsenGraphNlevels(ctrl_t *ctrl, graph_t *graph, idx_t nlevels); +idx_t Match_RM(ctrl_t *ctrl, graph_t *graph); +idx_t Match_SHEM(ctrl_t *ctrl, graph_t *graph); +idx_t Match_2Hop(ctrl_t *ctrl, graph_t *graph, idx_t *perm, idx_t *match, + idx_t cnvtxs, size_t nunmatched); +idx_t Match_2HopAny(ctrl_t *ctrl, graph_t *graph, idx_t *perm, idx_t *match, + idx_t cnvtxs, size_t *r_nunmatched, size_t maxdegree); +idx_t Match_2HopAll(ctrl_t *ctrl, graph_t *graph, idx_t *perm, idx_t *match, + idx_t cnvtxs, size_t *r_nunmatched, size_t maxdegree); +void PrintCGraphStats(ctrl_t *ctrl, graph_t *graph); +void CreateCoarseGraph(ctrl_t *ctrl, graph_t *graph, idx_t cnvtxs, + idx_t *match); +void CreateCoarseGraphNoMask(ctrl_t *ctrl, graph_t *graph, idx_t cnvtxs, + idx_t *match); +void CreateCoarseGraphPerm(ctrl_t *ctrl, graph_t *graph, idx_t cnvtxs, + idx_t *match, idx_t *perm); +graph_t *SetupCoarseGraph(graph_t *graph, idx_t cnvtxs, idx_t dovsize); +void ReAdjustMemory(ctrl_t *ctrl, graph_t *graph, graph_t *cgraph); + + + +/* compress.c */ +graph_t *CompressGraph(ctrl_t *ctrl, idx_t nvtxs, idx_t *xadj, idx_t *adjncy, + idx_t *vwgt, idx_t *cptr, idx_t *cind); +graph_t *PruneGraph(ctrl_t *ctrl, idx_t nvtxs, idx_t *xadj, idx_t *adjncy, + idx_t *vwgt, idx_t *iperm, real_t factor); + + +/* contig.c */ +idx_t FindPartitionInducedComponents(graph_t *graph, idx_t *where, + idx_t *cptr, idx_t *cind); +void ComputeBFSOrdering(ctrl_t *ctrl, graph_t *graph, idx_t *bfsperm); +idx_t IsConnected(graph_t *graph, idx_t report); +idx_t IsConnectedSubdomain(ctrl_t *, graph_t *, idx_t, idx_t); +idx_t FindSepInducedComponents(ctrl_t *, graph_t *, idx_t *, idx_t *); +void EliminateComponents(ctrl_t *ctrl, graph_t *graph); +void MoveGroupContigForCut(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t gid, + idx_t *ptr, idx_t *ind); +void MoveGroupContigForVol(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t gid, + idx_t *ptr, idx_t *ind, idx_t *vmarker, idx_t *pmarker, + idx_t *modind); + + +/* debug.c */ +idx_t ComputeCut(graph_t *graph, idx_t *where); +idx_t ComputeVolume(graph_t *, idx_t *); +idx_t ComputeMaxCut(graph_t *graph, idx_t nparts, idx_t *where); +idx_t CheckBnd(graph_t *); +idx_t CheckBnd2(graph_t *); +idx_t CheckNodeBnd(graph_t *, idx_t); +idx_t CheckRInfo(ctrl_t *ctrl, ckrinfo_t *rinfo); +idx_t CheckNodePartitionParams(graph_t *); +idx_t IsSeparable(graph_t *); +void CheckKWayVolPartitionParams(ctrl_t *ctrl, graph_t *graph); + + +/* fm.c */ +void FM_2WayRefine(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niter); +void FM_2WayCutRefine(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niter); +void FM_Mc2WayCutRefine(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niter); +void SelectQueue(graph_t *graph, real_t *pijbm, real_t *ubfactors, rpq_t **queues, + idx_t *from, idx_t *cnum); +void Print2WayRefineStats(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, + real_t deltabal, idx_t mincutorder); + + +/* fortran.c */ +void Change2CNumbering(idx_t, idx_t *, idx_t *); +void Change2FNumbering(idx_t, idx_t *, idx_t *, idx_t *); +void Change2FNumbering2(idx_t, idx_t *, idx_t *); +void Change2FNumberingOrder(idx_t, idx_t *, idx_t *, idx_t *, idx_t *); +void ChangeMesh2CNumbering(idx_t n, idx_t *ptr, idx_t *ind); +void ChangeMesh2FNumbering(idx_t n, idx_t *ptr, idx_t *ind, idx_t nvtxs, + idx_t *xadj, idx_t *adjncy); +void ChangeMesh2FNumbering2(idx_t ne, idx_t nn, idx_t *ptr, idx_t *ind, + idx_t *epart, idx_t *npart); + + +/* graph.c */ +graph_t *SetupGraph(ctrl_t *ctrl, idx_t nvtxs, idx_t ncon, idx_t *xadj, + idx_t *adjncy, idx_t *vwgt, idx_t *vsize, idx_t *adjwgt); +void SetupGraph_tvwgt(graph_t *graph); +void SetupGraph_label(graph_t *graph); +graph_t *SetupSplitGraph(graph_t *graph, idx_t snvtxs, idx_t snedges); +graph_t *CreateGraph(void); +void InitGraph(graph_t *graph); +void FreeRData(graph_t *graph); +void FreeGraph(graph_t **graph); + + +/* initpart.c */ +void Init2WayPartition(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niparts); +void InitSeparator(ctrl_t *ctrl, graph_t *graph, idx_t niparts); +void RandomBisection(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niparts); +void GrowBisection(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niparts); +void McRandomBisection(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niparts); +void McGrowBisection(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niparts); +void GrowBisectionNode(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niparts); +void GrowBisectionNode2(ctrl_t *ctrl, graph_t *graph, real_t *ntpwgts, idx_t niparts); + + +/* kmetis.c */ +idx_t MlevelKWayPartitioning(ctrl_t *ctrl, graph_t *graph, idx_t *part); +void InitKWayPartitioning(ctrl_t *ctrl, graph_t *graph); + + +/* kwayfm.c */ +void Greedy_KWayOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode); +void Greedy_KWayCutOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode); +void Greedy_KWayVolOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode); +void Greedy_McKWayCutOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode); +void Greedy_McKWayVolOptimize(ctrl_t *ctrl, graph_t *graph, idx_t niter, + real_t ffactor, idx_t omode); +idx_t IsArticulationNode(idx_t i, idx_t *xadj, idx_t *adjncy, idx_t *where, + idx_t *bfslvl, idx_t *bfsind, idx_t *bfsmrk); +void KWayVolUpdate(ctrl_t *ctrl, graph_t *graph, idx_t v, idx_t from, + idx_t to, ipq_t *queue, idx_t *vstatus, idx_t *r_nupd, idx_t *updptr, + idx_t *updind, idx_t bndtype, idx_t *vmarker, idx_t *pmarker, + idx_t *modind); + + +/* kwayrefine.c */ +void RefineKWay(ctrl_t *ctrl, graph_t *orggraph, graph_t *graph); +void AllocateKWayPartitionMemory(ctrl_t *ctrl, graph_t *graph); +void ComputeKWayPartitionParams(ctrl_t *ctrl, graph_t *graph); +void ProjectKWayPartition(ctrl_t *ctrl, graph_t *graph); +void ComputeKWayBoundary(ctrl_t *ctrl, graph_t *graph, idx_t bndtype); +void ComputeKWayVolGains(ctrl_t *ctrl, graph_t *graph); +int IsBalanced(ctrl_t *ctrl, graph_t *graph, real_t ffactor); + + +/* mcutil.c */ +int rvecle(idx_t n, real_t *x, real_t *y); +int rvecge(idx_t n, real_t *x, real_t *y); +int rvecsumle(idx_t n, real_t *x1, real_t *x2, real_t *y); +real_t rvecmaxdiff(idx_t n, real_t *x, real_t *y); +int ivecle(idx_t n, idx_t *x, idx_t *z); +int ivecge(idx_t n, idx_t *x, idx_t *z); +int ivecaxpylez(idx_t n, idx_t a, idx_t *x, idx_t *y, idx_t *z); +int ivecaxpygez(idx_t n, idx_t a, idx_t *x, idx_t *y, idx_t *z); +int BetterVBalance(idx_t ncon, real_t *itvwgt, idx_t *v_vwgt, idx_t *u1_vwgt, + idx_t *u2_vwgt); +int BetterBalance2Way(idx_t n, real_t *x, real_t *y); +int BetterBalanceKWay(idx_t ncon, idx_t *vwgt, real_t *itvwgt, idx_t a1, + idx_t *pt1, real_t *bm1, idx_t a2, idx_t *pt2, real_t *bm2); +real_t ComputeLoadImbalance(graph_t *graph, idx_t nparts, real_t *pijbm); +real_t ComputeLoadImbalanceDiff(graph_t *graph, idx_t nparts, real_t *pijbm, + real_t *ubvec); +real_t ComputeLoadImbalanceDiffVec(graph_t *graph, idx_t nparts, real_t *pijbm, + real_t *ubfactors, real_t *diffvec); +void ComputeLoadImbalanceVec(graph_t *graph, idx_t nparts, real_t *pijbm, + real_t *lbvec); + + +/* mesh.c */ +void CreateGraphDual(idx_t ne, idx_t nn, idx_t *eptr, idx_t *eind, idx_t ncommon, + idx_t **r_xadj, idx_t **r_adjncy); +idx_t FindCommonElements(idx_t qid, idx_t elen, idx_t *eind, idx_t *nptr, + idx_t *nind, idx_t *eptr, idx_t ncommon, idx_t *marker, idx_t *nbrs); +void CreateGraphNodal(idx_t ne, idx_t nn, idx_t *eptr, idx_t *eind, idx_t **r_xadj, + idx_t **r_adjncy); +idx_t FindCommonNodes(idx_t qid, idx_t nelmnts, idx_t *elmntids, idx_t *eptr, + idx_t *eind, idx_t *marker, idx_t *nbrs); +mesh_t *CreateMesh(void); +void InitMesh(mesh_t *mesh); +void FreeMesh(mesh_t **mesh); + + +/* meshpart.c */ +void InduceRowPartFromColumnPart(idx_t nrows, idx_t *rowptr, idx_t *rowind, + idx_t *rpart, idx_t *cpart, idx_t nparts, real_t *tpwgts); + + +/* minconn.c */ +void ComputeSubDomainGraph(ctrl_t *ctrl, graph_t *graph); +void UpdateEdgeSubDomainGraph(ctrl_t *ctrl, idx_t u, idx_t v, idx_t ewgt, + idx_t *r_maxndoms); +void PrintSubDomainGraph(graph_t *graph, idx_t nparts, idx_t *where); +void EliminateSubDomainEdges(ctrl_t *ctrl, graph_t *graph); +void MoveGroupMinConnForCut(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t nind, + idx_t *ind); +void MoveGroupMinConnForVol(ctrl_t *ctrl, graph_t *graph, idx_t to, idx_t nind, + idx_t *ind, idx_t *vmarker, idx_t *pmarker, idx_t *modind); + + +/* mincover.o */ +void MinCover(idx_t *, idx_t *, idx_t, idx_t, idx_t *, idx_t *); +idx_t MinCover_Augment(idx_t *, idx_t *, idx_t, idx_t *, idx_t *, idx_t *, idx_t); +void MinCover_Decompose(idx_t *, idx_t *, idx_t, idx_t, idx_t *, idx_t *, idx_t *); +void MinCover_ColDFS(idx_t *, idx_t *, idx_t, idx_t *, idx_t *, idx_t); +void MinCover_RowDFS(idx_t *, idx_t *, idx_t, idx_t *, idx_t *, idx_t); + + +/* mmd.c */ +void genmmd(idx_t, idx_t *, idx_t *, idx_t *, idx_t *, idx_t , idx_t *, idx_t *, idx_t *, idx_t *, idx_t, idx_t *); +void mmdelm(idx_t, idx_t *xadj, idx_t *, idx_t *, idx_t *, idx_t *, idx_t *, idx_t *, idx_t *, idx_t, idx_t); +idx_t mmdint(idx_t, idx_t *xadj, idx_t *, idx_t *, idx_t *, idx_t *, idx_t *, idx_t *, idx_t *); +void mmdnum(idx_t, idx_t *, idx_t *, idx_t *); +void mmdupd(idx_t, idx_t, idx_t *, idx_t *, idx_t, idx_t *, idx_t *, idx_t *, idx_t *, idx_t *, idx_t *, idx_t *, idx_t, idx_t *tag); + + +/* ometis.c */ +void MlevelNestedDissection(ctrl_t *ctrl, graph_t *graph, idx_t *order, + idx_t lastvtx); +void MlevelNestedDissectionCC(ctrl_t *ctrl, graph_t *graph, idx_t *order, + idx_t lastvtx); +void MlevelNodeBisectionMultiple(ctrl_t *ctrl, graph_t *graph); +void MlevelNodeBisectionL2(ctrl_t *ctrl, graph_t *graph, idx_t niparts); +void MlevelNodeBisectionL1(ctrl_t *ctrl, graph_t *graph, idx_t niparts); +void SplitGraphOrder(ctrl_t *ctrl, graph_t *graph, graph_t **r_lgraph, + graph_t **r_rgraph); +graph_t **SplitGraphOrderCC(ctrl_t *ctrl, graph_t *graph, idx_t ncmps, + idx_t *cptr, idx_t *cind); +void MMDOrder(ctrl_t *ctrl, graph_t *graph, idx_t *order, idx_t lastvtx); + + +/* options.c */ +ctrl_t *SetupCtrl(moptype_et optype, idx_t *options, idx_t ncon, idx_t nparts, + real_t *tpwgts, real_t *ubvec); +void SetupKWayBalMultipliers(ctrl_t *ctrl, graph_t *graph); +void Setup2WayBalMultipliers(ctrl_t *ctrl, graph_t *graph, real_t *tpwgts); +void PrintCtrl(ctrl_t *ctrl); +int CheckParams(ctrl_t *ctrl); +void FreeCtrl(ctrl_t **r_ctrl); + + +/* parmetis.c */ +void MlevelNestedDissectionP(ctrl_t *ctrl, graph_t *graph, idx_t *order, + idx_t lastvtx, idx_t npes, idx_t cpos, idx_t *sizes); +void FM_2WayNodeRefine1SidedP(ctrl_t *ctrl, graph_t *graph, idx_t *hmarker, + real_t ubfactor, idx_t npasses); +void FM_2WayNodeRefine2SidedP(ctrl_t *ctrl, graph_t *graph, idx_t *hmarker, + real_t ubfactor, idx_t npasses); + + +/* pmetis.c */ +idx_t MlevelRecursiveBisection(ctrl_t *ctrl, graph_t *graph, idx_t nparts, + idx_t *part, real_t *tpwgts, idx_t fpart); +idx_t MultilevelBisect(ctrl_t *ctrl, graph_t *graph, real_t *tpwgts); +void SplitGraphPart(ctrl_t *ctrl, graph_t *graph, graph_t **r_lgraph, graph_t **r_rgraph); + + +/* refine.c */ +void Refine2Way(ctrl_t *ctrl, graph_t *orggraph, graph_t *graph, real_t *rtpwgts); +void Allocate2WayPartitionMemory(ctrl_t *ctrl, graph_t *graph); +void Compute2WayPartitionParams(ctrl_t *ctrl, graph_t *graph); +void Project2WayPartition(ctrl_t *ctrl, graph_t *graph); + + +/* separator.c */ +void ConstructSeparator(ctrl_t *ctrl, graph_t *graph); +void ConstructMinCoverSeparator(ctrl_t *ctrl, graph_t *graph); + + +/* sfm.c */ +void FM_2WayNodeRefine2Sided(ctrl_t *ctrl, graph_t *graph, idx_t niter); +void FM_2WayNodeRefine1Sided(ctrl_t *ctrl, graph_t *graph, idx_t niter); +void FM_2WayNodeBalance(ctrl_t *ctrl, graph_t *graph); + + +/* srefine.c */ +void Refine2WayNode(ctrl_t *ctrl, graph_t *orggraph, graph_t *graph); +void Allocate2WayNodePartitionMemory(ctrl_t *ctrl, graph_t *graph); +void Compute2WayNodePartitionParams(ctrl_t *ctrl, graph_t *graph); +void Project2WayNodePartition(ctrl_t *ctrl, graph_t *graph); + + +/* stat.c */ +void ComputePartitionInfoBipartite(graph_t *, idx_t, idx_t *); +void ComputePartitionBalance(graph_t *, idx_t, idx_t *, real_t *); +real_t ComputeElementBalance(idx_t, idx_t, idx_t *); + + +/* timing.c */ +void InitTimers(ctrl_t *); +void PrintTimers(ctrl_t *); + +/* util.c */ +idx_t iargmax_strd(size_t, idx_t *, idx_t); +idx_t iargmax_nrm(size_t n, idx_t *x, real_t *y); +idx_t iargmax2_nrm(size_t n, idx_t *x, real_t *y); +idx_t rargmax2(size_t, real_t *); +void InitRandom(idx_t); +int metis_rcode(int sigrval); + + + +/* wspace.c */ +void AllocateWorkSpace(ctrl_t *ctrl, graph_t *graph); +void AllocateRefinementWorkSpace(ctrl_t *ctrl, idx_t nbrpoolsize); +void FreeWorkSpace(ctrl_t *ctrl); +void *wspacemalloc(ctrl_t *ctrl, size_t nbytes); +void wspacepush(ctrl_t *ctrl); +void wspacepop(ctrl_t *ctrl); +idx_t *iwspacemalloc(ctrl_t *, idx_t); +real_t *rwspacemalloc(ctrl_t *, idx_t); +ikv_t *ikvwspacemalloc(ctrl_t *, idx_t); +void cnbrpoolReset(ctrl_t *ctrl); +idx_t cnbrpoolGetNext(ctrl_t *ctrl, idx_t nnbrs); +void vnbrpoolReset(ctrl_t *ctrl); +idx_t vnbrpoolGetNext(ctrl_t *ctrl, idx_t nnbrs); + + +#endif diff --git a/src/metis/libmetis/refine.c b/src/metis/libmetis/refine.c new file mode 100644 index 0000000..c08dc2d --- /dev/null +++ b/src/metis/libmetis/refine.c @@ -0,0 +1,211 @@ +/* +\file +\brief This file contains the driving routines for multilevel refinement + +\date Started 7/24/1997 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version\verbatim $Id: refine.c 10513 2011-07-07 22:06:03Z karypis $ \endverbatim +*/ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function is the entry point of refinement */ +/*************************************************************************/ +void Refine2Way(ctrl_t *ctrl, graph_t *orggraph, graph_t *graph, real_t *tpwgts) +{ + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); + + /* Compute the parameters of the coarsest graph */ + Compute2WayPartitionParams(ctrl, graph); + + for (;;) { + ASSERT(CheckBnd(graph)); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->RefTmr)); + + Balance2Way(ctrl, graph, tpwgts); + + FM_2WayRefine(ctrl, graph, tpwgts, ctrl->niter); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); + + if (graph == orggraph) + break; + + graph = graph->finer; + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); + Project2WayPartition(ctrl, graph); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); + } + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); +} + + +/*************************************************************************/ +/*! This function allocates memory for 2-way edge refinement */ +/*************************************************************************/ +void Allocate2WayPartitionMemory(ctrl_t *ctrl, graph_t *graph) +{ + idx_t nvtxs, ncon; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + + graph->pwgts = imalloc(2*ncon, "Allocate2WayPartitionMemory: pwgts"); + graph->where = imalloc(nvtxs, "Allocate2WayPartitionMemory: where"); + graph->bndptr = imalloc(nvtxs, "Allocate2WayPartitionMemory: bndptr"); + graph->bndind = imalloc(nvtxs, "Allocate2WayPartitionMemory: bndind"); + graph->id = imalloc(nvtxs, "Allocate2WayPartitionMemory: id"); + graph->ed = imalloc(nvtxs, "Allocate2WayPartitionMemory: ed"); +} + + +/*************************************************************************/ +/*! This function computes the initial id/ed */ +/*************************************************************************/ +void Compute2WayPartitionParams(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, j, nvtxs, ncon, nbnd, mincut, istart, iend, tid, ted, me; + idx_t *xadj, *vwgt, *adjncy, *adjwgt, *pwgts; + idx_t *where, *bndptr, *bndind, *id, *ed; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + where = graph->where; + id = graph->id; + ed = graph->ed; + + pwgts = iset(2*ncon, 0, graph->pwgts); + bndptr = iset(nvtxs, -1, graph->bndptr); + bndind = graph->bndind; + + /* Compute pwgts */ + if (ncon == 1) { + for (i=0; i= 0 && where[i] <= 1); + pwgts[where[i]] += vwgt[i]; + } + ASSERT(pwgts[0]+pwgts[1] == graph->tvwgt[0]); + } + else { + for (i=0; i 0 || istart == iend) { + BNDInsert(nbnd, bndind, bndptr, i); + mincut += ted; + } + } + + graph->mincut = mincut/2; + graph->nbnd = nbnd; + +} + + +/*************************************************************************/ +/*! Projects a partition and computes the refinement params. */ +/*************************************************************************/ +void Project2WayPartition(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, j, istart, iend, nvtxs, nbnd, me, tid, ted; + idx_t *xadj, *adjncy, *adjwgt; + idx_t *cmap, *where, *bndptr, *bndind; + idx_t *cwhere, *cbndptr; + idx_t *id, *ed; + graph_t *cgraph; + + Allocate2WayPartitionMemory(ctrl, graph); + + cgraph = graph->coarser; + cwhere = cgraph->where; + cbndptr = cgraph->bndptr; + + nvtxs = graph->nvtxs; + cmap = graph->cmap; + xadj = graph->xadj; + adjncy = graph->adjncy; + adjwgt = graph->adjwgt; + + where = graph->where; + id = graph->id; + ed = graph->ed; + + bndptr = iset(nvtxs, -1, graph->bndptr); + bndind = graph->bndind; + + /* Project the partition and record which of these nodes came from the + coarser boundary */ + for (i=0; i 0 || istart == iend) + BNDInsert(nbnd, bndind, bndptr, i); + } + graph->mincut = cgraph->mincut; + graph->nbnd = nbnd; + + /* copy pwgts */ + icopy(2*graph->ncon, cgraph->pwgts, graph->pwgts); + + FreeGraph(&graph->coarser); + graph->coarser = NULL; +} + diff --git a/src/metis/libmetis/rename.h b/src/metis/libmetis/rename.h new file mode 100644 index 0000000..62b03b4 --- /dev/null +++ b/src/metis/libmetis/rename.h @@ -0,0 +1,266 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * rename.h + * + * This file contains header files + * + * Started 10/2/97 + * George + * + * $Id: rename.h 13933 2013-03-29 22:20:46Z karypis $ + * + */ + + +#ifndef _LIBMETIS_RENAME_H_ +#define _LIBMETIS_RENAME_H_ + + +/* balance.c */ +#define Balance2Way libmetis__Balance2Way +#define Bnd2WayBalance libmetis__Bnd2WayBalance +#define General2WayBalance libmetis__General2WayBalance +#define McGeneral2WayBalance libmetis__McGeneral2WayBalance + +/* bucketsort.c */ +#define BucketSortKeysInc libmetis__BucketSortKeysInc + +/* checkgraph.c */ +#define CheckGraph libmetis__CheckGraph +#define CheckInputGraphWeights libmetis__CheckInputGraphWeights +#define FixGraph libmetis__FixGraph + +/* coarsen.c */ +#define CoarsenGraph libmetis__CoarsenGraph +#define Match_RM libmetis__Match_RM +#define Match_SHEM libmetis__Match_SHEM +#define Match_2Hop libmetis__Match_2Hop +#define Match_2HopAny libmetis__Match_2HopAny +#define Match_2HopAll libmetis__Match_2HopAll +#define PrintCGraphStats libmetis__PrintCGraphStats +#define CreateCoarseGraph libmetis__CreateCoarseGraph +#define CreateCoarseGraphNoMask libmetis__CreateCoarseGraphNoMask +#define CreateCoarseGraphPerm libmetis__CreateCoarseGraphPerm +#define SetupCoarseGraph libmetis__SetupCoarseGraph +#define ReAdjustMemory libmetis__ReAdjustMemory + +/* compress.c */ +#define CompressGraph libmetis__CompressGraph +#define PruneGraph libmetis__PruneGraph + +/* contig.c */ +#define FindPartitionInducedComponents libmetis__FindPartitionInducedComponents +#define IsConnected libmetis__IsConnected +#define IsConnectedSubdomain libmetis__IsConnectedSubdomain +#define FindSepInducedComponents libmetis__FindSepInducedComponents +#define EliminateComponents libmetis__EliminateComponents +#define MoveGroupContigForCut libmetis__MoveGroupContigForCut +#define MoveGroupContigForVol libmetis__MoveGroupContigForVol + +/* debug.c */ +#define ComputeCut libmetis__ComputeCut +#define ComputeVolume libmetis__ComputeVolume +#define ComputeMaxCut libmetis__ComputeMaxCut +#define CheckBnd libmetis__CheckBnd +#define CheckBnd2 libmetis__CheckBnd2 +#define CheckNodeBnd libmetis__CheckNodeBnd +#define CheckRInfo libmetis__CheckRInfo +#define CheckNodePartitionParams libmetis__CheckNodePartitionParams +#define IsSeparable libmetis__IsSeparable +#define CheckKWayVolPartitionParams libmetis__CheckKWayVolPartitionParams + +/* fm.c */ +#define FM_2WayRefine libmetis__FM_2WayRefine +#define FM_2WayCutRefine libmetis__FM_2WayCutRefine +#define FM_Mc2WayCutRefine libmetis__FM_Mc2WayCutRefine +#define SelectQueue libmetis__SelectQueue +#define Print2WayRefineStats libmetis__Print2WayRefineStats + +/* fortran.c */ +#define Change2CNumbering libmetis__Change2CNumbering +#define Change2FNumbering libmetis__Change2FNumbering +#define Change2FNumbering2 libmetis__Change2FNumbering2 +#define Change2FNumberingOrder libmetis__Change2FNumberingOrder +#define ChangeMesh2CNumbering libmetis__ChangeMesh2CNumbering +#define ChangeMesh2FNumbering libmetis__ChangeMesh2FNumbering +#define ChangeMesh2FNumbering2 libmetis__ChangeMesh2FNumbering2 + +/* graph.c */ +#define SetupGraph libmetis__SetupGraph +#define SetupGraph_adjrsum libmetis__SetupGraph_adjrsum +#define SetupGraph_tvwgt libmetis__SetupGraph_tvwgt +#define SetupGraph_label libmetis__SetupGraph_label +#define SetupSplitGraph libmetis__SetupSplitGraph +#define CreateGraph libmetis__CreateGraph +#define InitGraph libmetis__InitGraph +#define FreeRData libmetis__FreeRData +#define FreeGraph libmetis__FreeGraph + +/* initpart.c */ +#define Init2WayPartition libmetis__Init2WayPartition +#define InitSeparator libmetis__InitSeparator +#define RandomBisection libmetis__RandomBisection +#define GrowBisection libmetis__GrowBisection +#define McRandomBisection libmetis__McRandomBisection +#define McGrowBisection libmetis__McGrowBisection +#define GrowBisectionNode libmetis__GrowBisectionNode + +/* kmetis.c */ +#define MlevelKWayPartitioning libmetis__MlevelKWayPartitioning +#define InitKWayPartitioning libmetis__InitKWayPartitioning + +/* kwayfm.c */ +#define Greedy_KWayOptimize libmetis__Greedy_KWayOptimize +#define Greedy_KWayCutOptimize libmetis__Greedy_KWayCutOptimize +#define Greedy_KWayVolOptimize libmetis__Greedy_KWayVolOptimize +#define Greedy_McKWayCutOptimize libmetis__Greedy_McKWayCutOptimize +#define Greedy_McKWayVolOptimize libmetis__Greedy_McKWayVolOptimize +#define IsArticulationNode libmetis__IsArticulationNode +#define KWayVolUpdate libmetis__KWayVolUpdate + +/* kwayrefine.c */ +#define RefineKWay libmetis__RefineKWay +#define AllocateKWayPartitionMemory libmetis__AllocateKWayPartitionMemory +#define ComputeKWayPartitionParams libmetis__ComputeKWayPartitionParams +#define ProjectKWayPartition libmetis__ProjectKWayPartition +#define ComputeKWayBoundary libmetis__ComputeKWayBoundary +#define ComputeKWayVolGains libmetis__ComputeKWayVolGains +#define IsBalanced libmetis__IsBalanced + +/* mcutil */ +#define rvecle libmetis__rvecle +#define rvecge libmetis__rvecge +#define rvecsumle libmetis__rvecsumle +#define rvecmaxdiff libmetis__rvecmaxdiff +#define ivecle libmetis__ivecle +#define ivecge libmetis__ivecge +#define ivecaxpylez libmetis__ivecaxpylez +#define ivecaxpygez libmetis__ivecaxpygez +#define BetterVBalance libmetis__BetterVBalance +#define BetterBalance2Way libmetis__BetterBalance2Way +#define BetterBalanceKWay libmetis__BetterBalanceKWay +#define ComputeLoadImbalance libmetis__ComputeLoadImbalance +#define ComputeLoadImbalanceDiff libmetis__ComputeLoadImbalanceDiff +#define ComputeLoadImbalanceDiffVec libmetis__ComputeLoadImbalanceDiffVec +#define ComputeLoadImbalanceVec libmetis__ComputeLoadImbalanceVec + +/* mesh.c */ +#define CreateGraphDual libmetis__CreateGraphDual +#define FindCommonElements libmetis__FindCommonElements +#define CreateGraphNodal libmetis__CreateGraphNodal +#define FindCommonNodes libmetis__FindCommonNodes +#define CreateMesh libmetis__CreateMesh +#define InitMesh libmetis__InitMesh +#define FreeMesh libmetis__FreeMesh + +/* meshpart.c */ +#define InduceRowPartFromColumnPart libmetis__InduceRowPartFromColumnPart + +/* minconn.c */ +#define ComputeSubDomainGraph libmetis__ComputeSubDomainGraph +#define UpdateEdgeSubDomainGraph libmetis__UpdateEdgeSubDomainGraph +#define PrintSubDomainGraph libmetis__PrintSubDomainGraph +#define EliminateSubDomainEdges libmetis__EliminateSubDomainEdges +#define MoveGroupMinConnForCut libmetis__MoveGroupMinConnForCut +#define MoveGroupMinConnForVol libmetis__MoveGroupMinConnForVol + +/* mincover.c */ +#define MinCover libmetis__MinCover +#define MinCover_Augment libmetis__MinCover_Augment +#define MinCover_Decompose libmetis__MinCover_Decompose +#define MinCover_ColDFS libmetis__MinCover_ColDFS +#define MinCover_RowDFS libmetis__MinCover_RowDFS + +/* mmd.c */ +#define genmmd libmetis__genmmd +#define mmdelm libmetis__mmdelm +#define mmdint libmetis__mmdint +#define mmdnum libmetis__mmdnum +#define mmdupd libmetis__mmdupd + + +/* ometis.c */ +#define MlevelNestedDissection libmetis__MlevelNestedDissection +#define MlevelNestedDissectionCC libmetis__MlevelNestedDissectionCC +#define MlevelNodeBisectionMultiple libmetis__MlevelNodeBisectionMultiple +#define MlevelNodeBisectionL2 libmetis__MlevelNodeBisectionL2 +#define MlevelNodeBisectionL1 libmetis__MlevelNodeBisectionL1 +#define SplitGraphOrder libmetis__SplitGraphOrder +#define SplitGraphOrderCC libmetis__SplitGraphOrderCC +#define MMDOrder libmetis__MMDOrder + +/* options.c */ +#define SetupCtrl libmetis__SetupCtrl +#define SetupKWayBalMultipliers libmetis__SetupKWayBalMultipliers +#define Setup2WayBalMultipliers libmetis__Setup2WayBalMultipliers +#define PrintCtrl libmetis__PrintCtrl +#define FreeCtrl libmetis__FreeCtrl +#define CheckParams libmetis__CheckParams + +/* parmetis.c */ +#define MlevelNestedDissectionP libmetis__MlevelNestedDissectionP +#define FM_2WayNodeRefine1SidedP libmetis__FM_2WayNodeRefine1SidedP +#define FM_2WayNodeRefine2SidedP libmetis__FM_2WayNodeRefine2SidedP + +/* pmetis.c */ +#define MlevelRecursiveBisection libmetis__MlevelRecursiveBisection +#define MultilevelBisect libmetis__MultilevelBisect +#define SplitGraphPart libmetis__SplitGraphPart + +/* refine.c */ +#define Refine2Way libmetis__Refine2Way +#define Allocate2WayPartitionMemory libmetis__Allocate2WayPartitionMemory +#define Compute2WayPartitionParams libmetis__Compute2WayPartitionParams +#define Project2WayPartition libmetis__Project2WayPartition + +/* separator.c */ +#define ConstructSeparator libmetis__ConstructSeparator +#define ConstructMinCoverSeparator libmetis__ConstructMinCoverSeparator + +/* sfm.c */ +#define FM_2WayNodeRefine2Sided libmetis__FM_2WayNodeRefine2Sided +#define FM_2WayNodeRefine1Sided libmetis__FM_2WayNodeRefine1Sided +#define FM_2WayNodeBalance libmetis__FM_2WayNodeBalance + +/* srefine.c */ +#define Refine2WayNode libmetis__Refine2WayNode +#define Allocate2WayNodePartitionMemory libmetis__Allocate2WayNodePartitionMemory +#define Compute2WayNodePartitionParams libmetis__Compute2WayNodePartitionParams +#define Project2WayNodePartition libmetis__Project2WayNodePartition + +/* stat.c */ +#define ComputePartitionInfoBipartite libmetis__ComputePartitionInfoBipartite +#define ComputePartitionBalance libmetis__ComputePartitionBalance +#define ComputeElementBalance libmetis__ComputeElementBalance + +/* timing.c */ +#define InitTimers libmetis__InitTimers +#define PrintTimers libmetis__PrintTimers + +/* util.c */ +#define iargmax_strd libmetis__iargmax_strd +#define iargmax_nrm libmetis__iargmax_nrm +#define iargmax2_nrm libmetis__iargmax2_nrm +#define rargmax2 libmetis__rargmax2 +#define InitRandom libmetis__InitRandom +#define metis_rcode libmetis__metis_rcode + +/* wspace.c */ +#define AllocateWorkSpace libmetis__AllocateWorkSpace +#define AllocateRefinementWorkSpace libmetis__AllocateRefinementWorkSpace +#define FreeWorkSpace libmetis__FreeWorkSpace +#define wspacemalloc libmetis__wspacemalloc +#define wspacepush libmetis__wspacepush +#define wspacepop libmetis__wspacepop +#define iwspacemalloc libmetis__iwspacemalloc +#define rwspacemalloc libmetis__rwspacemalloc +#define ikvwspacemalloc libmetis__ikvwspacemalloc +#define cnbrpoolReset libmetis__cnbrpoolReset +#define cnbrpoolGetNext libmetis__cnbrpoolGetNext +#define vnbrpoolReset libmetis__vnbrpoolReset +#define vnbrpoolGetNext libmetis__vnbrpoolGetNext + +#endif + + diff --git a/src/metis/libmetis/separator.c b/src/metis/libmetis/separator.c new file mode 100644 index 0000000..72dae9b --- /dev/null +++ b/src/metis/libmetis/separator.c @@ -0,0 +1,176 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * separator.c + * + * This file contains code for separator extraction + * + * Started 8/1/97 + * George + * + * $Id: separator.c 10481 2011-07-05 18:01:23Z karypis $ + * + */ + +#include "metislib.h" + +/************************************************************************* +* This function takes a bisection and constructs a minimum weight vertex +* separator out of it. It uses the node-based separator refinement for it. +**************************************************************************/ +void ConstructSeparator(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, j, k, nvtxs, nbnd; + idx_t *xadj, *where, *bndind; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + nbnd = graph->nbnd; + bndind = graph->bndind; + + where = icopy(nvtxs, graph->where, iwspacemalloc(ctrl, nvtxs)); + + /* Put the nodes in the boundary into the separator */ + for (i=0; i 0) /* Ignore islands */ + where[j] = 2; + } + + FreeRData(graph); + + Allocate2WayNodePartitionMemory(ctrl, graph); + icopy(nvtxs, where, graph->where); + + WCOREPOP; + + ASSERT(IsSeparable(graph)); + + Compute2WayNodePartitionParams(ctrl, graph); + + ASSERT(CheckNodePartitionParams(graph)); + + FM_2WayNodeRefine2Sided(ctrl, graph, 1); + FM_2WayNodeRefine1Sided(ctrl, graph, 4); + + ASSERT(IsSeparable(graph)); + +} + + + +/************************************************************************* +* This function takes a bisection and constructs a minimum weight vertex +* separator out of it. It uses an unweighted minimum-cover algorithm +* followed by node-based separator refinement. +**************************************************************************/ +void ConstructMinCoverSeparator(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, ii, j, jj, k, l, nvtxs, nbnd, bnvtxs[3], bnedges[2], csize; + idx_t *xadj, *adjncy, *bxadj, *badjncy; + idx_t *where, *bndind, *bndptr, *vmap, *ivmap, *cover; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + nbnd = graph->nbnd; + bndind = graph->bndind; + bndptr = graph->bndptr; + where = graph->where; + + vmap = iwspacemalloc(ctrl, nvtxs); + ivmap = iwspacemalloc(ctrl, nbnd); + cover = iwspacemalloc(ctrl, nbnd); + + if (nbnd > 0) { + /* Go through the boundary and determine the sizes of the bipartite graph */ + bnvtxs[0] = bnvtxs[1] = bnedges[0] = bnedges[1] = 0; + for (i=0; i 0) { + bnvtxs[k]++; + bnedges[k] += xadj[j+1]-xadj[j]; + } + } + + bnvtxs[2] = bnvtxs[0]+bnvtxs[1]; + bnvtxs[1] = bnvtxs[0]; + bnvtxs[0] = 0; + + bxadj = iwspacemalloc(ctrl, bnvtxs[2]+1); + badjncy = iwspacemalloc(ctrl, bnedges[0]+bnedges[1]+1); + + /* Construct the ivmap and vmap */ + ASSERT(iset(nvtxs, -1, vmap) == vmap); + for (i=0; i 0) { + vmap[j] = bnvtxs[k]; + ivmap[bnvtxs[k]++] = j; + } + } + + /* OK, go through and put the vertices of each part starting from 0 */ + bnvtxs[1] = bnvtxs[0]; + bnvtxs[0] = 0; + bxadj[0] = l = 0; + for (k=0; k<2; k++) { + for (ii=0; iibndptr[jj])); + badjncy[l++] = vmap[jj]; + } + } + bxadj[++bnvtxs[k]] = l; + } + } + } + + ASSERT(l <= bnedges[0]+bnedges[1]); + + MinCover(bxadj, badjncy, bnvtxs[0], bnvtxs[1], cover, &csize); + + IFSET(ctrl->dbglvl, METIS_DBG_SEPINFO, + printf("Nvtxs: %6"PRIDX", [%5"PRIDX" %5"PRIDX"], Cut: %6"PRIDX", SS: [%6"PRIDX" %6"PRIDX"], Cover: %6"PRIDX"\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, bnvtxs[0], bnvtxs[1]-bnvtxs[0], csize)); + + for (i=0; idbglvl, METIS_DBG_SEPINFO, + printf("Nvtxs: %6"PRIDX", [%5"PRIDX" %5"PRIDX"], Cut: %6"PRIDX", SS: [%6"PRIDX" %6"PRIDX"], Cover: %6"PRIDX"\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, (idx_t)0, (idx_t)0, (idx_t)0)); + } + + /* Prepare to refine the vertex separator */ + icopy(nvtxs, graph->where, vmap); + + FreeRData(graph); + + Allocate2WayNodePartitionMemory(ctrl, graph); + icopy(nvtxs, vmap, graph->where); + + WCOREPOP; + + Compute2WayNodePartitionParams(ctrl, graph); + + ASSERT(CheckNodePartitionParams(graph)); + + FM_2WayNodeRefine1Sided(ctrl, graph, ctrl->niter); + + ASSERT(IsSeparable(graph)); +} + diff --git a/src/metis/libmetis/sfm.c b/src/metis/libmetis/sfm.c new file mode 100644 index 0000000..d418173 --- /dev/null +++ b/src/metis/libmetis/sfm.c @@ -0,0 +1,612 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * sfm.c + * + * This file contains code that implementes an FM-based separator refinement + * + * Started 8/1/97 + * George + * + * $Id: sfm.c 10874 2011-10-17 23:13:00Z karypis $ + * + */ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function performs a node-based FM refinement */ +/**************************************************************************/ +void FM_2WayNodeRefine2Sided(ctrl_t *ctrl, graph_t *graph, idx_t niter) +{ + idx_t i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind; + idx_t *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; + idx_t *mptr, *mind, *moved, *swaps; + rpq_t *queues[2]; + nrinfo_t *rinfo; + idx_t higain, oldgain, mincut, initcut, mincutorder; + idx_t pass, to, other, limit; + idx_t badmaxpwgt, mindiff, newdiff; + idx_t u[2], g[2]; + real_t mult; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + + bndind = graph->bndind; + bndptr = graph->bndptr; + where = graph->where; + pwgts = graph->pwgts; + rinfo = graph->nrinfo; + + queues[0] = rpqCreate(nvtxs); + queues[1] = rpqCreate(nvtxs); + + moved = iwspacemalloc(ctrl, nvtxs); + swaps = iwspacemalloc(ctrl, nvtxs); + mptr = iwspacemalloc(ctrl, nvtxs+1); + mind = iwspacemalloc(ctrl, 2*nvtxs); + + mult = 0.5*ctrl->ubfactors[0]; + badmaxpwgt = (idx_t)(mult*(pwgts[0]+pwgts[1]+pwgts[2])); + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("Partitions-N2: [%6"PRIDX" %6"PRIDX"] Nv-Nb[%6"PRIDX" %6"PRIDX"]. ISep: %6"PRIDX"\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); + + for (pass=0; passmincut; + nbnd = graph->nbnd; + + /* use the swaps array in place of the traditional perm array to save memory */ + irandArrayPermute(nbnd, swaps, nbnd, 1); + for (ii=0; iicompress ? gk_min(5*nbnd, 400) : gk_min(2*nbnd, 300)); + + /****************************************************** + * Get into the FM loop + *******************************************************/ + mptr[0] = nmind = 0; + mindiff = iabs(pwgts[0]-pwgts[1]); + to = (pwgts[0] < pwgts[1] ? 0 : 1); + for (nswaps=0; nswaps g[1] ? 0 : (g[0] < g[1] ? 1 : pass%2)); + + if (pwgts[to]+vwgt[u[to]] > badmaxpwgt) + to = (to+1)%2; + } + else if (u[0] == -1 && u[1] == -1) { + break; + } + else if (u[0] != -1 && pwgts[0]+vwgt[u[0]] <= badmaxpwgt) { + to = 0; + } + else if (u[1] != -1 && pwgts[1]+vwgt[u[1]] <= badmaxpwgt) { + to = 1; + } + else + break; + + other = (to+1)%2; + + higain = rpqGetTop(queues[to]); + if (moved[higain] == -1) /* Delete if it was in the separator originally */ + rpqDelete(queues[other], higain); + + ASSERT(bndptr[higain] != -1); + + /* The following check is to ensure we break out if there is a posibility + of over-running the mind array. */ + if (nmind + xadj[higain+1]-xadj[higain] >= 2*nvtxs-1) + break; + + pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]); + + newdiff = iabs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other])); + if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) { + mincut = pwgts[2]; + mincutorder = nswaps; + mindiff = newdiff; + } + else { + if (nswaps - mincutorder > 2*limit || + (nswaps - mincutorder > limit && pwgts[2] > 1.10*mincut)) { + pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]); + break; /* No further improvement, break out */ + } + } + + BNDDelete(nbnd, bndind, bndptr, higain); + pwgts[to] += vwgt[higain]; + where[higain] = to; + moved[higain] = nswaps; + swaps[nswaps] = higain; + + + /********************************************************** + * Update the degrees of the affected nodes + ***********************************************************/ + for (j=xadj[higain]; jdbglvl, METIS_DBG_MOVEINFO, + printf("Moved %6"PRIDX" to %3"PRIDX", Gain: %5"PRIDX" [%5"PRIDX"] [%4"PRIDX" %4"PRIDX"] \t[%5"PRIDX" %5"PRIDX" %5"PRIDX"]\n", higain, to, g[to], g[other], vwgt[u[to]], vwgt[u[other]], pwgts[0], pwgts[1], pwgts[2])); + + } + + + /**************************************************************** + * Roll back computation + *****************************************************************/ + for (nswaps--; nswaps>mincutorder; nswaps--) { + higain = swaps[nswaps]; + + ASSERT(CheckNodePartitionParams(graph)); + + to = where[higain]; + other = (to+1)%2; + INC_DEC(pwgts[2], pwgts[to], vwgt[higain]); + where[higain] = 2; + BNDInsert(nbnd, bndind, bndptr, higain); + + edegrees = rinfo[higain].edegrees; + edegrees[0] = edegrees[1] = 0; + for (j=xadj[higain]; jdbglvl, METIS_DBG_REFINE, + printf("\tMinimum sep: %6"PRIDX" at %5"PRIDX", PWGTS: [%6"PRIDX" %6"PRIDX"], NBND: %6"PRIDX"\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd)); + + graph->mincut = mincut; + graph->nbnd = nbnd; + + if (mincutorder == -1 || mincut >= initcut) + break; + } + + rpqDestroy(queues[0]); + rpqDestroy(queues[1]); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function performs a node-based FM refinement. + Each refinement iteration is split into two sub-iterations. + In each sub-iteration only moves to one of the left/right partitions + is allowed; hence, it is one-sided. +*/ +/**************************************************************************/ +void FM_2WayNodeRefine1Sided(ctrl_t *ctrl, graph_t *graph, idx_t niter) +{ + idx_t i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind, iend; + idx_t *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; + idx_t *mptr, *mind, *swaps; + rpq_t *queue; + nrinfo_t *rinfo; + idx_t higain, mincut, initcut, mincutorder; + idx_t pass, to, other, limit; + idx_t badmaxpwgt, mindiff, newdiff; + real_t mult; + + WCOREPUSH; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + + bndind = graph->bndind; + bndptr = graph->bndptr; + where = graph->where; + pwgts = graph->pwgts; + rinfo = graph->nrinfo; + + queue = rpqCreate(nvtxs); + + swaps = iwspacemalloc(ctrl, nvtxs); + mptr = iwspacemalloc(ctrl, nvtxs+1); + mind = iwspacemalloc(ctrl, 2*nvtxs); + + mult = 0.5*ctrl->ubfactors[0]; + badmaxpwgt = (idx_t)(mult*(pwgts[0]+pwgts[1]+pwgts[2])); + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("Partitions-N1: [%6"PRIDX" %6"PRIDX"] Nv-Nb[%6"PRIDX" %6"PRIDX"]. ISep: %6"PRIDX"\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); + + to = (pwgts[0] < pwgts[1] ? 1 : 0); + for (pass=0; pass<2*niter; pass++) { /* the 2*niter is for the two sides */ + other = to; + to = (to+1)%2; + + rpqReset(queue); + + mincutorder = -1; + initcut = mincut = graph->mincut; + nbnd = graph->nbnd; + + /* use the swaps array in place of the traditional perm array to save memory */ + irandArrayPermute(nbnd, swaps, nbnd, 1); + for (ii=0; iicompress ? gk_min(5*nbnd, 500) : gk_min(3*nbnd, 300)); + + /****************************************************** + * Get into the FM loop + *******************************************************/ + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->Aux3Tmr)); + mptr[0] = nmind = 0; + mindiff = iabs(pwgts[0]-pwgts[1]); + for (nswaps=0; nswaps= 2*nvtxs-1) + break; + + if (pwgts[to]+vwgt[higain] > badmaxpwgt) + break; /* No point going any further. Balance will be bad */ + + pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]); + + newdiff = iabs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other])); + if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) { + mincut = pwgts[2]; + mincutorder = nswaps; + mindiff = newdiff; + } + else { + if (nswaps - mincutorder > 3*limit || + (nswaps - mincutorder > limit && pwgts[2] > 1.10*mincut)) { + pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]); + break; /* No further improvement, break out */ + } + } + + BNDDelete(nbnd, bndind, bndptr, higain); + pwgts[to] += vwgt[higain]; + where[higain] = to; + swaps[nswaps] = higain; + + + /********************************************************** + * Update the degrees of the affected nodes + ***********************************************************/ + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->Aux1Tmr)); + for (j=xadj[higain]; jdbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->Aux1Tmr)); + + + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, + printf("Moved %6"PRIDX" to %3"PRIDX", Gain: %5"PRIDX" [%5"PRIDX"] \t[%5"PRIDX" %5"PRIDX" %5"PRIDX"] [%3"PRIDX" %2"PRIDX"]\n", + higain, to, (vwgt[higain]-rinfo[higain].edegrees[other]), vwgt[higain], + pwgts[0], pwgts[1], pwgts[2], nswaps, limit)); + } + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->Aux3Tmr)); + + + /**************************************************************** + * Roll back computation + *****************************************************************/ + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->Aux2Tmr)); + for (nswaps--; nswaps>mincutorder; nswaps--) { + higain = swaps[nswaps]; + + ASSERT(CheckNodePartitionParams(graph)); + ASSERT(where[higain] == to); + + INC_DEC(pwgts[2], pwgts[to], vwgt[higain]); + where[higain] = 2; + BNDInsert(nbnd, bndind, bndptr, higain); + + edegrees = rinfo[higain].edegrees; + edegrees[0] = edegrees[1] = 0; + for (j=xadj[higain]; jdbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->Aux2Tmr)); + + ASSERT(mincut == pwgts[2]); + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("\tMinimum sep: %6"PRIDX" at %5"PRIDX", PWGTS: [%6"PRIDX" %6"PRIDX"], NBND: %6"PRIDX"\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd)); + + graph->mincut = mincut; + graph->nbnd = nbnd; + + if (pass%2 == 1 && (mincutorder == -1 || mincut >= initcut)) + break; + } + + rpqDestroy(queue); + + WCOREPOP; +} + + +/*************************************************************************/ +/*! This function balances the left/right partitions of a separator + tri-section */ +/*************************************************************************/ +void FM_2WayNodeBalance(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, gain; + idx_t badmaxpwgt, higain, oldgain, pass, to, other; + idx_t *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; + idx_t *perm, *moved; + rpq_t *queue; + nrinfo_t *rinfo; + real_t mult; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + + bndind = graph->bndind; + bndptr = graph->bndptr; + where = graph->where; + pwgts = graph->pwgts; + rinfo = graph->nrinfo; + + mult = 0.5*ctrl->ubfactors[0]; + + badmaxpwgt = (idx_t)(mult*(pwgts[0]+pwgts[1])); + if (gk_max(pwgts[0], pwgts[1]) < badmaxpwgt) + return; + if (iabs(pwgts[0]-pwgts[1]) < 3*graph->tvwgt[0]/nvtxs) + return; + + WCOREPUSH; + + to = (pwgts[0] < pwgts[1] ? 0 : 1); + other = (to+1)%2; + + queue = rpqCreate(nvtxs); + + perm = iwspacemalloc(ctrl, nvtxs); + moved = iset(nvtxs, -1, iwspacemalloc(ctrl, nvtxs)); + + IFSET(ctrl->dbglvl, METIS_DBG_REFINE, + printf("Partitions: [%6"PRIDX" %6"PRIDX"] Nv-Nb[%6"PRIDX" %6"PRIDX"]. ISep: %6"PRIDX" [B]\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); + + nbnd = graph->nbnd; + irandArrayPermute(nbnd, perm, nbnd, 1); + for (ii=0; ii pwgts[other]) + break; + + /* break if balance is achieved and no +ve or zero gain */ + if (gain < 0 && pwgts[other] < badmaxpwgt) + break; + + /* skip this vertex if it will violate balance on the other side */ + if (pwgts[to]+vwgt[higain] > badmaxpwgt) + continue; + + ASSERT(bndptr[higain] != -1); + + pwgts[2] -= gain; + + BNDDelete(nbnd, bndind, bndptr, higain); + pwgts[to] += vwgt[higain]; + where[higain] = to; + + IFSET(ctrl->dbglvl, METIS_DBG_MOVEINFO, + printf("Moved %6"PRIDX" to %3"PRIDX", Gain: %3"PRIDX", \t[%5"PRIDX" %5"PRIDX" %5"PRIDX"]\n", higain, to, vwgt[higain]-rinfo[higain].edegrees[other], pwgts[0], pwgts[1], pwgts[2])); + + + /********************************************************** + * Update the degrees of the affected nodes + ***********************************************************/ + for (j=xadj[higain]; jdbglvl, METIS_DBG_REFINE, + printf("\tBalanced sep: %6"PRIDX" at %4"PRIDX", PWGTS: [%6"PRIDX" %6"PRIDX"], NBND: %6"PRIDX"\n", pwgts[2], nswaps, pwgts[0], pwgts[1], nbnd)); + + graph->mincut = pwgts[2]; + graph->nbnd = nbnd; + + rpqDestroy(queue); + + WCOREPOP; +} + diff --git a/src/metis/libmetis/srefine.c b/src/metis/libmetis/srefine.c new file mode 100644 index 0000000..603f782 --- /dev/null +++ b/src/metis/libmetis/srefine.c @@ -0,0 +1,163 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * srefine.c + * + * This file contains code for the separator refinement algortihms + * + * Started 8/1/97 + * George + * + * $Id: srefine.c 10515 2011-07-08 15:46:18Z karypis $ + * + */ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function is the entry point of the separator refinement. + It does not perform any refinement on graph, but it starts by first + projecting it to the next level finer graph and proceeds from there. */ +/*************************************************************************/ +void Refine2WayNode(ctrl_t *ctrl, graph_t *orggraph, graph_t *graph) +{ + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); + + if (graph == orggraph) { + Compute2WayNodePartitionParams(ctrl, graph); + } + else { + do { + graph = graph->finer; + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); + Project2WayNodePartition(ctrl, graph); + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_startcputimer(ctrl->RefTmr)); + FM_2WayNodeBalance(ctrl, graph); + + ASSERT(CheckNodePartitionParams(graph)); + + switch (ctrl->rtype) { + case METIS_RTYPE_SEP2SIDED: + FM_2WayNodeRefine2Sided(ctrl, graph, ctrl->niter); + break; + case METIS_RTYPE_SEP1SIDED: + FM_2WayNodeRefine1Sided(ctrl, graph, ctrl->niter); + break; + default: + gk_errexit(SIGERR, "Unknown rtype of %d\n", ctrl->rtype); + } + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); + + } while (graph != orggraph); + } + + IFSET(ctrl->dbglvl, METIS_DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); +} + + +/*************************************************************************/ +/*! This function allocates memory for 2-way node-based refinement */ +/**************************************************************************/ +void Allocate2WayNodePartitionMemory(ctrl_t *ctrl, graph_t *graph) +{ + idx_t nvtxs; + + nvtxs = graph->nvtxs; + + graph->pwgts = imalloc(3, "Allocate2WayNodePartitionMemory: pwgts"); + graph->where = imalloc(nvtxs, "Allocate2WayNodePartitionMemory: where"); + graph->bndptr = imalloc(nvtxs, "Allocate2WayNodePartitionMemory: bndptr"); + graph->bndind = imalloc(nvtxs, "Allocate2WayNodePartitionMemory: bndind"); + graph->nrinfo = (nrinfo_t *)gk_malloc(nvtxs*sizeof(nrinfo_t), "Allocate2WayNodePartitionMemory: nrinfo"); +} + + +/*************************************************************************/ +/*! This function computes the edegrees[] to the left & right sides */ +/*************************************************************************/ +void Compute2WayNodePartitionParams(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, j, nvtxs, nbnd; + idx_t *xadj, *adjncy, *vwgt; + idx_t *where, *pwgts, *bndind, *bndptr, *edegrees; + nrinfo_t *rinfo; + idx_t me, other; + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + vwgt = graph->vwgt; + adjncy = graph->adjncy; + + where = graph->where; + rinfo = graph->nrinfo; + pwgts = iset(3, 0, graph->pwgts); + bndind = graph->bndind; + bndptr = iset(nvtxs, -1, graph->bndptr); + + + /*------------------------------------------------------------ + / Compute now the separator external degrees + /------------------------------------------------------------*/ + nbnd = 0; + for (i=0; i=0 && me <= 2); + + if (me == 2) { /* If it is on the separator do some computations */ + BNDInsert(nbnd, bndind, bndptr, i); + + edegrees = rinfo[i].edegrees; + edegrees[0] = edegrees[1] = 0; + + for (j=xadj[i]; jmincut = pwgts[2]; + graph->nbnd = nbnd; +} + + +/*************************************************************************/ +/*! This function projects the node-based bisection */ +/*************************************************************************/ +void Project2WayNodePartition(ctrl_t *ctrl, graph_t *graph) +{ + idx_t i, j, nvtxs; + idx_t *cmap, *where, *cwhere; + graph_t *cgraph; + + cgraph = graph->coarser; + cwhere = cgraph->where; + + nvtxs = graph->nvtxs; + cmap = graph->cmap; + + Allocate2WayNodePartitionMemory(ctrl, graph); + where = graph->where; + + /* Project the partition */ + for (i=0; i= 0 && where[i] <= 2, ("%"PRIDX" %"PRIDX" %"PRIDX" %"PRIDX"\n", + i, cmap[i], where[i], cwhere[cmap[i]])); + } + + FreeGraph(&graph->coarser); + graph->coarser = NULL; + + Compute2WayNodePartitionParams(ctrl, graph); +} diff --git a/src/metis/libmetis/stat.c b/src/metis/libmetis/stat.c new file mode 100644 index 0000000..f19791b --- /dev/null +++ b/src/metis/libmetis/stat.c @@ -0,0 +1,179 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * stat.c + * + * This file computes various statistics + * + * Started 7/25/97 + * George + * + * $Id: stat.c 9942 2011-05-17 22:09:52Z karypis $ + * + */ + +#include "metislib.h" + + +/************************************************************************* +* This function computes cuts and balance information +**************************************************************************/ +void ComputePartitionInfoBipartite(graph_t *graph, idx_t nparts, idx_t *where) +{ + idx_t i, j, k, nvtxs, ncon, mustfree=0; + idx_t *xadj, *adjncy, *vwgt, *vsize, *adjwgt, *kpwgts, *tmpptr; + idx_t *padjncy, *padjwgt, *padjcut; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + vsize = graph->vsize; + adjwgt = graph->adjwgt; + + if (vwgt == NULL) { + vwgt = graph->vwgt = ismalloc(nvtxs, 1, "vwgt"); + mustfree = 1; + } + if (adjwgt == NULL) { + adjwgt = graph->adjwgt = ismalloc(xadj[nvtxs], 1, "adjwgt"); + mustfree += 2; + } + + printf("%"PRIDX"-way Cut: %5"PRIDX", Vol: %5"PRIDX", ", nparts, ComputeCut(graph, where), ComputeVolume(graph, where)); + + /* Compute balance information */ + kpwgts = ismalloc(ncon*nparts, 0, "ComputePartitionInfo: kpwgts"); + + for (i=0; ivwgt = NULL; + } + if (mustfree == 2 || mustfree == 3) { + gk_free((void **)&adjwgt, LTERM); + graph->adjwgt = NULL; + } + + gk_free((void **)&kpwgts, &padjncy, &padjwgt, &padjcut, LTERM); +} + + +/************************************************************************* +* This function computes the balance of the partitioning +**************************************************************************/ +void ComputePartitionBalance(graph_t *graph, idx_t nparts, idx_t *where, real_t *ubvec) +{ + idx_t i, j, nvtxs, ncon; + idx_t *kpwgts, *vwgt; + real_t balance; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + vwgt = graph->vwgt; + + kpwgts = ismalloc(nparts, 0, "ComputePartitionInfo: kpwgts"); + + if (vwgt == NULL) { + for (i=0; invtxs; i++) + kpwgts[where[i]] += vwgt[i*ncon+j]; + + ubvec[j] = 1.0*nparts*kpwgts[iargmax(nparts, kpwgts)]/(1.0*isum(nparts, kpwgts, 1)); + } + } + + gk_free((void **)&kpwgts, LTERM); + +} + + +/************************************************************************* +* This function computes the balance of the element partitioning +**************************************************************************/ +real_t ComputeElementBalance(idx_t ne, idx_t nparts, idx_t *where) +{ + idx_t i; + idx_t *kpwgts; + real_t balance; + + kpwgts = ismalloc(nparts, 0, "ComputeElementBalance: kpwgts"); + + for (i=0; i #ifdef __STDC__ @@ -24,3 +26,4 @@ #include #include +#endif diff --git a/src/metis/libmetis/struct.h b/src/metis/libmetis/struct.h new file mode 100644 index 0000000..5fc8588 --- /dev/null +++ b/src/metis/libmetis/struct.h @@ -0,0 +1,206 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * struct.h + * + * This file contains data structures for ILU routines. + * + * Started 9/26/95 + * George + * + * $Id: struct.h 13900 2013-03-24 15:27:07Z karypis $ + */ + +#ifndef _LIBMETIS_STRUCT_H_ +#define _LIBMETIS_STRUCT_H_ + + + +/*************************************************************************/ +/*! This data structure stores cut-based k-way refinement info about an + adjacent subdomain for a given vertex. */ +/*************************************************************************/ +typedef struct cnbr_t { + idx_t pid; /*!< The partition ID */ + idx_t ed; /*!< The sum of the weights of the adjacent edges + that are incident on pid */ +} cnbr_t; + + +/*************************************************************************/ +/*! The following data structure stores holds information on degrees for k-way + partition */ +/*************************************************************************/ +typedef struct ckrinfo_t { + idx_t id; /*!< The internal degree of a vertex (sum of weights) */ + idx_t ed; /*!< The total external degree of a vertex */ + idx_t nnbrs; /*!< The number of neighboring subdomains */ + idx_t inbr; /*!< The index in the cnbr_t array where the nnbrs list + of neighbors is stored */ +} ckrinfo_t; + + +/*************************************************************************/ +/*! This data structure stores volume-based k-way refinement info about an + adjacent subdomain for a given vertex. */ +/*************************************************************************/ +typedef struct vnbr_t { + idx_t pid; /*!< The partition ID */ + idx_t ned; /*!< The number of the adjacent edges + that are incident on pid */ + idx_t gv; /*!< The gain in volume achieved by moving the + vertex to pid */ +} vnbr_t; + + +/*************************************************************************/ +/*! The following data structure holds information on degrees for k-way + vol-based partition */ +/*************************************************************************/ +typedef struct vkrinfo_t { + idx_t nid; /*!< The internal degree of a vertex (count of edges) */ + idx_t ned; /*!< The total external degree of a vertex (count of edges) */ + idx_t gv; /*!< The volume gain of moving that vertex */ + idx_t nnbrs; /*!< The number of neighboring subdomains */ + idx_t inbr; /*!< The index in the vnbr_t array where the nnbrs list + of neighbors is stored */ +} vkrinfo_t; + + +/*************************************************************************/ +/*! The following data structure holds information on degrees for k-way + partition */ +/*************************************************************************/ +typedef struct nrinfo_t { + idx_t edegrees[2]; +} nrinfo_t; + + +/*************************************************************************/ +/*! This data structure holds a graph */ +/*************************************************************************/ +typedef struct graph_t { + idx_t nvtxs, nedges; /* The # of vertices and edges in the graph */ + idx_t ncon; /* The # of constrains */ + idx_t *xadj; /* Pointers to the locally stored vertices */ + idx_t *vwgt; /* Vertex weights */ + idx_t *vsize; /* Vertex sizes for min-volume formulation */ + idx_t *adjncy; /* Array that stores the adjacency lists of nvtxs */ + idx_t *adjwgt; /* Array that stores the weights of the adjacency lists */ + + idx_t *tvwgt; /* The sum of the vertex weights in the graph */ + real_t *invtvwgt; /* The inverse of the sum of the vertex weights in the graph */ + + + /* These are to keep track control if the corresponding fields correspond to + application or library memory */ + int free_xadj, free_vwgt, free_vsize, free_adjncy, free_adjwgt; + + idx_t *label; + + idx_t *cmap; + + /* Partition parameters */ + idx_t mincut, minvol; + idx_t *where, *pwgts; + idx_t nbnd; + idx_t *bndptr, *bndind; + + /* Bisection refinement parameters */ + idx_t *id, *ed; + + /* K-way refinement parameters */ + ckrinfo_t *ckrinfo; /*!< The per-vertex cut-based refinement info */ + vkrinfo_t *vkrinfo; /*!< The per-vertex volume-based refinement info */ + + /* Node refinement information */ + nrinfo_t *nrinfo; + + struct graph_t *coarser, *finer; +} graph_t; + + +/*************************************************************************/ +/*! This data structure holds a mesh */ +/*************************************************************************/ +typedef struct mesh_t { + idx_t ne, nn; /*!< The # of elements and nodes in the mesh */ + idx_t ncon; /*!< The number of element balancing constraints (element weights) */ + + idx_t *eptr, *eind; /*!< The CSR-structure storing the nodes in the elements */ + idx_t *ewgt; /*!< The weights of the elements */ +} mesh_t; + + + +/*************************************************************************/ +/*! The following structure stores information used by Metis */ +/*************************************************************************/ +typedef struct ctrl_t { + moptype_et optype; /* Type of operation */ + mobjtype_et objtype; /* Type of refinement objective */ + mdbglvl_et dbglvl; /* Controls the debuging output of the program */ + mctype_et ctype; /* The type of coarsening */ + miptype_et iptype; /* The type of initial partitioning */ + mrtype_et rtype; /* The type of refinement */ + + idx_t CoarsenTo; /* The # of vertices in the coarsest graph */ + idx_t nIparts; /* The number of initial partitions to compute */ + idx_t no2hop; /* Indicates if 2-hop matching will be used */ + idx_t minconn; /* Indicates if the subdomain connectivity will be minimized */ + idx_t contig; /* Indicates if contigous partitions are required */ + idx_t nseps; /* The number of separators to be found during multiple bisections */ + idx_t ufactor; /* The user-supplied load imbalance factor */ + idx_t compress; /* If the graph will be compressed prior to ordering */ + idx_t ccorder; /* If connected components will be ordered separately */ + idx_t seed; /* The seed for the random number generator */ + idx_t ncuts; /* The number of different partitionings to compute */ + idx_t niter; /* The number of iterations during each refinement */ + idx_t numflag; /* The user-supplied numflag for the graph */ + idx_t *maxvwgt; /* The maximum allowed weight for a vertex */ + + idx_t ncon; /*!< The number of balancing constraints */ + idx_t nparts; /*!< The number of partitions */ + + real_t pfactor; /* .1*(user-supplied prunning factor) */ + + real_t *ubfactors; /*!< The per-constraint ubfactors */ + + real_t *tpwgts; /*!< The target partition weights */ + real_t *pijbm; /*!< The nparts*ncon multiplies for the ith partition + and jth constraint for obtaining the balance */ + + real_t cfactor; /*!< The achieved compression factor */ + + /* Various Timers */ + double TotalTmr, InitPartTmr, MatchTmr, ContractTmr, CoarsenTmr, UncoarsenTmr, + RefTmr, ProjectTmr, SplitTmr, Aux1Tmr, Aux2Tmr, Aux3Tmr; + + /* Workspace information */ + gk_mcore_t *mcore; /*!< The persistent memory core for within function + mallocs/frees */ + + /* These are for use by the k-way refinement routines */ + size_t nbrpoolsize; /*!< The number of {c,v}nbr_t entries that have been allocated */ + size_t nbrpoolcpos; /*!< The position of the first free entry in the array */ + size_t nbrpoolreallocs; /*!< The number of times the pool was resized */ + + cnbr_t *cnbrpool; /*!< The pool of cnbr_t entries to be used during refinement. + The size and current position of the pool is controlled + by nnbrs & cnbrs */ + vnbr_t *vnbrpool; /*!< The pool of vnbr_t entries to be used during refinement. + The size and current position of the pool is controlled + by nnbrs & cnbrs */ + + /* The subdomain graph, in sparse format */ + idx_t *maxnads; /* The maximum allocated number of adjacent domains */ + idx_t *nads; /* The number of adjacent domains */ + idx_t **adids; /* The IDs of the adjacent domains */ + idx_t **adwgts; /* The edge-weight to the adjacent domains */ + idx_t *pvec1, *pvec2; /* Auxiliar nparts-size vectors for efficiency */ + +} ctrl_t; + + + +#endif diff --git a/src/metis/libmetis/timing.c b/src/metis/libmetis/timing.c new file mode 100644 index 0000000..9d6e05c --- /dev/null +++ b/src/metis/libmetis/timing.c @@ -0,0 +1,63 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * timing.c + * + * This file contains routines that deal with timing Metis + * + * Started 7/24/97 + * George + * + * $Id: timing.c 13936 2013-03-30 03:59:09Z karypis $ + * + */ + +#include "metislib.h" + + +/************************************************************************* +* This function clears the timers +**************************************************************************/ +void InitTimers(ctrl_t *ctrl) +{ + gk_clearcputimer(ctrl->TotalTmr); + gk_clearcputimer(ctrl->InitPartTmr); + gk_clearcputimer(ctrl->MatchTmr); + gk_clearcputimer(ctrl->ContractTmr); + gk_clearcputimer(ctrl->CoarsenTmr); + gk_clearcputimer(ctrl->UncoarsenTmr); + gk_clearcputimer(ctrl->RefTmr); + gk_clearcputimer(ctrl->ProjectTmr); + gk_clearcputimer(ctrl->SplitTmr); + gk_clearcputimer(ctrl->Aux1Tmr); + gk_clearcputimer(ctrl->Aux2Tmr); + gk_clearcputimer(ctrl->Aux3Tmr); +} + + + +/************************************************************************* +* This function prints the various timers +**************************************************************************/ +void PrintTimers(ctrl_t *ctrl) +{ + printf("\nTiming Information -------------------------------------------------"); + printf("\n Multilevel: \t\t %7.3"PRREAL"", gk_getcputimer(ctrl->TotalTmr)); + printf("\n Coarsening: \t\t %7.3"PRREAL"", gk_getcputimer(ctrl->CoarsenTmr)); + printf("\n Matching: \t\t\t %7.3"PRREAL"", gk_getcputimer(ctrl->MatchTmr)); + printf("\n Contract: \t\t\t %7.3"PRREAL"", gk_getcputimer(ctrl->ContractTmr)); + printf("\n Initial Partition: \t %7.3"PRREAL"", gk_getcputimer(ctrl->InitPartTmr)); + printf("\n Uncoarsening: \t\t %7.3"PRREAL"", gk_getcputimer(ctrl->UncoarsenTmr)); + printf("\n Refinement: \t\t\t %7.3"PRREAL"", gk_getcputimer(ctrl->RefTmr)); + printf("\n Projection: \t\t\t %7.3"PRREAL"", gk_getcputimer(ctrl->ProjectTmr)); + printf("\n Splitting: \t\t %7.3"PRREAL"", gk_getcputimer(ctrl->SplitTmr)); +/* + printf("\n Aux1Tmr: \t\t %7.3"PRREAL"", gk_getcputimer(ctrl->Aux1Tmr)); + printf("\n Aux2Tmr: \t\t %7.3"PRREAL"", gk_getcputimer(ctrl->Aux2Tmr)); + printf("\n Aux3Tmr: \t\t %7.3"PRREAL"", gk_getcputimer(ctrl->Aux3Tmr)); +*/ + printf("\n********************************************************************\n"); +} + + + diff --git a/src/metis/libmetis/util.c b/src/metis/libmetis/util.c new file mode 100644 index 0000000..7fbc467 --- /dev/null +++ b/src/metis/libmetis/util.c @@ -0,0 +1,138 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * util.c + * + * This function contains various utility routines + * + * Started 9/28/95 + * George + * + * $Id: util.c 10495 2011-07-06 16:04:45Z karypis $ + */ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function initializes the random number generator + */ +/*************************************************************************/ +void InitRandom(idx_t seed) +{ + isrand((seed == -1 ? 4321 : seed)); +} + + +/*************************************************************************/ +/*! Returns the highest weight index of x[i]*y[i] + */ +/*************************************************************************/ +idx_t iargmax_nrm(size_t n, idx_t *x, real_t *y) +{ + idx_t i, max=0; + + for (i=1; i x[max]*y[max] ? i : max); + + return max; +} + + +/*************************************************************************/ +/*! These functions return the index of the maximum element in a vector + */ +/*************************************************************************/ +idx_t iargmax_strd(size_t n, idx_t *x, idx_t incx) +{ + size_t i, max=0; + + n *= incx; + for (i=incx; i x[max] ? i : max); + + return max/incx; +} + + +/*************************************************************************/ +/*! These functions return the index of the almost maximum element in a + vector + */ +/*************************************************************************/ +idx_t rargmax2(size_t n, real_t *x) +{ + size_t i, max1, max2; + + if (x[0] > x[1]) { + max1 = 0; + max2 = 1; + } + else { + max1 = 1; + max2 = 0; + } + + for (i=2; i x[max1]) { + max2 = max1; + max1 = i; + } + else if (x[i] > x[max2]) + max2 = i; + } + + return max2; +} + + +/*************************************************************************/ +/*! These functions return the index of the second largest elements in the + vector formed by x.y where '.' is element-wise multiplication */ +/*************************************************************************/ +idx_t iargmax2_nrm(size_t n, idx_t *x, real_t *y) +{ + size_t i, max1, max2; + + if (x[0]*y[0] > x[1]*y[1]) { + max1 = 0; + max2 = 1; + } + else { + max1 = 1; + max2 = 0; + } + + for (i=2; i x[max1]*y[max1]) { + max2 = max1; + max1 = i; + } + else if (x[i]*y[i] > x[max2]*y[max2]) + max2 = i; + } + + return max2; +} + + +/*************************************************************************/ +/*! converts a signal code into a Metis return code + */ +/*************************************************************************/ +int metis_rcode(int sigrval) +{ + switch (sigrval) { + case 0: + return METIS_OK; + break; + case SIGMEM: + return METIS_ERROR_MEMORY; + break; + default: + return METIS_ERROR; + break; + } +} + + diff --git a/src/metis/libmetis/wspace.c b/src/metis/libmetis/wspace.c new file mode 100644 index 0000000..a474c3c --- /dev/null +++ b/src/metis/libmetis/wspace.c @@ -0,0 +1,214 @@ +/*! +\file +\brief Functions dealing with memory allocation and workspace management + +\date Started 2/24/96 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version $Id: wspace.c 10492 2011-07-06 09:28:42Z karypis $ +*/ + +#include "metislib.h" + + +/*************************************************************************/ +/*! This function allocates memory for the workspace */ +/*************************************************************************/ +void AllocateWorkSpace(ctrl_t *ctrl, graph_t *graph) +{ + size_t coresize; + + switch (ctrl->optype) { + case METIS_OP_PMETIS: + coresize = 3*(graph->nvtxs+1)*sizeof(idx_t) + + 5*(ctrl->nparts+1)*graph->ncon*sizeof(idx_t) + + 5*(ctrl->nparts+1)*graph->ncon*sizeof(real_t); + break; + default: + coresize = 4*(graph->nvtxs+1)*sizeof(idx_t) + + 5*(ctrl->nparts+1)*graph->ncon*sizeof(idx_t) + + 5*(ctrl->nparts+1)*graph->ncon*sizeof(real_t); + } + /*coresize = 0;*/ + ctrl->mcore = gk_mcoreCreate(coresize); + + ctrl->nbrpoolsize = 0; + ctrl->nbrpoolcpos = 0; +} + + +/*************************************************************************/ +/*! This function allocates refinement-specific memory for the workspace */ +/*************************************************************************/ +void AllocateRefinementWorkSpace(ctrl_t *ctrl, idx_t nbrpoolsize) +{ + ctrl->nbrpoolsize = nbrpoolsize; + ctrl->nbrpoolcpos = 0; + ctrl->nbrpoolreallocs = 0; + + switch (ctrl->objtype) { + case METIS_OBJTYPE_CUT: + ctrl->cnbrpool = (cnbr_t *)gk_malloc(ctrl->nbrpoolsize*sizeof(cnbr_t), + "AllocateRefinementWorkSpace: cnbrpool"); + break; + + case METIS_OBJTYPE_VOL: + ctrl->vnbrpool = (vnbr_t *)gk_malloc(ctrl->nbrpoolsize*sizeof(vnbr_t), + "AllocateRefinementWorkSpace: vnbrpool"); + break; + + default: + gk_errexit(SIGERR, "Unknown objtype of %d\n", ctrl->objtype); + } + + + /* Allocate the memory for the sparse subdomain graph */ + if (ctrl->minconn) { + ctrl->pvec1 = imalloc(ctrl->nparts+1, "AllocateRefinementWorkSpace: pvec1"); + ctrl->pvec2 = imalloc(ctrl->nparts+1, "AllocateRefinementWorkSpace: pvec2"); + ctrl->maxnads = ismalloc(ctrl->nparts, INIT_MAXNAD, "AllocateRefinementWorkSpace: maxnads"); + ctrl->nads = imalloc(ctrl->nparts, "AllocateRefinementWorkSpace: nads"); + ctrl->adids = iAllocMatrix(ctrl->nparts, INIT_MAXNAD, 0, "AllocateRefinementWorkSpace: adids"); + ctrl->adwgts = iAllocMatrix(ctrl->nparts, INIT_MAXNAD, 0, "AllocateRefinementWorkSpace: adwgts"); + } +} + + +/*************************************************************************/ +/*! This function frees the workspace */ +/*************************************************************************/ +void FreeWorkSpace(ctrl_t *ctrl) +{ + gk_mcoreDestroy(&ctrl->mcore, ctrl->dbglvl&METIS_DBG_INFO); + + IFSET(ctrl->dbglvl, METIS_DBG_INFO, + printf(" nbrpool statistics\n" + " nbrpoolsize: %12zu nbrpoolcpos: %12zu\n" + " nbrpoolreallocs: %12zu\n\n", + ctrl->nbrpoolsize, ctrl->nbrpoolcpos, + ctrl->nbrpoolreallocs)); + + gk_free((void **)&ctrl->cnbrpool, &ctrl->vnbrpool, LTERM); + ctrl->nbrpoolsize = 0; + ctrl->nbrpoolcpos = 0; + + if (ctrl->minconn) { + iFreeMatrix(&(ctrl->adids), ctrl->nparts, INIT_MAXNAD); + iFreeMatrix(&(ctrl->adwgts), ctrl->nparts, INIT_MAXNAD); + + gk_free((void **)&ctrl->pvec1, &ctrl->pvec2, + &ctrl->maxnads, &ctrl->nads, LTERM); + } +} + + +/*************************************************************************/ +/*! This function allocate space from the workspace/heap */ +/*************************************************************************/ +void *wspacemalloc(ctrl_t *ctrl, size_t nbytes) +{ + return gk_mcoreMalloc(ctrl->mcore, nbytes); +} + + +/*************************************************************************/ +/*! This function sets a marker in the stack of malloc ops to be used + subsequently for freeing purposes */ +/*************************************************************************/ +void wspacepush(ctrl_t *ctrl) +{ + gk_mcorePush(ctrl->mcore); +} + + +/*************************************************************************/ +/*! This function frees all mops since the last push */ +/*************************************************************************/ +void wspacepop(ctrl_t *ctrl) +{ + gk_mcorePop(ctrl->mcore); +} + + +/*************************************************************************/ +/*! This function allocate space from the core */ +/*************************************************************************/ +idx_t *iwspacemalloc(ctrl_t *ctrl, idx_t n) +{ + return (idx_t *)wspacemalloc(ctrl, n*sizeof(idx_t)); +} + + +/*************************************************************************/ +/*! This function allocate space from the core */ +/*************************************************************************/ +real_t *rwspacemalloc(ctrl_t *ctrl, idx_t n) +{ + return (real_t *)wspacemalloc(ctrl, n*sizeof(real_t)); +} + + +/*************************************************************************/ +/*! This function allocate space from the core */ +/*************************************************************************/ +ikv_t *ikvwspacemalloc(ctrl_t *ctrl, idx_t n) +{ + return (ikv_t *)wspacemalloc(ctrl, n*sizeof(ikv_t)); +} + + +/*************************************************************************/ +/*! This function resets the cnbrpool */ +/*************************************************************************/ +void cnbrpoolReset(ctrl_t *ctrl) +{ + ctrl->nbrpoolcpos = 0; +} + + +/*************************************************************************/ +/*! This function gets the next free index from cnbrpool */ +/*************************************************************************/ +idx_t cnbrpoolGetNext(ctrl_t *ctrl, idx_t nnbrs) +{ + ctrl->nbrpoolcpos += nnbrs; + + if (ctrl->nbrpoolcpos > ctrl->nbrpoolsize) { + ctrl->nbrpoolsize += gk_max(10*nnbrs, ctrl->nbrpoolsize/2); + + ctrl->cnbrpool = (cnbr_t *)gk_realloc(ctrl->cnbrpool, + ctrl->nbrpoolsize*sizeof(cnbr_t), "cnbrpoolGet: cnbrpool"); + ctrl->nbrpoolreallocs++; + } + + return ctrl->nbrpoolcpos - nnbrs; +} + + +/*************************************************************************/ +/*! This function resets the vnbrpool */ +/*************************************************************************/ +void vnbrpoolReset(ctrl_t *ctrl) +{ + ctrl->nbrpoolcpos = 0; +} + + +/*************************************************************************/ +/*! This function gets the next free index from vnbrpool */ +/*************************************************************************/ +idx_t vnbrpoolGetNext(ctrl_t *ctrl, idx_t nnbrs) +{ + ctrl->nbrpoolcpos += nnbrs; + + if (ctrl->nbrpoolcpos > ctrl->nbrpoolsize) { + ctrl->nbrpoolsize += gk_max(10*nnbrs, ctrl->nbrpoolsize/2); + + ctrl->vnbrpool = (vnbr_t *)gk_realloc(ctrl->vnbrpool, + ctrl->nbrpoolsize*sizeof(vnbr_t), "vnbrpoolGet: vnbrpool"); + ctrl->nbrpoolreallocs++; + } + + return ctrl->nbrpoolcpos - nnbrs; +} + diff --git a/src/metis/macros.h b/src/metis/macros.h deleted file mode 100644 index 7f2b0ca..0000000 --- a/src/metis/macros.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * macros.h - * - * This file contains macros used in multilevel - * - * Started 9/25/94 - * George - * - * $Id: macros.h,v 1.6 2003/04/30 12:42:05 karypis Exp $ - * - */ - - -/************************************************************************* -* The following macro returns a random number in the specified range -**************************************************************************/ -#define AND(a, b) ((a) < 0 ? ((-(a))&(b)) : ((a)&(b))) -#define OR(a, b) ((a) < 0 ? -((-(a))|(b)) : ((a)|(b))) -#define XOR(a, b) ((a) < 0 ? -((-(a))^(b)) : ((a)^(b))) - -#define idxcopy(n, a, b) (idxtype *)memcpy((void *)(b), (void *)(a), sizeof(idxtype)*(n)) - -#define HASHFCT(key, size) ((key)%(size)) - - -/************************************************************************* -* Datatype related macros -**************************************************************************/ -#define idxtype_abs(x) ((x) > 0 ? (x) : -(x)) - - - - - -/************************************************************************* -* These macros insert and remove nodes from the boundary list -**************************************************************************/ -#define BNDInsert(nbnd, bndind, bndptr, vtx) \ - do { \ - ASSERT(bndptr[vtx] == -1); \ - bndind[nbnd] = vtx; \ - bndptr[vtx] = nbnd++;\ - } while(0) - -#define BNDDelete(nbnd, bndind, bndptr, vtx) \ - do { \ - ASSERT(bndptr[vtx] != -1); \ - bndind[bndptr[vtx]] = bndind[--nbnd]; \ - bndptr[bndind[nbnd]] = bndptr[vtx]; \ - bndptr[vtx] = -1; \ - } while(0) - - diff --git a/src/metis/manual/manual.pdf b/src/metis/manual/manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e1fed75e367563657486362a10a11b88ef44f219 GIT binary patch literal 355362 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%0DLM>D(C%%v2 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 z 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℞-_W=#97O^Hqqa9B{jBW`p*b~yDF&Hifs=crZXu2lf zFz2?az$In^0djle%bKqDeQJ48@|WiM%TyVExU}-t&%Wqs1=wRFqmqRlDre2AzbP$} z;kzmH5?N9JgYlTq|6Ez^c;l2fGlK?GtI8Hv5kqRR_dt&*`e-s;MTU=pZscGdEBTb+9EVpPiRi2&VNV_u(7FUzVS01!TL8gI@RrT_wfyOBJ#bu3+)$iKwpDB10g^u2TF!ktyHm`sj5F5%yu{h@4)H zRF00^U>A`zB8n(-?sXB@#^^B#&6&CD@NPIbP?Tsd7xS&Y~xv@yR08;BHCIh*YTnsiopb@~Rv z3lBCu(GiK$&6f@hNo^2Ln6Svu z>yhf<$bUO>ib%XYYr71$?sNn^TAm8hWK+O6=k41|1Z z-pc6Wd|>gB|Ms z^4~;4SFuzRRMuv9cDE`dzl=DlU4&^jhh6}qUEp=yFVFW6^c5iviG+(x15^#-=|(*h zzvXu1sN+xtEh%9P?cHL=f(L?8G?|y`B28)Ib8R#-16i-^?dR->Wrz@kQ;h(33;z+ZN4aqK)*@L}E$`xs{!_K6uUMkA8ur$Y!>pQxlaIKZ`vnM+7! zA{vd`#4th3)u{R`BX{{Bt4x?JIWCD?DXhdEN5(>>@NZC!p`@+L%E*?Lbx>D^EvmX$2L(d&k$erhE)eyOVqb+xY5;Z#rV3~0PL(zjI`Vm(+#rl`)6k%rfG(g57kWkyp+*H$o3{2jX z3mbV+?Dz3XCgR{G6SA}$OwIC<;JvB1`BPq6fGVXtCfbOYn7L0uGke67kjNQLZXFYt zlC+|`L`Usghp%2vZ{=~%t{$s1aNx#l&x>c%3moj3VLa9z1=Xf45pzpm8-Imd>7J-- zi=8dN@UEhEVXs%`hs?D-OKvGUlsqho$xrxI(pfg^+2fP5FrLDk#D1xIqER`$1y6d& zJhBgL0**Tn9PPT{qWsrI=foEKw-%mX1%d2|L&)u5SZlY`*soqSUqRpzu%K*2L|7eOSo9CREK&uF>WgtnNS$XQ zf}RFrQ|ZfLG94>dfj;BgZL3fc>)DA3bEXld*{NKSUH2JBb7MS2&e&QYtuo^~b4eL4bvg=#M7af?RbH*AxBUl! zz$4P|3R2=0|6U16CHQb-2Bz}&KYIHeh5#VJMp#)xDKdTd#I$V*>!4VBZc9+E^3gpO zBuP!L{Jea^9oIqxp5cCyIE+rtmm~?DOOg5&8iqWHzmZ@(MenT(h7+09hCZj@!2UeF zgsCznXu&u%q7u`x`v#32*#`f3?^sL&)esQ{^*t4E)#wg#7lh34%>J07M;W@obWV*p zB$BP13!H#l+R&B*TK8$tVWn92*p;ZCwrv)VcU3?)*Dg69ZrR^2rXgaHZQSx9^O#P% zMEdol{u_L%TC(N5XPimntVuU21|RsYUrE<3s8AS}+L~{Qp!K~w27X32E~;TvJ@JVzai4=z>-JL5yN5HZchfLh$C+`qkrjwlw*RKh!N z6g}R>_t=ot;9pubskB=DIh!JPmo-KHFlrv~BfkWWCNyEp5jPmXG-|M9ueQc zpKIz-z>cKQJb-#gts<3H6n9D^&>@dgF+tQRi&JOFC?Ia^Hg+yXD=(_Bz!G6}>>tWM z=f8?Sy~F)j>9Fil>HjR3r{WP-*GeRA28-x*4|S|T+S*O5ldtWi&$j_T_~VsfASn9s z)rr>m@#279Vpmm8dqIGAQWU6IM4|zx{Z;B869v+nqAV<*m=G)`WyDrVq<|K(ihF`S zKrD?4*c~9y$*OYuL25a=o?r2};fQ7OcfnN>j)4a7%#%o-3quolz~}LdqCC7b`Dno7 zdh?(%sQ{a&8PnK_K)^yJKsk7vR=sigiKKona$5_D

&_(sIPS$~(h<;UD@W)|(U2 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ͼ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%0An>zUCb+Qr$T~Y{841E1jPAq} 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 H2 - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void Match_RM(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, ii, j, nvtxs, cnvtxs, maxidx; - idxtype *xadj, *vwgt, *adjncy, *adjwgt; - idxtype *match, *cmap, *perm; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - cmap = graph->cmap; - match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs)); - - perm = idxwspacemalloc(ctrl, nvtxs); - RandomPermute(nvtxs, perm, 1); - - cnvtxs = 0; - for (ii=0; iimaxvwgt) { - maxidx = adjncy[j]; - break; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); - - CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void Match_RM_NVW(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, ii, j, nvtxs, cnvtxs, maxidx; - idxtype *xadj, *adjncy; - idxtype *match, *cmap, *perm; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - - cmap = graph->cmap; - match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs)); - - perm = idxwspacemalloc(ctrl, nvtxs); - RandomPermute(nvtxs, perm, 1); - - cnvtxs = 0; - for (ii=0; iidbglvl, DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); - - CreateCoarseGraph_NVW(ctrl, graph, cnvtxs, match, perm); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void Match_HEM(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, ii, j, k, nvtxs, cnvtxs, maxidx, maxwgt; - idxtype *xadj, *vwgt, *adjncy, *adjwgt; - idxtype *match, *cmap, *perm; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - cmap = graph->cmap; - match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs)); - - perm = idxwspacemalloc(ctrl, nvtxs); - RandomPermute(nvtxs, perm, 1); - - cnvtxs = 0; - for (ii=0; iimaxvwgt) { - maxwgt = adjwgt[j]; - maxidx = adjncy[j]; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); - - CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void Match_SHEM(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, ii, j, k, nvtxs, cnvtxs, maxidx, maxwgt, avgdegree; - idxtype *xadj, *vwgt, *adjncy, *adjwgt; - idxtype *match, *cmap, *degrees, *perm, *tperm; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - cmap = graph->cmap; - match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs)); - - perm = idxwspacemalloc(ctrl, nvtxs); - tperm = idxwspacemalloc(ctrl, nvtxs); - degrees = idxwspacemalloc(ctrl, nvtxs); - - RandomPermute(nvtxs, tperm, 1); - avgdegree = 0.7*(xadj[nvtxs]/nvtxs); - for (i=0; i avgdegree ? avgdegree : xadj[i+1]-xadj[i]); - BucketSortKeysInc(nvtxs, avgdegree, degrees, tperm, perm); - - cnvtxs = 0; - - /* Take care any islands. Islands are matched with non-islands due to coarsening */ - for (ii=0; iiii; j--) { - k = perm[j]; - if (match[k] == UNMATCHED && xadj[k] < xadj[k+1]) { - maxidx = k; - break; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - /* Continue with normal matching */ - for (; iimaxvwgt) { - maxwgt = adjwgt[j]; - maxidx = adjncy[j]; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); - - idxwspacefree(ctrl, nvtxs); /* degrees */ - idxwspacefree(ctrl, nvtxs); /* tperm */ - - CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - diff --git a/src/metis/mbalance.c b/src/metis/mbalance.c deleted file mode 100644 index d2654ec..0000000 --- a/src/metis/mbalance.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * mbalance.c - * - * This file contains code that is used to forcefully balance either - * bisections or k-sections - * - * Started 7/29/97 - * George - * - * $Id: mbalance.c,v 1.2 2002/08/10 06:29:31 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function is the entry point of the bisection balancing algorithms. -**************************************************************************/ -void MocBalance2Way(CtrlType *ctrl, GraphType *graph, float *tpwgts, float lbfactor) -{ - - if (Compute2WayHLoadImbalance(graph->ncon, graph->npwgts, tpwgts) < lbfactor) - return; - - MocGeneral2WayBalance(ctrl, graph, tpwgts, lbfactor); - -} - - -/************************************************************************* -* This function performs an edge-based FM refinement -**************************************************************************/ -void MocGeneral2WayBalance(CtrlType *ctrl, GraphType *graph, float *tpwgts, float lbfactor) -{ - idxtype i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, me, limit, tmp, cnum; - idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind; - idxtype *moved, *swaps, *perm, *qnum; - float *nvwgt, *npwgts, mindiff[MAXNCON], origbal, minbal, newbal; - PQueueType parts[MAXNCON][2]; - idxtype higain, oldgain, mincut, newcut, mincutorder; - idxtype qsizes[MAXNCON][2]; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - id = graph->id; - ed = graph->ed; - npwgts = graph->npwgts; - bndptr = graph->bndptr; - bndind = graph->bndind; - - moved = idxwspacemalloc(ctrl, nvtxs); - swaps = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - qnum = idxwspacemalloc(ctrl, nvtxs); - - limit = amin(amax(0.01*nvtxs, 15), 100); - - /* Initialize the queues */ - for (i=0; i qsizes[j][from] && nvwgt[i*ncon+qnum[i]] < 1.3*nvwgt[i*ncon+j]) { - qsizes[qnum[i]][from]--; - qsizes[j][from]++; - qnum[i] = j; - } - } - } - } - } - -/* - mprintf("Weight Distribution (after):\t "); - for (i=0; imincut; - mincutorder = -1; - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("Parts: ["); - for (l=0; lnvtxs, graph->nbnd, graph->mincut, origbal); - } - - idxset(nvtxs, -1, moved); - - ASSERT(ComputeCut(graph, where) == graph->mincut); - ASSERT(CheckBnd(graph)); - - /* Insert all nodes in the priority queues */ - nbnd = graph->nbnd; - RandomPermute(nvtxs, perm, 1); - for (ii=0; ii limit) { /* We hit the limit, undo last move */ - newcut += (ed[higain]-id[higain]); - gk_faxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1); - break; - } - - where[higain] = to; - moved[higain] = nswaps; - swaps[nswaps] = higain; - - if (ctrl->dbglvl&DBG_MOVEINFO) { - mprintf("Moved %6D from %D(%D). Gain: %5D, Cut: %5D, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut); - for (l=0; l 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - for (j=xadj[higain]; j 0 && bndptr[k] == -1) - BNDInsert(nbnd, bndind, bndptr, k); - } - } - - - - /**************************************************************** - * Roll back computations - *****************************************************************/ - for (nswaps--; nswaps>mincutorder; nswaps--) { - higain = swaps[nswaps]; - - to = where[higain] = (where[higain]+1)%2; - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - else if (ed[higain] > 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - gk_faxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+((to+1)%2)*ncon, 1); - for (j=xadj[higain]; j 0) - BNDInsert(nbnd, bndind, bndptr, k); - } - } - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("\tMincut: %6D at %5D, NBND: %6D, NPwgts: [", mincut, mincutorder, nbnd); - for (l=0; lmincut = mincut; - graph->nbnd = nbnd; - - - for (i=0; i - - -/************************************************************************* -* This function is the entry point of the bisection balancing algorithms. -**************************************************************************/ -void MocBalance2Way2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec) -{ - idxtype i; - float tvec[MAXNCON]; - - Compute2WayHLoadImbalanceVec(graph->ncon, graph->npwgts, tpwgts, tvec); - if (!AreAllBelow(graph->ncon, tvec, ubvec)) - MocGeneral2WayBalance2(ctrl, graph, tpwgts, ubvec); -} - - - -/************************************************************************* -* This function performs an edge-based FM refinement -**************************************************************************/ -void MocGeneral2WayBalance2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec) -{ - idxtype i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, me, limit, tmp, cnum; - idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind; - idxtype *moved, *swaps, *perm, *qnum; - float *nvwgt, *npwgts, origbal[MAXNCON], minbal[MAXNCON], newbal[MAXNCON]; - PQueueType parts[MAXNCON][2]; - idxtype higain, oldgain, mincut, newcut, mincutorder; - float *maxwgt, *minwgt, tvec[MAXNCON]; - - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - id = graph->id; - ed = graph->ed; - npwgts = graph->npwgts; - bndptr = graph->bndptr; - bndind = graph->bndind; - - moved = idxwspacemalloc(ctrl, nvtxs); - swaps = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - qnum = idxwspacemalloc(ctrl, nvtxs); - - limit = amin(amax(0.01*nvtxs, 15), 100); - - /* Setup the weight intervals of the two subdomains */ - minwgt = fwspacemalloc(ctrl, 2*ncon); - maxwgt = fwspacemalloc(ctrl, 2*ncon); - - for (i=0; i<2; i++) { - for (j=0; jmincut; - mincutorder = -1; - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("Parts: ["); - for (l=0; lnvtxs, graph->nbnd, graph->mincut); - for (i=0; imincut); - ASSERT(CheckBnd(graph)); - - /* Insert all nodes in the priority queues */ - nbnd = graph->nbnd; - RandomPermute(nvtxs, perm, 1); - for (ii=0; ii limit) { /* We hit the limit, undo last move */ - newcut += (ed[higain]-id[higain]); - gk_faxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1); - break; - } - - where[higain] = to; - moved[higain] = nswaps; - swaps[nswaps] = higain; - - if (ctrl->dbglvl&DBG_MOVEINFO) { - mprintf("Moved %6D from %D(%D). Gain: %5D, Cut: %5D, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut); - for (i=0; i 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - for (j=xadj[higain]; j 0 && bndptr[k] == -1) - BNDInsert(nbnd, bndind, bndptr, k); - } - - } - - - - /**************************************************************** - * Roll back computations - *****************************************************************/ - for (i=0; imincutorder; nswaps--) { - higain = swaps[nswaps]; - - to = where[higain] = (where[higain]+1)%2; - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - else if (ed[higain] > 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - gk_faxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+((to+1)%2)*ncon, 1); - for (j=xadj[higain]; j 0) - BNDInsert(nbnd, bndind, bndptr, k); - } - } - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("\tMincut: %6D at %5D, NBND: %6D, NPwgts: [", mincut, mincutorder, nbnd); - for (i=0; imincut = mincut; - graph->nbnd = nbnd; - - - for (i=0; i= maxdiff) { - maxdiff = diff; - *from = j; - *cnum = i; - } - } - } - -/* DELETE -j = *from; -for (i=0; i 0) { - maxdiff = (npwgts[(*from)*ncon+i] - maxwgt[(*from)*ncon+i]); - *cnum = i; - break; - } - } - - for (i++; i maxdiff && PQueueGetSize(&queues[i][*from]) > 0) { - maxdiff = diff; - *cnum = i; - } - } - } - - /* If the constraints ar OK, select a high gain vertex */ - if (*from == -1) { - maxgain = -100000; - for (j=0; j<2; j++) { - for (i=0; i 0 && PQueueGetKey(&queues[i][j]) > maxgain) { - maxgain = PQueueGetKey(&queues[i][0]); - *from = j; - *cnum = i; - } - } - } - - /* mprintf("(%2D %2D) %3D\n", *from, *cnum, maxgain); */ - } -} diff --git a/src/metis/mcoarsen.c b/src/metis/mcoarsen.c deleted file mode 100644 index d454cf8..0000000 --- a/src/metis/mcoarsen.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * mcoarsen.c - * - * This file contains the driving routines for the coarsening process - * - * Started 7/23/97 - * George - * - * $Id: mcoarsen.c,v 1.3 2003/07/31 15:52:47 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function takes a graph and creates a sequence of coarser graphs -**************************************************************************/ -GraphType *MCCoarsen2Way(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, clevel; - GraphType *cgraph; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->CoarsenTmr)); - - cgraph = graph; - - clevel = 0; - do { - if (ctrl->dbglvl&DBG_COARSEN) { - mprintf("%6D %7D %10D [%D] [%6.4f", cgraph->nvtxs, cgraph->nedges, - idxsum(cgraph->nvtxs, cgraph->adjwgtsum, 1), ctrl->CoarsenTo, ctrl->nmaxvwgt); - for (i=0; incon; i++) - mprintf(" %5.3f", gk_fsum(cgraph->nvtxs, cgraph->nvwgt+i, cgraph->ncon)); - mprintf("]\n"); - } - - if (cgraph->nedges == 0) { - MCMatch_RM(ctrl, cgraph); - } - else { - switch (ctrl->CType) { - case MTYPE_RM: - MCMatch_RM(ctrl, cgraph); - break; - case MTYPE_HEM: - if (clevel < 1) - MCMatch_RM(ctrl, cgraph); - else - MCMatch_HEM(ctrl, cgraph); - break; - case MTYPE_SHEM: - if (clevel < 1) - MCMatch_RM(ctrl, cgraph); - else - MCMatch_SHEM(ctrl, cgraph); - break; - case MTYPE_SHEMKWAY: - MCMatch_SHEM(ctrl, cgraph); - break; - case MTYPE_SHEBM_ONENORM: - MCMatch_SHEBM(ctrl, cgraph, 1); - break; - case MTYPE_SHEBM_INFNORM: - MCMatch_SHEBM(ctrl, cgraph, -1); - break; - case MTYPE_SBHEM_ONENORM: - MCMatch_SBHEM(ctrl, cgraph, 1); - break; - case MTYPE_SBHEM_INFNORM: - MCMatch_SBHEM(ctrl, cgraph, -1); - break; - default: - errexit("Unknown CType: %d\n", ctrl->CType); - } - } - - cgraph = cgraph->coarser; - clevel++; - - } while (cgraph->nvtxs > ctrl->CoarsenTo && cgraph->nvtxs < COARSEN_FRACTION2*cgraph->finer->nvtxs && cgraph->nedges > cgraph->nvtxs/2); - - if (ctrl->dbglvl&DBG_COARSEN) { - mprintf("%6D %7D %10D [%D] [%6.4f", cgraph->nvtxs, cgraph->nedges, - idxsum(cgraph->nvtxs, cgraph->adjwgtsum, 1), ctrl->CoarsenTo, ctrl->nmaxvwgt); - for (i=0; incon; i++) - mprintf(" %5.3f", gk_fsum(cgraph->nvtxs, cgraph->nvwgt+i, cgraph->ncon)); - mprintf("]\n"); - } - - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->CoarsenTmr)); - - return cgraph; -} - diff --git a/src/metis/memory.c b/src/metis/memory.c deleted file mode 100644 index 7b7be22..0000000 --- a/src/metis/memory.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * memory.c - * - * This file contains routines that deal with memory allocation - * - * Started 2/24/96 - * George - * - * $Id: memory.c,v 1.2 2002/08/10 06:29:31 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function allocates memory for the workspace -**************************************************************************/ -void AllocateWorkSpace(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - ctrl->wspace.pmat = NULL; - - if (ctrl->optype == OP_KMETIS) { - ctrl->wspace.edegrees = (EDegreeType *)gk_malloc(graph->nedges*sizeof(EDegreeType), "AllocateWorkSpace: edegrees"); - ctrl->wspace.vedegrees = NULL; - ctrl->wspace.auxcore = (idxtype *)ctrl->wspace.edegrees; - - ctrl->wspace.pmat = idxmalloc(nparts*nparts, "AllocateWorkSpace: pmat"); - - /* Memory requirements for different phases - Coarsening - Matching: 4*nvtxs vectors - Contraction: 2*nvtxs vectors (from the above 4), 1*nparts, 1*Nedges - Total = MAX(4*nvtxs, 2*nvtxs+nparts+nedges) - - Refinement - Random Refinement/Balance: 5*nparts + 1*nvtxs + 2*nedges - Greedy Refinement/Balance: 5*nparts + 2*nvtxs + 2*nedges + 1*PQueue(==Nvtxs) - Total = 5*nparts + 3*nvtxs + 2*nedges - - Total = 5*nparts + 3*nvtxs + 2*nedges - */ - ctrl->wspace.maxcore = 3*(graph->nvtxs+1) + /* Match/Refinement vectors */ - 5*(nparts+1) + /* Partition weights etc */ - graph->nvtxs*(sizeof(ListNodeType)/sizeof(idxtype)) + /* Greedy k-way balance/refine */ - 20 /* padding for 64 bit machines */ - ; - } - else if (ctrl->optype == OP_KVMETIS) { - ctrl->wspace.edegrees = NULL; - ctrl->wspace.vedegrees = (VEDegreeType *)gk_malloc(graph->nedges*sizeof(VEDegreeType), "AllocateWorkSpace: vedegrees"); - ctrl->wspace.auxcore = (idxtype *)ctrl->wspace.vedegrees; - - ctrl->wspace.pmat = idxmalloc(nparts*nparts, "AllocateWorkSpace: pmat"); - - /* Memory requirements for different phases are identical to KMETIS */ - ctrl->wspace.maxcore = 3*(graph->nvtxs+1) + /* Match/Refinement vectors */ - 3*(nparts+1) + /* Partition weights etc */ - graph->nvtxs*(sizeof(ListNodeType)/sizeof(idxtype)) + /* Greedy k-way balance/refine */ - 20 /* padding for 64 bit machines */ - ; - } - else { - ctrl->wspace.edegrees = (EDegreeType *)idxmalloc(graph->nedges, "AllocateWorkSpace: edegrees"); - ctrl->wspace.vedegrees = NULL; - ctrl->wspace.auxcore = (idxtype *)ctrl->wspace.edegrees; - - ctrl->wspace.maxcore = 5*(graph->nvtxs+1) + /* Refinement vectors */ - 4*(nparts+1) + /* Partition weights etc */ - 2*graph->ncon*graph->nvtxs*(sizeof(ListNodeType)/sizeof(idxtype)) + /* 2-way refinement */ - 2*graph->ncon*(NEG_GAINSPAN+PLUS_GAINSPAN+1)*(sizeof(ListNodeType *)/sizeof(idxtype)) + /* 2-way refinement */ - 20 /* padding for 64 bit machines */ - ; - } - - ctrl->wspace.maxcore += HTLENGTH; - ctrl->wspace.core = idxmalloc(ctrl->wspace.maxcore, "AllocateWorkSpace: maxcore"); - ctrl->wspace.ccore = 0; -} - - -/************************************************************************* -* This function allocates memory for the workspace -**************************************************************************/ -void FreeWorkSpace(CtrlType *ctrl, GraphType *graph) -{ - gk_free((void **)&ctrl->wspace.edegrees, &ctrl->wspace.vedegrees, &ctrl->wspace.core, &ctrl->wspace.pmat, LTERM); -} - -/************************************************************************* -* This function returns how may words are left in the workspace -**************************************************************************/ -idxtype WspaceAvail(CtrlType *ctrl) -{ - return ctrl->wspace.maxcore - ctrl->wspace.ccore; -} - - -/************************************************************************* -* This function allocate space from the core -**************************************************************************/ -idxtype *idxwspacemalloc(CtrlType *ctrl, idxtype n) -{ - n += n%2; /* This is a fix for 64 bit machines that require 8-byte pointer allignment */ - - ctrl->wspace.ccore += n; - ASSERT(ctrl->wspace.ccore <= ctrl->wspace.maxcore); - return ctrl->wspace.core + ctrl->wspace.ccore - n; -} - -/************************************************************************* -* This function frees space from the core -**************************************************************************/ -void idxwspacefree(CtrlType *ctrl, idxtype n) -{ - n += n%2; /* This is a fix for 64 bit machines that require 8-byte pointer allignment */ - - ctrl->wspace.ccore -= n; - ASSERT(ctrl->wspace.ccore >= 0); -} - - -/************************************************************************* -* This function allocate space from the core -**************************************************************************/ -float *fwspacemalloc(CtrlType *ctrl, idxtype n) -{ - n += n%2; /* This is a fix for 64 bit machines that require 8-byte pointer allignment */ - - ctrl->wspace.ccore += n; - ASSERT(ctrl->wspace.ccore <= ctrl->wspace.maxcore); - return (float *) (ctrl->wspace.core + ctrl->wspace.ccore - n); -} - -/************************************************************************* -* This function frees space from the core -**************************************************************************/ -void fwspacefree(CtrlType *ctrl, idxtype n) -{ - n += n%2; /* This is a fix for 64 bit machines that require 8-byte pointer allignment */ - - ctrl->wspace.ccore -= n; - ASSERT(ctrl->wspace.ccore >= 0); -} - - - -/************************************************************************* -* This function creates a CoarseGraphType data structure and initializes -* the various fields -**************************************************************************/ -GraphType *CreateGraph(void) -{ - GraphType *graph; - - graph = (GraphType *)gk_malloc(sizeof(GraphType), "CreateCoarseGraph: graph"); - - InitGraph(graph); - - return graph; -} - - -/************************************************************************* -* This function creates a CoarseGraphType data structure and initializes -* the various fields -**************************************************************************/ -void InitGraph(GraphType *graph) -{ - /* graph size constants */ - graph->nvtxs = -1; - graph->nedges = -1; - graph->ncon = -1; - graph->mincut = -1; - graph->minvol = -1; - graph->nbnd = -1; - - /* memory for the graph structure */ - graph->xadj = NULL; - graph->vwgt = NULL; - graph->nvwgt = NULL; - graph->vsize = NULL; - graph->adjncy = NULL; - graph->adjwgt = NULL; - graph->adjwgtsum = NULL; - graph->label = NULL; - graph->cmap = NULL; - graph->coords = NULL; - - /* by default these are set to true, but the can be explicitly changed afterwards */ - graph->free_xadj = 1; - graph->free_vwgt = 1; - graph->free_vsize = 1; - graph->free_adjncy = 1; - graph->free_adjwgt = 1; - - - /* memory for the partition/refinement structure */ - graph->where = NULL; - graph->pwgts = NULL; - graph->npwgts = NULL; - graph->id = NULL; - graph->ed = NULL; - graph->bndptr = NULL; - graph->bndind = NULL; - graph->rinfo = NULL; - graph->vrinfo = NULL; - graph->nrinfo = NULL; - - /* linked-list structure */ - graph->coarser = NULL; - graph->finer = NULL; -} - - - -/************************************************************************* -* This function allocates memory for the requested fields of the graph's -* structure (i.e., not partition/refinement structure). -* The size of these fields is determined by the size of the graph itself -* The fields should be provided as a comma-separated string with no spaces -* For example -* AllocGraphFields(&graph, "xadj,adjncy,vwgt") -**************************************************************************/ -void AllocGraphFields(GraphType *graph, char *fields) -{ - char *sptr, *eptr; - -} - - -/************************************************************************* -* This function frees the refinement/partition memory -* any memory stored in a graph -**************************************************************************/ -void FreeRData(GraphType *graph) -{ - /* free partition/refinement structure */ - gk_free((void **)&graph->where, &graph->pwgts, &graph->npwgts, &graph->id, - &graph->ed, &graph->bndptr, &graph->bndind, &graph->rinfo, &graph->vrinfo, - &graph->nrinfo, LTERM); -} - - -/************************************************************************* -* This function deallocates any memory stored in a graph -**************************************************************************/ -void FreeGraph(GraphType *graph, int flag) -{ - - /* free graph structure */ - if (graph->free_xadj) - gk_free((void **)&graph->xadj, LTERM); - if (graph->free_vwgt) - gk_free((void **)&graph->vwgt, LTERM); - if (graph->free_vsize) - gk_free((void **)&graph->vsize, LTERM); - if (graph->free_adjncy) - gk_free((void **)&graph->adjncy, LTERM); - if (graph->free_adjwgt) - gk_free((void **)&graph->adjwgt, LTERM); - - gk_free((void **)&graph->nvwgt, &graph->adjwgtsum, &graph->label, &graph->cmap, - &graph->coords, LTERM); - - /* free partition/refinement structure */ - gk_free((void **)&graph->where, &graph->pwgts, &graph->npwgts, &graph->id, - &graph->ed, &graph->bndptr, &graph->bndind, &graph->rinfo, &graph->vrinfo, - &graph->nrinfo, LTERM); - - if (flag) - gk_free((void **)&graph, LTERM); -} - diff --git a/src/metis/mesh.c b/src/metis/mesh.c deleted file mode 100644 index 9a5518e..0000000 --- a/src/metis/mesh.c +++ /dev/null @@ -1,1028 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * mesh.c - * - * This file contains routines for converting 3D and 4D finite element - * meshes into dual or nodal graphs - * - * Started 8/18/97 - * George - * - * $Id: mesh.c,v 1.2 2002/08/10 06:29:31 karypis Exp $ - * - */ - -#include - -/****************************************************************************** -* This function counts the neighbours of each element -******************************************************************************/ -idxtype METIS_MeshToDualCount(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *elms, -idxtype *etype, idxtype *numflag) -{ - idxtype cnt, esizes[] = {-1, 3, 4, 8, 4, 2}; - - if (*numflag == 1) - ChangeMesh2CNumbering((*ne)*esizes[*etype], elmnts); - - cnt=GENDUALMETIS_COUNT(*ne, *nn, *etype, elmnts, elms); - - if (*numflag == 1) - ChangeMesh2FNumbering3((*ne)*esizes[*etype], elmnts); - - return cnt; -} - - - -/***************************************************************************** -* This function creates a graph corresponding to the dual of a finite element -* mesh. At this point the supported elements are triangles, tetrahedrons, and -* bricks. -******************************************************************************/ -void METIS_MeshToDual(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *elms, -idxtype *etype, idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - idxtype esizes[] = {-1, 3, 4, 8, 4, 2}; - - if (*numflag == 1) - ChangeMesh2CNumbering((*ne)*esizes[*etype], elmnts); - - GENDUALMETIS(*ne, *nn, *etype, elmnts, elms, dxadj, dadjncy); - - if (*numflag == 1) - ChangeMesh2FNumbering((*ne)*esizes[*etype], elmnts, *ne, dxadj, dadjncy); -} - - - - - -/****************************************************************************** -* This function counts the neighbours of each element -******************************************************************************/ -idxtype METIS_MixedMeshToDualCount(idxtype *ne, idxtype *nn, idxtype *elmnts, -idxtype * elms, idxtype *etype, idxtype *numflag, idxtype *conmat, idxtype custom) -{ - - idxtype i, j, jj, k, kk, kkk, l, m, tot, n, nedges, mask, cnt=0; - idxtype *nptr, *nind; - idxtype *mark, ind[200], wgt[200]; - idxtype sizes[] = {-1, 3, 4, 8, 4, 2}, - mgcnums[] = {-1, 2, 3, 4, 2}; - - idxtype *hash; - - idxtype mgcnum[6][6] = {-1, -1, -1, -1, -1, -1, - -1, 2, 3, 3, 2 , 2, - -1, 3, 3, 3, 3, 2, - -1, 3, 3, 4, 4, 2, - -1, 2, 3, 4, 2, 2, - -1, 2, 2, 2, 2, 1} ; - - - if (custom==1) /* External magic numbers supplied */ - for (i=0,k=0;i<6;i++) - for (j=0;j<6;j++) - mgcnum[i][j]=conmat[k++]; - - - hash = idxsmalloc((*ne)+1, 0, "MXNODALMETIS: hash"); - - tot=0; - for (i=0;i<(*ne);i++){ - hash[i]=tot; - tot+=sizes[etype[i]]; - } - - if (*numflag == 1) - ChangeMesh2CNumbering(tot, elmnts); - - - - - mask = (1<<11)-1; - mark = idxsmalloc(mask+1, -1, "GENDUALMETIS: mark"); - - - /* Construct the node-element list first */ - nptr = idxsmalloc((*nn)+1, 0, "MXDUALMETIS: nptr"); - - for(i=0;i<(*ne);i++){ - l=hash[i]; - for (j=(sizes[etype[i]]), k=0; k0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - - for (i=0; i<(*ne); i++) { - for (m=j=0; j=nptr[n]; k--) { - if ((kk = nind[k]) <= i) - break; - - kkk = kk&mask; - if ((l = mark[kkk]) == -1) { - ind[m] = kk; - wgt[m] = 1; - mark[kkk] = m++; - } - else if (ind[l] == kk) { - wgt[l]++; - } - else { - for (jj=0; jj= mgcnum[etype[i]][etype[ind[j]]]){ - elms[i]++; - elms[ind[j]]++; - cnt+=2; - } - mark[ind[j]&mask] = -1; - } - } - - - gk_free((void **)&mark, LTERM); - gk_free((void **)&nptr, LTERM); - gk_free((void **)&nind, LTERM); - gk_free((void **)&hash, LTERM); - - if (*numflag == 1) - ChangeMesh2FNumbering3(tot, elmnts); - - return cnt; - -} - - - - - - -/***************************************************************************** -* This function creates a graph corresponding to the dual of a mixed element -* mesh. At this point the supported elements are triangles, tetrahedrons, -* bricks and lines. -******************************************************************************/ -void METIS_MixedMeshToDual(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *elms, -idxtype *etype, idxtype *numflag,idxtype *dxadj, idxtype *dadjncy,idxtype *conmat, -idxtype custom) -{ - - idxtype i, j, jj, k, kk, kkk, l, m, tot, n, nedges, mask; - idxtype *nptr, *nind; - idxtype *mark, ind[200], wgt[200]; - idxtype sizes[] = {-1, 3, 4, 8, 4, 2}, - mgcnums[] = {-1, 2, 3, 4, 2}; - - idxtype *hash,*mhash; - - idxtype mgcnum[6][6] = {-1, -1, -1, -1, -1, -1, - -1, 2, 3, 3, 2 , 2, - -1, 3, 3, 3, 3, 2, - -1, 3, 3, 4, 4, 2, - -1, 2, 3, 4, 2, 2, - -1, 2, 2, 2, 2, 1} ; - - - if (custom==1) /*External magic numbers supplied */ - for (i=0,k=0;i<6;i++) - for (j=0;j<6;j++) - mgcnum[i][j]=conmat[k++]; - - - hash = idxsmalloc((*ne), 0, "MXNODALMETIS: hash"); - mhash = idxsmalloc((*ne), 0, "MXNODALMETIS: hash"); - tot=0; - for (i=0;i<(*ne);i++){ - hash[i]=tot; - tot+=sizes[etype[i]]; - } - - if (*numflag == 1) - ChangeMesh2CNumbering(tot, elmnts); - - - - - mask = (1<<11)-1; - mark = idxsmalloc(mask+1, -1, "GENDUALMETIS: mark"); - - - /* Construct the node-element list first */ - nptr = idxsmalloc((*nn)+1, 0, "MXDUALMETIS: nptr"); - - for(i=0;i<(*ne);i++){ - l=hash[i]; - for (j=(sizes[etype[i]]), k=0; k0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - dxadj[0]=0; - - for (i=1; i<(*ne); i++) - mhash[i]=dxadj[i]=dxadj[i-1]+elms[i-1]; - - - for (i=0; i<(*ne); i++) { - for (m=j=0; j=nptr[n]; k--) { - if ((kk = nind[k]) <= i) - break; - - kkk = kk&mask; - if ((l = mark[kkk]) == -1) { - ind[m] = kk; - wgt[m] = 1; - mark[kkk] = m++; - } - else if (ind[l] == kk) { - wgt[l]++; - } - else { - for (jj=0; jj= mgcnum[etype[i]][etype[ind[j]]]) { - k = ind[j]; - dadjncy[dxadj[i]++] = k; - dadjncy[dxadj[k]++] = i; - } - mark[ind[j]&mask] = -1; - } - } - - /* Go and consolidate the dxadj and dadjncy */ - for (j=i=0; i<*ne; i++) { - for (k=mhash[i]; k0; i--) - dxadj[i] = dxadj[i-1]; - dxadj[0] = 0; - - gk_free((void **)&mark, LTERM); - gk_free((void **)&nptr, LTERM); - gk_free((void **)&nind, LTERM); - gk_free((void **)&hash, LTERM); - - if (*numflag == 1) - ChangeMesh2FNumbering(tot, elmnts, *nn, dxadj, dadjncy); - - -} - - - - - - -/***************************************************************************** -* This function creates a graph corresponding to the finite element mesh. -* At this point the supported elements are triangles, tetrahedrons. -******************************************************************************/ -void METIS_MeshToNodal(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, -idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - idxtype esizes[] = {-1, 3, 4, 8, 4, 2}; - - if (*numflag == 1) - ChangeMesh2CNumbering((*ne)*esizes[*etype], elmnts); - - switch (*etype) { - case 1: - TRINODALMETIS(*ne, *nn, elmnts, dxadj, dadjncy); - break; - case 2: - TETNODALMETIS(*ne, *nn, elmnts, dxadj, dadjncy); - break; - case 3: - HEXNODALMETIS(*ne, *nn, elmnts, dxadj, dadjncy); - break; - case 4: - QUADNODALMETIS(*ne, *nn, elmnts, dxadj, dadjncy); - break; - case 5: - LINENODALMETIS(*ne, *nn, elmnts, dxadj, dadjncy); - break; - } - - if (*numflag == 1) - ChangeMesh2FNumbering((*ne)*esizes[*etype], elmnts, *nn, dxadj, dadjncy); -} - - -/***************************************************************************** -* This function creates a graph corresponding to the finite mixed element mesh. -* At this point the supported elements are triangles, tetrahedrons, hexahedras, -* quadilaterals and lines. -******************************************************************************/ -void METIS_MixedMeshToNodal(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, -idxtype *numflag, idxtype *dxadj, idxtype *dadjncy) -{ - idxtype sizes[] = {-1, 3,4,8,4,2}; - idxtype i, j, jj, k, kk, kkk, l, m, n, nedges; - idxtype *nptr, *nind; - idxtype *mark; - idxtype *hash; - idxtype tableH[8][3] = {1, 3, 4, - 0, 2, 5, - 1, 3, 6, - 0, 2, 7, - 0, 5, 7, - 1, 4, 6, - 2, 5, 7, - 3, 4, 6}; - - idxtype tableQ[4][2] = {1, 3, - 0, 2, - 1, 3, - 0, 2}; - - - - - hash = idxsmalloc((*ne), 0, "MXNODALMETIS: hash"); - m=0; - for (i=0;i<(*ne);i++){ - hash[i]=m; - m+=sizes[etype[i]]; - } - - if (*numflag == 1) - ChangeMesh2CNumbering(m, elmnts); - - - - /* Construct the node-element list first */ - nptr = idxsmalloc((*nn)+1, 0, "MXNODALMETIS: nptr"); - - for(i=0;i<(*ne);i++){ - l=hash[i]; - for (j=(sizes[etype[i]]), k=0; k0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - mark = idxsmalloc(*nn, -1, "MXNODALMETIS: mark"); - - nedges = dxadj[0] = 0; - for (i=0; i<(*nn); i++) { - mark[i] = i; - dxadj[i+1]=dxadj[i]; - for (j=nptr[i]; j0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - for (i=0; i=nptr[n]; k--) { - if ((kk = nind[k]) <= i) - break; - - kkk = kk&mask; - if ((l = mark[kkk]) == -1) { - ind[m] = kk; - wgt[m] = 1; - mark[kkk] = m++; - } - else if (ind[l] == kk) { - wgt[l]++; - } - else { - for (jj=0; jj0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - mhash = idxsmalloc(nelmnts, 0, "MXNODALMETIS: hash"); - - dxadj[0]=0; - for (i=1; i=nptr[n]; k--) { - if ((kk = nind[k]) <= i) - break; - - kkk = kk&mask; - if ((l = mark[kkk]) == -1) { - ind[m] = kk; - wgt[m] = 1; - mark[kkk] = m++; - } - else if (ind[l] == kk) { - wgt[l]++; - } - else { - for (jj=0; jj0; i--) - dxadj[i] = dxadj[i-1]; - dxadj[0] = 0; - - gk_free((void **)&mark, LTERM); - gk_free((void **)&nptr, LTERM); - gk_free((void **)&nind, LTERM); - gk_free((void **)&mhash, LTERM); - - -} - - - - -/***************************************************************************** -* This function creates the nodal graph of a finite element mesh -******************************************************************************/ -void TRINODALMETIS(idxtype nelmnts, idxtype nvtxs, idxtype *elmnts, idxtype *dxadj, idxtype *dadjncy) -{ - idxtype i, j, jj, k, kk, kkk, l, m, n, nedges; - idxtype *nptr, *nind; - idxtype *mark; - - /* Construct the node-element list first */ - nptr = idxsmalloc(nvtxs+1, 0, "TRINODALMETIS: nptr"); - for (j=3*nelmnts, i=0; i0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - mark = idxsmalloc(nvtxs, -1, "TRINODALMETIS: mark"); - - nedges = dxadj[0] = 0; - for (i=0; i0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - mark = idxsmalloc(nvtxs, -1, "TETNODALMETIS: mark"); - - nedges = dxadj[0] = 0; - for (i=0; i0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - mark = idxsmalloc(nvtxs, -1, "HEXNODALMETIS: mark"); - - nedges = dxadj[0] = 0; - for (i=0; i0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - mark = idxsmalloc(nvtxs, -1, "QUADNODALMETIS: mark"); - - nedges = dxadj[0] = 0; - for (i=0; i0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - mark = idxsmalloc(nvtxs, -1, "TRINODALMETIS: mark"); - - nedges = dxadj[0] = 0; - for (i=0; i - - -/************************************************************************* -* This function partitions a finite element mesh by partitioning its nodal -* graph using KMETIS and then assigning elements in a load balanced fashion. -**************************************************************************/ -void METIS_PartMeshNodal(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, -idxtype *numflag, idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart) - -{ - idxtype i, j, k, me; - idxtype *xadj, *adjncy, *pwgts; - idxtype options[10], pnumflag=0, wgtflag=0; - idxtype nnbrs, nbrind[200], nbrwgt[200], maxpwgt; - idxtype esize, esizes[] = {-1, 3, 4, 8, 4, 2}; - - esize = esizes[*etype]; - - if (*numflag == 1) - ChangeMesh2CNumbering((*ne)*esize, elmnts); - - xadj = idxmalloc(*nn+1, "METIS_MESHPARTNODAL: xadj"); - adjncy = idxmalloc(20*(*nn), "METIS_MESHPARTNODAL: adjncy"); - - METIS_MeshToNodal(ne, nn, elmnts, etype, &pnumflag, xadj, adjncy); - - adjncy = realloc(adjncy, xadj[*nn]*sizeof(idxtype)); - - options[0] = 0; - METIS_PartGraphKway(nn, xadj, adjncy, NULL, NULL, &wgtflag, &pnumflag, nparts, options, edgecut, npart); - - /* OK, now compute an element partition based on the nodal partition npart */ - idxset(*ne, -1, epart); - pwgts = idxsmalloc(*nparts, 0, "METIS_MESHPARTNODAL: pwgts"); - for (i=0; i<*ne; i++) { - me = npart[elmnts[i*esize]]; - for (j=1; j0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - /* OK, now compute a nodal partition based on the element partition npart */ - idxset(*nn, -1, npart); - pwgts = idxsmalloc(*nparts, 0, "METIS_MESHPARTDUAL: pwgts"); - for (i=0; i<*nn; i++) { - me = epart[nind[nptr[i]]]; - for (j=nptr[i]+1; j0; i--) - nptr[i] = nptr[i-1]; - nptr[0] = 0; - - - /* OK, now compute a nodal partition based on the element partition npart */ - idxset(*nn, -1, npart); - pwgts = idxsmalloc(*nparts, 0, "METIS_MESHPARTDUAL: pwgts"); - for (i=0; i<*nn; i++) { - me = epart[nind[nptr[i]]]; - for (j=nptr[i]+1; j - #define __thread __declspec( thread ) - - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; -#else - #include - #include - #include -#endif - - - -/*------------------------------------------------------------------------ -* Undefine the following #define in order to use short idxtype as the idxtype -*-------------------------------------------------------------------------*/ -#if IDXTYPEWIDTH == 32 - #define SCNIDX SCNd32 - #define PRIIDX PRId32 - - typedef int32_t idxtype; -#elif IDXTYPEWIDTH == 64 - #define SCNIDX SCNd64 - #define PRIIDX PRId64 - - typedef int64_t idxtype; -#else - #error "Incorrect user-supplied value fo IDXTYPEWIDTH" -#endif - - - - - -/*------------------------------------------------------------------------ -* Function prototypes -*-------------------------------------------------------------------------*/ - -#if !defined(__cdecl) - #define __cdecl -#endif - - - -#ifdef __cplusplus -extern "C" { -#endif - -void __cdecl METIS_EstimateMemory(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, - idxtype *optype, idxtype *nbytes); - -void __cdecl METIS_PartGraphKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, - idxtype *edgecut, idxtype *part); - -void __cdecl METIS_WPartGraphKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, - idxtype *options, idxtype *edgecut, idxtype *part); - -void __cdecl METIS_PartGraphVKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, - idxtype *volume, idxtype *part); - -void __cdecl METIS_WPartGraphVKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *vsize, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, - idxtype *options, idxtype *volume, idxtype *part); - -idxtype __cdecl METIS_MeshToDualCount(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *elms, idxtype *etype, - idxtype *numflag); - -void __cdecl METIS_MeshToDual(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *elms, idxtype *etype, - idxtype *numflag, idxtype *dxadj, idxtype *dadjncy); - -idxtype __cdecl METIS_MixedMeshToDualCount(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype * elms, - idxtype *etype, idxtype *numflag, idxtype *conmat, idxtype custom); - -void __cdecl METIS_MixedMeshToDual(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *elms, - idxtype *etype, idxtype *numflag,idxtype *dxadj, idxtype *dadjncy,idxtype *conmat, - idxtype custom); - -void __cdecl METIS_MeshToNodal(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, - idxtype *dxadj, idxtype *dadjncy); - -void __cdecl METIS_MixedMeshToNodal(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, - idxtype *numflag, idxtype *dxadj, idxtype *dadjncy); - -void __cdecl METIS_PartMeshNodal(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, - idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart); - -void __cdecl METIS_PartMixedMeshNodal(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, - idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart); - -void __cdecl METIS_PartMeshDual(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, - idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart, idxtype wgtflag, - idxtype * vwgt); - -void __cdecl METIS_PartMixedMeshDual(idxtype *ne, idxtype *nn, idxtype *elmnts, idxtype *etype, idxtype *numflag, - idxtype *nparts, idxtype *edgecut, idxtype *epart, idxtype *npart, idxtype *conmat, - idxtype custom, idxtype wgtflag, idxtype *vwgt); - -void __cdecl METIS_mCPartGraphKway(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, - idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - float *rubvec, idxtype *options, idxtype *edgecut, idxtype *part); - -void __cdecl METIS_mCPartGraphRecursive(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, - idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - idxtype *options, idxtype *edgecut, idxtype *part); - -void __cdecl METIS_mCHPartGraphRecursive(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, - idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - float *ubvec, idxtype *options, idxtype *edgecut, idxtype *part); - -void __cdecl METIS_mCPartGraphRecursiveInternal(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, - idxtype *adjncy, float *nvwgt, idxtype *adjwgt, idxtype *nparts, idxtype *options, - idxtype *edgecut, idxtype *part); - -void __cdecl METIS_mCHPartGraphRecursiveInternal(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, - idxtype *adjncy, float *nvwgt, idxtype *adjwgt, idxtype *nparts, float *ubvec, - idxtype *options, idxtype *edgecut, idxtype *part); - -void __cdecl METIS_EdgeND(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, - idxtype *perm, idxtype *iperm); - -void __cdecl METIS_NodeND(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, - idxtype *perm, idxtype *iperm); - - -void __cdecl METIS_NodeWND(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *numflag, - idxtype *options, idxtype *perm, idxtype *iperm); - -void __cdecl METIS_PartGraphKway2(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, - idxtype *edgecut, idxtype *part); - -void __cdecl METIS_WPartGraphKway2(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, - idxtype *options, idxtype *edgecut, idxtype *part); - -void __cdecl METIS_NodeNDP(idxtype nvtxs, idxtype *xadj, idxtype *adjncy, idxtype npes, idxtype *options, - idxtype *perm, idxtype *iperm, idxtype *sizes); - -void __cdecl METIS_NodeComputeSeparator(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *options, idxtype *sepsize, idxtype *part); - -void __cdecl METIS_EdgeComputeSeparator(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *options, idxtype *sepsize, idxtype *part); - -void __cdecl METIS_PartGraphRecursive(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, - idxtype *edgecut, idxtype *part); - -void __cdecl METIS_WPartGraphRecursive(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, float *tpwgts, - idxtype *options, idxtype *edgecut, idxtype *part); - -void __cdecl METIS_PartFillGraph(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, idxtype *options, - idxtype *edgecut, idxtype *part); - - -#ifdef __cplusplus -} -#endif - - - - -/*------------------------------------------------------------------------ -* Constant definitions -*-------------------------------------------------------------------------*/ - -/* Matching Schemes */ -#define MTYPE_RM 1 -#define MTYPE_HEM 2 -#define MTYPE_SHEM 3 -#define MTYPE_SHEMKWAY 4 -#define MTYPE_SHEBM_ONENORM 5 -#define MTYPE_SHEBM_INFNORM 6 -#define MTYPE_SBHEM_ONENORM 7 -#define MTYPE_SBHEM_INFNORM 8 - -/* Initial partitioning schemes for PMETIS and ONMETIS */ -#define ITYPE_GGPKL 1 -#define ITYPE_GGPKLNODE 2 -#define ITYPE_RANDOM 2 - -/* Refinement schemes for PMETIS */ -#define RTYPE_FM 1 - -/* Initial partitioning schemes for KMETIS */ -#define ITYPE_PMETIS 1 - -/* Refinement schemes for KMETIS */ -#define RTYPE_KWAYRANDOM 1 -#define RTYPE_KWAYGREEDY 2 -#define RTYPE_KWAYRANDOM_MCONN 3 - -/* Refinement schemes for ONMETIS */ -#define RTYPE_SEP2SIDED 1 -#define RTYPE_SEP1SIDED 2 - -/* Initial Partitioning Schemes for McKMETIS */ -#define ITYPE_McPMETIS 1 /* Simple McPMETIS */ -#define ITYPE_McHPMETIS 2 /* horizontally relaxed McPMETIS */ - - -/* Debug Levels */ -#define DBG_TIME 1 /* Perform timing analysis */ -#define DBG_OUTPUT 2 -#define DBG_COARSEN 4 /* Show the coarsening progress */ -#define DBG_REFINE 8 /* Show info on communication during folding */ -#define DBG_IPART 16 /* Show info on initial partition */ -#define DBG_MOVEINFO 32 /* Show info on communication during folding */ -#define DBG_KWAYPINFO 64 /* Show info on communication during folding */ -#define DBG_SEPINFO 128 /* Show info on communication during folding */ - - -/* Metis's version number */ -#define METIS_VER_MAJOR 5 -#define METIS_VER_MINOR 0 -#define METIS_VER_SUBMINOR 0 - - - -#endif /* METIS_H */ diff --git a/src/metis/metislib.h b/src/metis/metislib.h deleted file mode 100644 index 8757525..0000000 --- a/src/metis/metislib.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * metis.h - * - * This file includes all necessary header files - * - * Started 8/27/94 - * George - * - * $Id: metislib.h,v 1.1 2002/08/10 06:29:31 karypis Exp $ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(ENABLE_OPENMP) - #include -#endif - - -#include - -#include -#include -#include -#include -#include - - -#if defined(COMPILER_MSC) -#define rint(x) ((idxtype)((x)+0.5)) /* MSC does not have rint() function */ -#endif - - -#if defined(COMPILER_GCC) -/* extern char* strdup (const char *); */ -#endif - diff --git a/src/metis/mfm.c b/src/metis/mfm.c deleted file mode 100644 index b7411f0..0000000 --- a/src/metis/mfm.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * mfm.c - * - * This file contains code that implements the edge-based FM refinement - * - * Started 7/23/97 - * George - * - * $Id: mfm.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - */ - -#include - - -/************************************************************************* -* This function performs an edge-based FM refinement -**************************************************************************/ -void MocFM_2WayEdgeRefine(CtrlType *ctrl, GraphType *graph, float *tpwgts, idxtype npasses) -{ - idxtype i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, me, limit, tmp, cnum; - idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind; - idxtype *moved, *swaps, *perm, *qnum; - float *nvwgt, *npwgts, mindiff[MAXNCON], origbal, minbal, newbal; - PQueueType parts[MAXNCON][2]; - idxtype higain, oldgain, mincut, initcut, newcut, mincutorder; - float rtpwgts[2]; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - id = graph->id; - ed = graph->ed; - npwgts = graph->npwgts; - bndptr = graph->bndptr; - bndind = graph->bndind; - - moved = idxwspacemalloc(ctrl, nvtxs); - swaps = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - qnum = idxwspacemalloc(ctrl, nvtxs); - - limit = amin(amax(0.01*nvtxs, 25), 150); - - /* Initialize the queues */ - for (i=0; idbglvl&DBG_REFINE) { - mprintf("Parts: ["); - for (l=0; lnvtxs, graph->nbnd, graph->mincut, origbal); - } - - idxset(nvtxs, -1, moved); - for (pass=0; passmincut; - for (i=0; imincut); - ASSERT(CheckBnd(graph)); - - /* Insert boundary nodes in the priority queues */ - nbnd = graph->nbnd; - RandomPermute(nbnd, perm, 1); - for (ii=0; ii 0 || id[i] == 0); - ASSERT(bndptr[i] != -1); - PQueueInsert(&parts[qnum[i]][where[i]], i, ed[i]-id[i]); - } - - for (nswaps=0; nswaps limit) { /* We hit the limit, undo last move */ - newcut += (ed[higain]-id[higain]); - gk_faxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1); - break; - } - - where[higain] = to; - moved[higain] = nswaps; - swaps[nswaps] = higain; - - if (ctrl->dbglvl&DBG_MOVEINFO) { - mprintf("Moved %6D from %D(%D). Gain: %5D, Cut: %5D, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut); - for (l=0; l 0) { /* It will now become a boundary vertex */ - BNDInsert(nbnd, bndind, bndptr, k); - if (moved[k] == -1) - PQueueInsert(&parts[qnum[k]][where[k]], k, ed[k]-id[k]); - } - } - } - - } - - - /**************************************************************** - * Roll back computations - *****************************************************************/ - for (i=0; imincutorder; nswaps--) { - higain = swaps[nswaps]; - - to = where[higain] = (where[higain]+1)%2; - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - else if (ed[higain] > 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - gk_faxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+((to+1)%2)*ncon, 1); - for (j=xadj[higain]; j 0) - BNDInsert(nbnd, bndind, bndptr, k); - } - } - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("\tMincut: %6D at %5D, NBND: %6D, NPwgts: [", mincut, mincutorder, nbnd); - for (l=0; lmincut = mincut; - graph->nbnd = nbnd; - - if (mincutorder == -1 || mincut == initcut) - break; - } - - for (i=0; i= maxdiff) { - maxdiff = npwgts[part*ncon+i]-tpwgts[part]; - *from = part; - *cnum = i; - } - } - } - - /* mprintf("Selected1 %D(%D) -> %D [%5f]\n", *from, *cnum, PQueueGetSize(&queues[*cnum][*from]), maxdiff); */ - - if (*from != -1 && PQueueGetSize(&queues[*cnum][*from]) == 0) { - /* The desired queue is empty, select a node from that side anyway */ - for (i=0; i 0) { - max = npwgts[(*from)*ncon + i]; - *cnum = i; - break; - } - } - - for (i++; i max && PQueueGetSize(&queues[i][*from]) > 0) { - max = npwgts[(*from)*ncon + i]; - *cnum = i; - } - } - } - - /* Check to see if you can focus on the cut */ - if (maxdiff <= 0.0 || *from == -1) { - maxgain = -100000; - - for (part=0; part<2; part++) { - for (i=0; i 0 && PQueueGetKey(&queues[i][part]) > maxgain) { - maxgain = PQueueGetKey(&queues[i][part]); - *from = part; - *cnum = i; - } - } - } - } - - /* mprintf("Selected2 %D(%D) -> %D\n", *from, *cnum, PQueueGetSize(&queues[*cnum][*from])); */ -} - - - - - -/************************************************************************* -* This function checks if the balance achieved is better than the diff -* For now, it uses a 2-norm measure -**************************************************************************/ -idxtype BetterBalance(idxtype ncon, float *npwgts, float *tpwgts, float *diff) -{ - idxtype i; - float ndiff[MAXNCON]; - - for (i=0; i - - -/************************************************************************* -* This function performs an edge-based FM refinement -**************************************************************************/ -void MocFM_2WayEdgeRefine2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *orgubvec, - idxtype npasses) -{ - idxtype i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, me, limit, tmp, cnum; - idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind; - idxtype *moved, *swaps, *perm, *qnum; - float *nvwgt, *npwgts, origdiff[MAXNCON], origbal[MAXNCON], minbal[MAXNCON]; - PQueueType parts[MAXNCON][2]; - idxtype higain, oldgain, mincut, initcut, newcut, mincutorder; - float *maxwgt, *minwgt, ubvec[MAXNCON], tvec[MAXNCON]; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - id = graph->id; - ed = graph->ed; - npwgts = graph->npwgts; - bndptr = graph->bndptr; - bndind = graph->bndind; - - moved = idxwspacemalloc(ctrl, nvtxs); - swaps = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - qnum = idxwspacemalloc(ctrl, nvtxs); - - limit = amin(amax(0.01*nvtxs, 15), 100); - - Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, origbal); - for (i=0; idbglvl&DBG_REFINE) { - mprintf("Parts: ["); - for (l=0; lnvtxs, graph->nbnd, graph->mincut); - for (i=0; imincut; - Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, minbal); - - ASSERT(ComputeCut(graph, where) == graph->mincut); - ASSERT(CheckBnd(graph)); - - /* Insert boundary nodes in the priority queues */ - nbnd = graph->nbnd; - RandomPermute(nbnd, perm, 1); - for (ii=0; ii 0 || id[i] == 0); - ASSERT(bndptr[i] != -1); - PQueueInsert(&parts[qnum[i]][where[i]], i, ed[i]-id[i]); - } - - for (nswaps=0; nswaps limit) { /* We hit the limit, undo last move */ - newcut += (ed[higain]-id[higain]); - gk_faxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1); - break; - } - - where[higain] = to; - moved[higain] = nswaps; - swaps[nswaps] = higain; - - if (ctrl->dbglvl&DBG_MOVEINFO) { - mprintf("Moved %6D from %D(%D). Gain: %5D, Cut: %5D, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut); - for (l=0; l 0) { /* It will now become a boundary vertex */ - BNDInsert(nbnd, bndind, bndptr, k); - if (moved[k] == -1) - PQueueInsert(&parts[qnum[k]][where[k]], k, ed[k]-id[k]); - } - } - } - - } - - - /**************************************************************** - * Roll back computations - *****************************************************************/ - for (i=0; imincutorder; nswaps--) { - higain = swaps[nswaps]; - - to = where[higain] = (where[higain]+1)%2; - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - else if (ed[higain] > 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - gk_faxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+((to+1)%2)*ncon, 1); - for (j=xadj[higain]; j 0) - BNDInsert(nbnd, bndind, bndptr, k); - } - } - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("\tMincut: %6D at %5D, NBND: %6D, NPwgts: [", mincut, mincutorder, nbnd); - for (l=0; lmincut = mincut; - graph->nbnd = nbnd; - - if (mincutorder == -1 || mincut == initcut) - break; - } - - for (i=0; i= maxdiff) { - maxdiff = diff; - *from = j; - *cnum = i; - } - } - } - - if (*from != -1 && PQueueGetSize(&queues[*cnum][*from]) == 0) { - /* The desired queue is empty, select a node from that side anyway */ - for (i=0; i 0) { - max = (npwgts[(*from)*ncon+i] - maxwgt[(*from)*ncon+i]); - *cnum = i; - break; - } - } - - for (i++; i max && PQueueGetSize(&queues[i][*from]) > 0) { - max = diff; - *cnum = i; - } - } - } - - /* Check to see if you can focus on the cut */ - if (maxdiff <= 0.0 || *from == -1) { - maxgain = -100000; - - for (j=0; j<2; j++) { - for (i=0; i 0 && PQueueGetKey(&queues[i][j]) > maxgain) { - maxgain = PQueueGetKey(&queues[i][j]); - *from = j; - *cnum = i; - } - } - } - - /* mprintf("(%2D %2D) %3D\n", *from, *cnum, maxgain); */ - } -} - - -/************************************************************************* -* This function checks if the newbal is better than oldbal given the -* ubvector ubvec -**************************************************************************/ -idxtype IsBetter2wayBalance(idxtype ncon, float *newbal, float *oldbal, float *ubvec) -{ - idxtype i, j; - float max1=0.0, max2=0.0, sum1=0.0, sum2=0.0, tmp; - - for (i=0; i max2) - return 0; - else - return sum1 <= sum2; -} - - diff --git a/src/metis/minitpart.c b/src/metis/minitpart.c deleted file mode 100644 index 9ec133e..0000000 --- a/src/metis/minitpart.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * minitpart.c - * - * This file contains code that performs the initial partition of the - * coarsest graph - * - * Started 7/23/97 - * George - * - * $Id: minitpart.c,v 1.4 2003/07/31 16:14:40 karypis Exp $ - * - */ - -#include - -/************************************************************************* -* This function computes the initial bisection of the coarsest graph -**************************************************************************/ -void MocInit2WayPartition(CtrlType *ctrl, GraphType *graph, float *tpwgts, float ubfactor) -{ - idxtype i, dbglvl; - - dbglvl = ctrl->dbglvl; - IFSET(ctrl->dbglvl, DBG_REFINE, ctrl->dbglvl -= DBG_REFINE); - IFSET(ctrl->dbglvl, DBG_MOVEINFO, ctrl->dbglvl -= DBG_MOVEINFO); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); - - switch (ctrl->IType) { - case ITYPE_GGPKL: - if (graph->nedges == 0) - MocRandomBisection(ctrl, graph, tpwgts, ubfactor); - else - MocGrowBisection(ctrl, graph, tpwgts, ubfactor); - break; - case ITYPE_RANDOM: - MocRandomBisection(ctrl, graph, tpwgts, ubfactor); - break; - default: - errexit("Unknown initial partition type: %d\n", ctrl->IType); - } - - IFSET(ctrl->dbglvl, DBG_IPART, mprintf("Initial Cut: %D [%D]\n", graph->mincut, graph->where[0])); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); - ctrl->dbglvl = dbglvl; - -} - - - - - -/************************************************************************* -* This function takes a graph and produces a bisection by using a region -* growing algorithm. The resulting partition is returned in -* graph->where -**************************************************************************/ -void MocGrowBisection(CtrlType *ctrl, GraphType *graph, float *tpwgts, float ubfactor) -{ - idxtype i, j, k, nvtxs, ncon, from, bestcut, mincut, nbfs, inbfs; - idxtype *bestwhere, *where; - - nvtxs = graph->nvtxs; - - MocAllocate2WayPartitionMemory(ctrl, graph); - where = graph->where; - - bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere"); - nbfs = 2*(nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS); - - for (inbfs=0; inbfs= graph->mincut) { - bestcut = graph->mincut; - idxcopy(nvtxs, where, bestwhere); - if (bestcut == 0) - break; - } - } - - graph->mincut = bestcut; - idxcopy(nvtxs, bestwhere, where); - - gk_free((void **)&bestwhere, LTERM); -} - - - -/************************************************************************* -* This function takes a graph and produces a bisection by using a region -* growing algorithm. The resulting partition is returned in -* graph->where -**************************************************************************/ -void MocRandomBisection(CtrlType *ctrl, GraphType *graph, float *tpwgts, float ubfactor) -{ - idxtype i, ii, j, k, nvtxs, ncon, from, bestcut, mincut, nbfs, inbfs, qnum; - idxtype *bestwhere, *where, *perm; - idxtype counts[MAXNCON]; - float *nvwgt; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - nvwgt = graph->nvwgt; - - MocAllocate2WayPartitionMemory(ctrl, graph); - where = graph->where; - - bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere"); - nbfs = 2*(nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS); - perm = idxmalloc(nvtxs, "BisectGraph: perm"); - - for (inbfs=0; inbfsmincut); - for (i=0; incon; i++) - mprintf("(%.3f %.3f) ", graph->npwgts[i], graph->npwgts[graph->ncon+i]); - mprintf("]\n"); - */ - - if (inbfs == 0 || bestcut >= graph->mincut) { - bestcut = graph->mincut; - idxcopy(nvtxs, where, bestwhere); - if (bestcut == 0) - break; - } - } - - graph->mincut = bestcut; - idxcopy(nvtxs, bestwhere, where); - - gk_free((void **)&bestwhere, &perm, LTERM); -} - - - - -/************************************************************************* -* This function balances two partitions by moving the highest gain -* (including negative gain) vertices to the other domain. -* It is used only when tha unbalance is due to non contigous -* subdomains. That is, the are no boundary vertices. -* It moves vertices from the domain that is overweight to the one that -* is underweight. -**************************************************************************/ -void MocInit2WayBalance(CtrlType *ctrl, GraphType *graph, float *tpwgts) -{ - idxtype i, ii, j, k, l, kwgt, nvtxs, nbnd, ncon, nswaps, from, to, pass, me, cnum, tmp; - idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind; - idxtype *perm, *qnum; - float *nvwgt, *npwgts; - PQueueType parts[MAXNCON][2]; - idxtype higain, oldgain, mincut; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - adjncy = graph->adjncy; - nvwgt = graph->nvwgt; - adjwgt = graph->adjwgt; - where = graph->where; - id = graph->id; - ed = graph->ed; - npwgts = graph->npwgts; - bndptr = graph->bndptr; - bndind = graph->bndind; - - perm = idxwspacemalloc(ctrl, nvtxs); - qnum = idxwspacemalloc(ctrl, nvtxs); - - /* This is called for initial partitioning so we know from where to pick nodes */ - from = 1; - to = (from+1)%2; - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("Parts: ["); - for (l=0; lnvtxs, graph->nbnd, graph->mincut, - Compute2WayHLoadImbalance(ncon, npwgts, tpwgts)); - } - - for (i=0; imincut); - ASSERT(CheckBnd(graph)); - ASSERT(CheckGraph(graph)); - - /* Compute the queues in which each vertex will be assigned to */ - for (i=0; i 0) - PQueueInsert(&parts[qnum[i]][0], i, ed[i]-id[i]); - else - PQueueInsert(&parts[qnum[i]][1], i, ed[i]-id[i]); - } - } - - - mincut = graph->mincut; - nbnd = graph->nbnd; - for (nswaps=0; nswapsdbglvl&DBG_MOVEINFO) { - mprintf("Moved %6D from %D(%D). [%5D] %5D, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], mincut); - for (l=0; l 0) - mprintf("\t Pulled from the interior!\n"); - } - - - /************************************************************** - * Update the id[i]/ed[i] values of the affected nodes - ***************************************************************/ - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - if (ed[higain] > 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - for (j=xadj[higain]; j 0 && bndptr[k] == -1) { /* It moves in boundary */ - PQueueDelete(&parts[qnum[k]][1], k, oldgain); - PQueueInsert(&parts[qnum[k]][0], k, ed[k]-id[k]); - } - else { /* It must be in the boundary already */ - if (bndptr[k] == -1) - mprintf("What you thought was wrong!\n"); - PQueueUpdate(&parts[qnum[k]][0], k, oldgain, ed[k]-id[k]); - } - } - - /* Update its boundary information */ - if (ed[k] == 0 && bndptr[k] != -1) - BNDDelete(nbnd, bndind, bndptr, k); - else if (ed[k] > 0 && bndptr[k] == -1) - BNDInsert(nbnd, bndind, bndptr, k); - } - - ASSERTP(ComputeCut(graph, where) == mincut, ("%d != %d\n", ComputeCut(graph, where), mincut)); - - } - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("\tMincut: %6D, NBND: %6D, NPwgts: ", mincut, nbnd); - for (l=0; lmincut = mincut; - graph->nbnd = nbnd; - - for (i=0; imincut); - ASSERT(CheckBnd(graph)); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - - -/************************************************************************* -* This function selects the partition number and the queue from which -* we will move vertices out -**************************************************************************/ -idxtype SelectQueueOneWay(idxtype ncon, float *npwgts, float *tpwgts, idxtype from, PQueueType queues[MAXNCON][2]) -{ - idxtype i, cnum=-1; - float max=0.0; - - for (i=0; i= max && - PQueueGetSize(&queues[i][0]) + PQueueGetSize(&queues[i][1]) > 0) { - max = npwgts[from*ncon+i]-tpwgts[0]; - cnum = i; - } - } - - return cnum; -} - - diff --git a/src/metis/minitpart2.c b/src/metis/minitpart2.c deleted file mode 100644 index dce62af..0000000 --- a/src/metis/minitpart2.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * minitpart2.c - * - * This file contains code that performs the initial partition of the - * coarsest graph - * - * Started 7/23/97 - * George - * - * $Id: minitpart2.c,v 1.3 2002/08/10 06:57:50 karypis Exp $ - * - */ - -#include - -/************************************************************************* -* This function computes the initial bisection of the coarsest graph -**************************************************************************/ -void MocInit2WayPartition2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec) -{ - idxtype dbglvl; - - dbglvl = ctrl->dbglvl; - IFSET(ctrl->dbglvl, DBG_REFINE, ctrl->dbglvl -= DBG_REFINE); - IFSET(ctrl->dbglvl, DBG_MOVEINFO, ctrl->dbglvl -= DBG_MOVEINFO); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); - - switch (ctrl->IType) { - case ITYPE_GGPKL: - case ITYPE_RANDOM: - MocGrowBisection2(ctrl, graph, tpwgts, ubvec); - break; - case 3: - MocGrowBisectionNew2(ctrl, graph, tpwgts, ubvec); - break; - default: - errexit("Unknown initial partition type: %d\n", ctrl->IType); - } - - IFSET(ctrl->dbglvl, DBG_IPART, mprintf("Initial Cut: %D\n", graph->mincut)); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); - ctrl->dbglvl = dbglvl; - -} - - - - -/************************************************************************* -* This function takes a graph and produces a bisection by using a region -* growing algorithm. The resulting partition is returned in -* graph->where -**************************************************************************/ -void MocGrowBisection2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec) -{ - idxtype i, j, k, nvtxs, ncon, from, bestcut, mincut, nbfs, inbfs; - idxtype *bestwhere, *where; - - nvtxs = graph->nvtxs; - - MocAllocate2WayPartitionMemory(ctrl, graph); - where = graph->where; - - bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere"); - nbfs = 2*(nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS); - - for (inbfs=0; inbfs graph->mincut) { - bestcut = graph->mincut; - idxcopy(nvtxs, where, bestwhere); - if (bestcut == 0) - break; - } - } - - graph->mincut = bestcut; - idxcopy(nvtxs, bestwhere, where); - - gk_free((void **)&bestwhere, LTERM); -} - - - - - - -/************************************************************************* -* This function takes a graph and produces a bisection by using a region -* growing algorithm. The resulting partition is returned in -* graph->where -**************************************************************************/ -void MocGrowBisectionNew2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec) -{ - idxtype i, j, k, nvtxs, ncon, from, bestcut, mincut, nbfs, inbfs; - idxtype *bestwhere, *where; - - nvtxs = graph->nvtxs; - - MocAllocate2WayPartitionMemory(ctrl, graph); - where = graph->where; - - bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere"); - nbfs = 2*(nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS); - - for (inbfs=0; inbfs graph->mincut) { - bestcut = graph->mincut; - idxcopy(nvtxs, where, bestwhere); - if (bestcut == 0) - break; - } - } - - graph->mincut = bestcut; - idxcopy(nvtxs, bestwhere, where); - - gk_free((void **)&bestwhere, LTERM); -} - - - -/************************************************************************* -* This function balances two partitions by moving the highest gain -* (including negative gain) vertices to the other domain. -* It is used only when tha unbalance is due to non contigous -* subdomains. That is, the are no boundary vertices. -* It moves vertices from the domain that is overweight to the one that -* is underweight. -**************************************************************************/ -void MocInit2WayBalance2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec) -{ - idxtype i, ii, j, k, l, kwgt, nvtxs, nbnd, ncon, nswaps, from, to, pass, me, cnum, tmp, imin; - idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind; - idxtype *moved, *perm, *qnum; - float *nvwgt, *npwgts, minwgt; - PQueueType parts[MAXNCON][2]; - idxtype higain, oldgain, mincut; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - adjncy = graph->adjncy; - nvwgt = graph->nvwgt; - adjwgt = graph->adjwgt; - where = graph->where; - id = graph->id; - ed = graph->ed; - npwgts = graph->npwgts; - bndptr = graph->bndptr; - bndind = graph->bndind; - - moved = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - qnum = idxwspacemalloc(ctrl, nvtxs); - - /* This is called for initial partitioning so we know from where to pick nodes */ - from = 1; - to = (from+1)%2; - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("Parts: ["); - for (l=0; lnvtxs, graph->nbnd, graph->mincut, ComputeLoadImbalance(ncon, 2, npwgts, tpwgts)); - } - - for (i=0; imincut); - ASSERT(CheckBnd(graph)); - ASSERT(CheckGraph(graph)); - - /* Compute the queues in which each vertex will be assigned to */ - for (i=0; i 0) - PQueueInsert(&parts[qnum[i]][0], i, ed[i]-id[i]); - else - PQueueInsert(&parts[qnum[i]][1], i, ed[i]-id[i]); - } - } - -/* - for (i=0; imincut; - nbnd = graph->nbnd; - for (nswaps=0; nswaps minwgt) - break; - - if ((cnum = SelectQueueOneWay2(ncon, npwgts+to*ncon, parts, ubvec)) == -1) - break; - - if ((higain = PQueueGetMax(&parts[cnum][0])) == -1) - higain = PQueueGetMax(&parts[cnum][1]); - - mincut -= (ed[higain]-id[higain]); - gk_faxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1); - - where[higain] = to; - moved[higain] = nswaps; - - if (ctrl->dbglvl&DBG_MOVEINFO) { - mprintf("Moved %6D from %D(%D). [%5D] %5D, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], mincut); - for (l=0; l 0) - mprintf("\t Pulled from the interior!\n"); - } - - - /************************************************************** - * Update the id[i]/ed[i] values of the affected nodes - ***************************************************************/ - SWAP(id[higain], ed[higain], tmp); - if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) - BNDDelete(nbnd, bndind, bndptr, higain); - if (ed[higain] > 0 && bndptr[higain] == -1) - BNDInsert(nbnd, bndind, bndptr, higain); - - for (j=xadj[higain]; j 0 && bndptr[k] == -1) { /* It moves in boundary */ - PQueueDelete(&parts[qnum[k]][1], k, oldgain); - PQueueInsert(&parts[qnum[k]][0], k, ed[k]-id[k]); - } - else { /* It must be in the boundary already */ - if (bndptr[k] == -1) - mprintf("What you thought was wrong!\n"); - PQueueUpdate(&parts[qnum[k]][0], k, oldgain, ed[k]-id[k]); - } - } - - /* Update its boundary information */ - if (ed[k] == 0 && bndptr[k] != -1) - BNDDelete(nbnd, bndind, bndptr, k); - else if (ed[k] > 0 && bndptr[k] == -1) - BNDInsert(nbnd, bndind, bndptr, k); - } - - ASSERTP(ComputeCut(graph, where) == mincut, ("%d != %d\n", ComputeCut(graph, where), mincut)); - - } - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("\tMincut: %6D, NBND: %6D, NPwgts: ", mincut, nbnd); - for (l=0; lmincut = mincut; - graph->nbnd = nbnd; - - for (i=0; imincut); - ASSERT(CheckBnd(graph)); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function selects the partition number and the queue from which -* we will move vertices out -**************************************************************************/ -idxtype SelectQueueOneWay2(idxtype ncon, float *pto, PQueueType queues[MAXNCON][2], float *ubvec) -{ - idxtype i, cnum=-1, imax, maxgain; - float max=0.0; - float twgt[MAXNCON]; - - for (i=0; i 0 || PQueueGetSize(&queues[i][1]) > 0)) { - max = twgt[i]; - cnum = i; - } - } - if (max > 1) - return cnum; - - /* optimize of cut */ - maxgain = -10000000; - for (i=0; i 0 && PQueueGetKey(&queues[i][0]) > maxgain) { - maxgain = PQueueGetKey(&queues[i][0]); - cnum = i; - } - } - - return cnum; - -} - diff --git a/src/metis/mkmetis.c b/src/metis/mkmetis.c deleted file mode 100644 index b5bc37c..0000000 --- a/src/metis/mkmetis.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * mkmetis.c - * - * This file contains the top level routines for the multilevel k-way partitioning - * algorithm KMETIS. - * - * Started 7/28/97 - * George - * - * $Id: mkmetis.c,v 1.3 2002/08/10 07:07:27 karypis Exp $ - * - */ - -#include - - - -/************************************************************************* -* This function is the entry point for KWMETIS -**************************************************************************/ -void METIS_mCPartGraphKway(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, - idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, - idxtype *nparts, float *rubvec, idxtype *options, idxtype *edgecut, - idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - SetUpGraph(&graph, OP_KMETIS, *nvtxs, *ncon, xadj, adjncy, vwgt, adjwgt, *wgtflag); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = McKMETIS_CTYPE; - ctrl.IType = McKMETIS_ITYPE; - ctrl.RType = McKMETIS_RTYPE; - ctrl.dbglvl = McKMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_KMETIS; - ctrl.CoarsenTo = amax((*nvtxs)/(20*gk_log2(*nparts)), 30*(*nparts)); - - ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo); - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MCMlevelKWayPartitioning(&ctrl, &graph, *nparts, part, rubvec); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); -} - - -/************************************************************************* -* This function takes a graph and produces a bisection of it -**************************************************************************/ -idxtype MCMlevelKWayPartitioning(CtrlType *ctrl, GraphType *graph, idxtype nparts, idxtype *part, - float *rubvec) -{ - idxtype i, j, nvtxs; - GraphType *cgraph; - idxtype options[10], edgecut; - - cgraph = MCCoarsen2Way(ctrl, graph); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); - MocAllocateKWayPartitionMemory(ctrl, cgraph, nparts); - - options[0] = 1; - options[OPTION_CTYPE] = MTYPE_SBHEM_INFNORM; - options[OPTION_ITYPE] = ITYPE_RANDOM; - options[OPTION_RTYPE] = RTYPE_FM; - options[OPTION_DBGLVL] = 0; - - /* Determine what you will use as the initial partitioner, based on tolerances */ - for (i=0; incon; i++) { - if (rubvec[i] > 1.2) - break; - } - if (i == graph->ncon) - METIS_mCPartGraphRecursiveInternal(&cgraph->nvtxs, &cgraph->ncon, - cgraph->xadj, cgraph->adjncy, cgraph->nvwgt, cgraph->adjwgt, &nparts, - options, &edgecut, cgraph->where); - else - METIS_mCHPartGraphRecursiveInternal(&cgraph->nvtxs, &cgraph->ncon, - cgraph->xadj, cgraph->adjncy, cgraph->nvwgt, cgraph->adjwgt, &nparts, - rubvec, options, &edgecut, cgraph->where); - - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->InitPartTmr)); - IFSET(ctrl->dbglvl, DBG_IPART, mprintf("Initial %D-way partitioning cut: %D\n", nparts, edgecut)); - - IFSET(ctrl->dbglvl, DBG_KWAYPINFO, ComputePartitionInfo(cgraph, nparts, cgraph->where)); - - MocRefineKWayHorizontal(ctrl, graph, cgraph, nparts, rubvec); - - idxcopy(graph->nvtxs, graph->where, part); - - FreeGraph(graph, 0); - - return graph->mincut; - -} - diff --git a/src/metis/mkwayfmh.c b/src/metis/mkwayfmh.c deleted file mode 100644 index ce4083c..0000000 --- a/src/metis/mkwayfmh.c +++ /dev/null @@ -1,677 +0,0 @@ -/* - * mkwayfmh.c - * - * This file contains code that implements the multilevel k-way refinement - * - * Started 7/28/97 - * George - * - * $Id: mkwayfmh.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - * - */ - -#include - - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void MCRandom_KWayEdgeRefineHorizontal(CtrlType *ctrl, GraphType *graph, idxtype nparts, - float *orgubvec, idxtype npasses) -{ - idxtype i, ii, iii, j, jj, k, l, pass, nvtxs, ncon, nmoves, nbnd, myndegrees, same; - idxtype from, me, to, oldcut, gain; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *perm, *bndptr, *bndind; - EDegreeType *myedegrees; - RInfoType *myrinfo; - float *npwgts, *nvwgt, *minwgt, *maxwgt, maxlb, minlb, ubvec[MAXNCON], tvec[MAXNCON]; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndptr = graph->bndptr; - bndind = graph->bndind; - - where = graph->where; - npwgts = graph->npwgts; - - /* Setup the weight intervals of the various subdomains */ - minwgt = fwspacemalloc(ctrl, nparts*ncon); - maxwgt = fwspacemalloc(ctrl, nparts*ncon); - - /* See if the orgubvec consists of identical constraints */ - maxlb = minlb = orgubvec[0]; - for (i=1; i maxlb ? orgubvec[i] : maxlb); - } - same = (fabs(maxlb-minlb) < .01 ? 1 : 0); - - - /* Let's not get very optimistic. Let Balancing do the work */ - ComputeHKWayLoadImbalance(ncon, nparts, npwgts, ubvec); - for (i=0; i maxlb ? ubvec[i] : maxlb); - - for (i=0; idbglvl&DBG_REFINE) { - mprintf("Partitions: [%5.4f %5.4f], Nv-Nb[%6D %6D]. Cut: %6D, LB: ", - npwgts[gk_fargmin(ncon*nparts, npwgts)], npwgts[gk_fargmax(ncon*nparts, npwgts)], - graph->nvtxs, graph->nbnd, graph->mincut); - ComputeHKWayLoadImbalance(ncon, nparts, npwgts, tvec); - for (i=0; imincut); - - oldcut = graph->mincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (nmoves=iii=0; iiinbnd; iii++) { - ii = perm[iii]; - if (ii >= nbnd) - continue; - i = bndind[ii]; - - myrinfo = graph->rinfo+i; - - if (myrinfo->ed >= myrinfo->id) { /* Total ED is too high */ - from = where[i]; - nvwgt = graph->nvwgt+i*ncon; - - if (myrinfo->id > 0 && AreAllHVwgtsBelow(ncon, 1.0, npwgts+from*ncon, -1.0, nvwgt, minwgt+from*ncon)) - continue; /* This cannot be moved! */ - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - for (k=0; kid; - if (gain >= 0 && - (AreAllHVwgtsBelow(ncon, 1.0, npwgts+to*ncon, 1.0, nvwgt, maxwgt+to*ncon) || - IsHBalanceBetterFT(ncon, nparts, npwgts+from*ncon, npwgts+to*ncon, nvwgt, ubvec))) - break; - } - if (k == myndegrees) - continue; /* break out if you did not find a candidate */ - - for (j=k+1; j myedegrees[k].ed && - (AreAllHVwgtsBelow(ncon, 1.0, npwgts+to*ncon, 1.0, nvwgt, maxwgt+to*ncon) || - IsHBalanceBetterFT(ncon, nparts, npwgts+from*ncon, npwgts+to*ncon, nvwgt, ubvec))) || - (myedegrees[j].ed == myedegrees[k].ed && - IsHBalanceBetterTT(ncon, nparts, npwgts+myedegrees[k].pid*ncon, npwgts+to*ncon, nvwgt, ubvec))) - k = j; - } - - to = myedegrees[k].pid; - - if (myedegrees[k].ed-myrinfo->id == 0 - && !IsHBalanceBetterFT(ncon, nparts, npwgts+from*ncon, npwgts+to*ncon, nvwgt, ubvec) - && AreAllHVwgtsBelow(ncon, 1.0, npwgts+from*ncon, 0.0, npwgts+from*ncon, maxwgt+from*ncon)) - continue; - - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - graph->mincut -= myedegrees[k].ed-myrinfo->id; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D to %3D. Gain: %4D. Cut: %6D\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut)); - - /* Update where, weight, and ID/ED information of the vertex you moved */ - gk_faxpy(ncon, 1.0, nvwgt, 1, npwgts+to*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt, 1, npwgts+from*ncon, 1); - where[i] = to; - myrinfo->ed += myrinfo->id-myedegrees[k].ed; - SWAP(myrinfo->id, myedegrees[k].ed, j); - if (myedegrees[k].ed == 0) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].pid = from; - - if (myrinfo->ed-myrinfo->id < 0) - BNDDelete(nbnd, bndind, bndptr, i); - - /* Update the degrees of adjacent vertices */ - for (j=xadj[i]; jrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - ASSERT(CheckRInfo(myrinfo)); - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1) - BNDInsert(nbnd, bndind, bndptr, ii); - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1) - BNDDelete(nbnd, bndind, bndptr, ii); - } - - /* Remove contribution from the .ed of 'from' */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ed == adjwgt[j]) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].ed -= adjwgt[j]; - break; - } - } - } - - /* Add contribution to the .ed of 'to' */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - - ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]); - ASSERT(CheckRInfo(myrinfo)); - - } - nmoves++; - } - } - - graph->nbnd = nbnd; - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("\t [%5.4f %5.4f], Nb: %6D, Nmoves: %5D, Cut: %6D, LB: ", - npwgts[gk_fargmin(ncon*nparts, npwgts)], npwgts[gk_fargmax(ncon*nparts, npwgts)], - nbnd, nmoves, graph->mincut); - ComputeHKWayLoadImbalance(ncon, nparts, npwgts, tvec); - for (i=0; imincut == oldcut) - break; - } - - fwspacefree(ctrl, ncon*nparts); - fwspacefree(ctrl, ncon*nparts); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void MCGreedy_KWayEdgeBalanceHorizontal(CtrlType *ctrl, GraphType *graph, idxtype nparts, - float *ubvec, idxtype npasses) -{ - idxtype i, ii, iii, j, jj, k, l, pass, nvtxs, ncon, nbnd, myndegrees, oldgain, gain, nmoves; - idxtype from, me, to, oldcut; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *perm, *bndptr, *bndind, *moved; - EDegreeType *myedegrees; - RInfoType *myrinfo; - PQueueType queue; - float *npwgts, *nvwgt, *minwgt, *maxwgt, tvec[MAXNCON]; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndind = graph->bndind; - bndptr = graph->bndptr; - - where = graph->where; - npwgts = graph->npwgts; - - /* Setup the weight intervals of the various subdomains */ - minwgt = fwspacemalloc(ctrl, ncon*nparts); - maxwgt = fwspacemalloc(ctrl, ncon*nparts); - - for (i=0; iadjwgtsum[idxargmax(nvtxs, graph->adjwgtsum)]); - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("Partitions: [%5.4f %5.4f], Nv-Nb[%6D %6D]. Cut: %6D, LB: ", - npwgts[gk_fargmin(ncon*nparts, npwgts)], npwgts[gk_fargmax(ncon*nparts, npwgts)], - graph->nvtxs, graph->nbnd, graph->mincut); - ComputeHKWayLoadImbalance(ncon, nparts, npwgts, tvec); - for (i=0; imincut); - - /* Check to see if things are out of balance, given the tolerance */ - if (MocIsHBalanced(ncon, nparts, npwgts, ubvec)) - break; - - PQueueReset(&queue); - idxset(nvtxs, -1, moved); - - oldcut = graph->mincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (ii=0; iirinfo[i].ed - graph->rinfo[i].id); - moved[i] = 2; - } - - nmoves = 0; - for (;;) { - if ((i = PQueueGetMax(&queue)) == -1) - break; - moved[i] = 1; - - myrinfo = graph->rinfo+i; - from = where[i]; - nvwgt = graph->nvwgt+i*ncon; - - if (AreAllHVwgtsBelow(ncon, 1.0, npwgts+from*ncon, -1.0, nvwgt, minwgt+from*ncon)) - continue; /* This cannot be moved! */ - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - for (k=0; kid >= 0) - j++; - if (!AreAllHVwgtsAbove(ncon, 1.0, npwgts+to*ncon, 0.0, nvwgt, minwgt+to*ncon) && - AreAllHVwgtsBelow(ncon, 1.0, npwgts+to*ncon, 1.0, nvwgt, maxwgt+to*ncon)) - j++; - if (j == 0) - continue; - -/* DELETE - if (myedegrees[k].ed-myrinfo->id < 0 && - AreAllHVwgtsBelow(ncon, 1.0, npwgts+from*ncon, 0.0, nvwgt, maxwgt+from*ncon) && - AreAllHVwgtsAbove(ncon, 1.0, npwgts+to*ncon, 0.0, nvwgt, minwgt+to*ncon) && - AreAllHVwgtsBelow(ncon, 1.0, npwgts+to*ncon, 1.0, nvwgt, maxwgt+to*ncon)) - continue; -*/ - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - graph->mincut -= myedegrees[k].ed-myrinfo->id; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D to %3D. Gain: %4D. Cut: %6D\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut)); - - /* Update where, weight, and ID/ED information of the vertex you moved */ - gk_faxpy(ncon, 1.0, nvwgt, 1, npwgts+to*ncon, 1); - gk_faxpy(ncon, -1.0, nvwgt, 1, npwgts+from*ncon, 1); - where[i] = to; - myrinfo->ed += myrinfo->id-myedegrees[k].ed; - SWAP(myrinfo->id, myedegrees[k].ed, j); - if (myedegrees[k].ed == 0) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].pid = from; - - if (myrinfo->ed == 0) - BNDDelete(nbnd, bndind, bndptr, i); - - /* Update the degrees of adjacent vertices */ - for (j=xadj[i]; jrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - ASSERT(CheckRInfo(myrinfo)); - - oldgain = (myrinfo->ed-myrinfo->id); - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - - if (myrinfo->ed > 0 && bndptr[ii] == -1) - BNDInsert(nbnd, bndind, bndptr, ii); - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - - if (myrinfo->ed == 0 && bndptr[ii] != -1) - BNDDelete(nbnd, bndind, bndptr, ii); - } - - /* Remove contribution from the .ed of 'from' */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ed == adjwgt[j]) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].ed -= adjwgt[j]; - break; - } - } - } - - /* Add contribution to the .ed of 'to' */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - - - /* Update the queue */ - if (me == to || me == from) { - gain = myrinfo->ed-myrinfo->id; - if (moved[ii] == 2) { - if (myrinfo->ed > 0) - PQueueUpdate(&queue, ii, oldgain, gain); - else { - PQueueDelete(&queue, ii, oldgain); - moved[ii] = -1; - } - } - else if (moved[ii] == -1 && myrinfo->ed > 0) { - PQueueInsert(&queue, ii, gain); - moved[ii] = 2; - } - } - - ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]); - ASSERT(CheckRInfo(myrinfo)); - } - nmoves++; - } - - graph->nbnd = nbnd; - - if (ctrl->dbglvl&DBG_REFINE) { - mprintf("\t [%5.4f %5.4f], Nb: %6D, Nmoves: %5D, Cut: %6D, LB: ", - npwgts[gk_fargmin(ncon*nparts, npwgts)], npwgts[gk_fargmax(ncon*nparts, npwgts)], - nbnd, nmoves, graph->mincut); - ComputeHKWayLoadImbalance(ncon, nparts, npwgts, tvec); - for (i=0; i limit[i]) - return 0; - - return 1; -} - - - -/************************************************************************* -* This function checks if the vertex weights of two vertices are above -* a given set of values -**************************************************************************/ -idxtype AreAllHVwgtsAbove(idxtype ncon, float alpha, float *vwgt1, float beta, float *vwgt2, float *limit) -{ - idxtype i; - - for (i=0; i max) - max = npwgts[j*ncon+i]; - } - - lbvec[i] = max*nparts; - } -} - - -/************************************************************************* -* This function determines if a partitioning is horizontally balanced -**************************************************************************/ -idxtype MocIsHBalanced(idxtype ncon, idxtype nparts, float *npwgts, float *ubvec) -{ - idxtype i, j; - float max; - - for (i=0; i max) - max = npwgts[j*ncon+i]; - } - - if (ubvec[i] < max*nparts) - return 0; - } - - return 1; -} - - - - - -/************************************************************************* -* This function checks if the pairwise balance of the between the two -* partitions will improve by moving the vertex v from pfrom to pto, -* subject to the target partition weights of tfrom, and tto respectively -**************************************************************************/ -idxtype IsHBalanceBetterFT(idxtype ncon, idxtype nparts, float *pfrom, float *pto, float *vwgt, float *ubvec) -{ - idxtype i, j, k; - float blb1=0.0, alb1=0.0, sblb=0.0, salb=0.0; - float blb2=0.0, alb2=0.0; - float temp; - - for (i=0; i m11) - return 0; - if (m22 < m12) - return 1; - if (m22 > m12) - return 0; - - return sm2 < sm1; -} - diff --git a/src/metis/mkwayrefine.c b/src/metis/mkwayrefine.c deleted file mode 100644 index 2fa0979..0000000 --- a/src/metis/mkwayrefine.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * mkwayrefine.c - * - * This file contains the driving routines for multilevel k-way refinement - * - * Started 7/28/97 - * George - * - * $Id: mkwayrefine.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - */ - -#include - - -/************************************************************************* -* This function is the entry point of refinement -**************************************************************************/ -void MocRefineKWayHorizontal(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, idxtype nparts, - float *ubvec) -{ - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); - - /* Compute the parameters of the coarsest graph */ - MocComputeKWayPartitionParams(ctrl, graph, nparts); - - for (;;) { - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->RefTmr)); - - if (!MocIsHBalanced(graph->ncon, nparts, graph->npwgts, ubvec)) { - MocComputeKWayBalanceBoundary(ctrl, graph, nparts); - MCGreedy_KWayEdgeBalanceHorizontal(ctrl, graph, nparts, ubvec, 4); - ComputeKWayBoundary(ctrl, graph, nparts); - } - - MCRandom_KWayEdgeRefineHorizontal(ctrl, graph, nparts, ubvec, 10); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); - - if (graph == orggraph) - break; - - graph = graph->finer; - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); - MocProjectKWayPartition(ctrl, graph, nparts); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); - } - - if (!MocIsHBalanced(graph->ncon, nparts, graph->npwgts, ubvec)) { - MocComputeKWayBalanceBoundary(ctrl, graph, nparts); - MCGreedy_KWayEdgeBalanceHorizontal(ctrl, graph, nparts, ubvec, 4); - ComputeKWayBoundary(ctrl, graph, nparts); - MCRandom_KWayEdgeRefineHorizontal(ctrl, graph, nparts, ubvec, 10); - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); -} - - - - -/************************************************************************* -* This function allocates memory for k-way edge refinement -**************************************************************************/ -void MocAllocateKWayPartitionMemory(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype nvtxs, ncon; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - - graph->where = idxmalloc(nvtxs, "MocAllocateKWayPartitionMemory: where"); - graph->bndptr = idxmalloc(nvtxs, "MocAllocateKWayPartitionMemory: bndptr"); - graph->bndind = idxmalloc(nvtxs, "MocAllocateKWayPartitionMemory: bndind"); - - graph->npwgts = gk_fmalloc(ncon*nparts, "MocAllocateKWayPartitionMemory: npwgts"); - - graph->rinfo = (RInfoType *)gk_malloc(nvtxs*sizeof(RInfoType), "MocAllocateKWayPartitionMemory: rinfo"); -} - - -/************************************************************************* -* This function computes the initial id/ed -**************************************************************************/ -void MocComputeKWayPartitionParams(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, j, k, l, nvtxs, ncon, nbnd, mincut, me, other; - idxtype *xadj, *adjncy, *adjwgt, *where, *bndind, *bndptr; - RInfoType *rinfo, *myrinfo; - EDegreeType *myedegrees; - float *nvwgt, *npwgts; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - npwgts = gk_fset(ncon*nparts, 0.0, graph->npwgts); - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - rinfo = graph->rinfo; - - - /*------------------------------------------------------------ - / Compute now the id/ed degrees - /------------------------------------------------------------*/ - ctrl->wspace.cdegree = 0; - nbnd = mincut = 0; - for (i=0; iid = myrinfo->ed = myrinfo->ndegrees = 0; - myrinfo->edegrees = NULL; - - for (j=xadj[i]; jed += adjwgt[j]; - } - myrinfo->id = graph->adjwgtsum[i] - myrinfo->ed; - - if (myrinfo->ed > 0) - mincut += myrinfo->ed; - - if (myrinfo->ed-myrinfo->id >= 0) - BNDInsert(nbnd, bndind, bndptr, i); - - /* Time to compute the particular external degrees */ - if (myrinfo->ed > 0) { - myedegrees = myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[i+1]-xadj[i]; - - for (j=xadj[i]; jndegrees; k++) { - if (myedegrees[k].pid == other) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = other; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - } - - ASSERT(myrinfo->ndegrees <= xadj[i+1]-xadj[i]); - } - } - - graph->mincut = mincut/2; - graph->nbnd = nbnd; - -} - - - -/************************************************************************* -* This function projects a partition, and at the same time computes the -* parameters for refinement. -**************************************************************************/ -void MocProjectKWayPartition(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, j, k, nvtxs, nbnd, me, other, istart, iend, ndegrees; - idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum; - idxtype *cmap, *where, *bndptr, *bndind; - idxtype *cwhere; - GraphType *cgraph; - RInfoType *crinfo, *rinfo, *myrinfo; - EDegreeType *myedegrees; - idxtype *htable; - - cgraph = graph->coarser; - cwhere = cgraph->where; - crinfo = cgraph->rinfo; - - nvtxs = graph->nvtxs; - cmap = graph->cmap; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - - MocAllocateKWayPartitionMemory(ctrl, graph, nparts); - where = graph->where; - rinfo = graph->rinfo; - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - - /* Go through and project partition and compute id/ed for the nodes */ - for (i=0; iwspace.cdegree = 0; - for (nbnd=0, i=0; iid = myrinfo->ed = myrinfo->ndegrees = 0; - myrinfo->edegrees = NULL; - - myrinfo->id = adjwgtsum[i]; - - if (cmap[i] > 0) { /* If it is an interface node. Note cmap[i] = crinfo[cmap[i]].ed */ - istart = xadj[i]; - iend = xadj[i+1]; - - myedegrees = myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += iend-istart; - - ndegrees = 0; - for (j=istart; jed += adjwgt[j]; - if ((k = htable[other]) == -1) { - htable[other] = ndegrees; - myedegrees[ndegrees].pid = other; - myedegrees[ndegrees++].ed = adjwgt[j]; - } - else { - myedegrees[k].ed += adjwgt[j]; - } - } - } - myrinfo->id -= myrinfo->ed; - - /* Remove space for edegrees if it was interior */ - if (myrinfo->ed == 0) { - myrinfo->edegrees = NULL; - ctrl->wspace.cdegree -= iend-istart; - } - else { - if (myrinfo->ed-myrinfo->id >= 0) - BNDInsert(nbnd, bndind, bndptr, i); - - myrinfo->ndegrees = ndegrees; - - for (j=0; jncon*nparts, cgraph->npwgts, graph->npwgts); - graph->mincut = cgraph->mincut; - graph->nbnd = nbnd; - - FreeGraph(graph->coarser, 1); - graph->coarser = NULL; - - idxwspacefree(ctrl, nparts); - - ASSERT(CheckBnd2(graph)); - -} - - - -/************************************************************************* -* This function computes the boundary definition for balancing -**************************************************************************/ -void MocComputeKWayBalanceBoundary(CtrlType *ctrl, GraphType *graph, idxtype nparts) -{ - idxtype i, nvtxs, nbnd; - idxtype *bndind, *bndptr; - - nvtxs = graph->nvtxs; - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - - - /* Compute the new boundary */ - nbnd = 0; - for (i=0; irinfo[i].ed > 0) - BNDInsert(nbnd, bndind, bndptr, i); - } - - graph->nbnd = nbnd; -} - diff --git a/src/metis/mmatch.c b/src/metis/mmatch.c deleted file mode 100644 index 04d699f..0000000 --- a/src/metis/mmatch.c +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * mmatch.c - * - * This file contains the code that computes matchings and creates the next - * level coarse graph. - * - * Started 7/23/97 - * George - * - * $Id: mmatch.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void MCMatch_RM(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, ii, j, k, nvtxs, ncon, cnvtxs, maxidx; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *match, *cmap, *perm; - float *nvwgt; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - cmap = graph->cmap; - match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs)); - - perm = idxwspacemalloc(ctrl, nvtxs); - RandomPermute(nvtxs, perm, 1); - - cnvtxs = 0; - for (ii=0; iinmaxvwgt)) { - maxidx = k; - break; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); - - CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void MCMatch_HEM(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, ii, j, k, l, nvtxs, cnvtxs, ncon, maxidx, maxwgt; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *match, *cmap, *perm; - float *nvwgt; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - cmap = graph->cmap; - match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs)); - - perm = idxwspacemalloc(ctrl, nvtxs); - RandomPermute(nvtxs, perm, 1); - - cnvtxs = 0; - for (ii=0; iinmaxvwgt)) { - maxwgt = adjwgt[j]; - maxidx = adjncy[j]; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); - - CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void MCMatch_SHEM(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, ii, j, k, nvtxs, cnvtxs, ncon, maxidx, maxwgt, avgdegree; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *match, *cmap, *degrees, *perm, *tperm; - float *nvwgt; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - cmap = graph->cmap; - match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs)); - - perm = idxwspacemalloc(ctrl, nvtxs); - tperm = idxwspacemalloc(ctrl, nvtxs); - degrees = idxwspacemalloc(ctrl, nvtxs); - - RandomPermute(nvtxs, tperm, 1); - avgdegree = 0.7*(xadj[nvtxs]/nvtxs); - for (i=0; i avgdegree ? avgdegree : xadj[i+1]-xadj[i]); - BucketSortKeysInc(nvtxs, avgdegree, degrees, tperm, perm); - - cnvtxs = 0; - - /* Take care any islands. Islands are matched with non-islands due to coarsening */ - for (ii=0; iiii; j--) { - k = perm[j]; - if (match[k] == UNMATCHED && xadj[k] < xadj[k+1]) { - maxidx = k; - break; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - /* Continue with normal matching */ - for (; iinmaxvwgt)) { - maxwgt = adjwgt[j]; - maxidx = adjncy[j]; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); - - idxwspacefree(ctrl, nvtxs); /* degrees */ - idxwspacefree(ctrl, nvtxs); /* tperm */ - - CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void MCMatch_SHEBM(CtrlType *ctrl, GraphType *graph, idxtype norm) -{ - idxtype i, ii, j, k, nvtxs, cnvtxs, ncon, maxidx, maxwgt, avgdegree; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *match, *cmap, *degrees, *perm, *tperm; - float *nvwgt; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - cmap = graph->cmap; - match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs)); - - perm = idxwspacemalloc(ctrl, nvtxs); - tperm = idxwspacemalloc(ctrl, nvtxs); - degrees = idxwspacemalloc(ctrl, nvtxs); - - RandomPermute(nvtxs, tperm, 1); - avgdegree = 0.7*(xadj[nvtxs]/nvtxs); - for (i=0; i avgdegree ? avgdegree : xadj[i+1]-xadj[i]); - BucketSortKeysInc(nvtxs, avgdegree, degrees, tperm, perm); - - cnvtxs = 0; - - /* Take care any islands. Islands are matched with non-islands due to coarsening */ - for (ii=0; iiii; j--) { - k = perm[j]; - if (match[k] == UNMATCHED && xadj[k] < xadj[k+1]) { - maxidx = k; - break; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - /* Continue with normal matching */ - for (; iinmaxvwgt) && - (maxwgt < adjwgt[j] || - (maxwgt == adjwgt[j] && - BetterVBalance(ncon, norm, nvwgt+i*ncon, nvwgt+maxidx*ncon, nvwgt+k*ncon) >= 0 - ) - ) - ) { - maxwgt = adjwgt[j]; - maxidx = k; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); - - idxwspacefree(ctrl, nvtxs); /* degrees */ - idxwspacefree(ctrl, nvtxs); /* tperm */ - - CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function finds a matching using the HEM heuristic -**************************************************************************/ -void MCMatch_SBHEM(CtrlType *ctrl, GraphType *graph, idxtype norm) -{ - idxtype i, ii, j, k, nvtxs, cnvtxs, ncon, maxidx, maxwgt, avgdegree; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *match, *cmap, *degrees, *perm, *tperm; - float *nvwgt, vbal; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->MatchTmr)); - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - cmap = graph->cmap; - match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs)); - - perm = idxwspacemalloc(ctrl, nvtxs); - tperm = idxwspacemalloc(ctrl, nvtxs); - degrees = idxwspacemalloc(ctrl, nvtxs); - - RandomPermute(nvtxs, tperm, 1); - avgdegree = 0.7*(xadj[nvtxs]/nvtxs); - for (i=0; i avgdegree ? avgdegree : xadj[i+1]-xadj[i]); - BucketSortKeysInc(nvtxs, avgdegree, degrees, tperm, perm); - - cnvtxs = 0; - - /* Take care any islands. Islands are matched with non-islands due to coarsening */ - for (ii=0; iiii; j--) { - k = perm[j]; - if (match[k] == UNMATCHED && xadj[k] < xadj[k+1]) { - maxidx = k; - break; - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - /* Continue with normal matching */ - for (; iinmaxvwgt)) { - if (maxidx != i) - vbal = BetterVBalance(ncon, norm, nvwgt+i*ncon, nvwgt+maxidx*ncon, nvwgt+k*ncon); - - if (vbal > 0 || (vbal > -.01 && maxwgt < adjwgt[j])) { - maxwgt = adjwgt[j]; - maxidx = k; - } - } - } - - cmap[i] = cmap[maxidx] = cnvtxs++; - match[i] = maxidx; - match[maxidx] = i; - } - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->MatchTmr)); - - idxwspacefree(ctrl, nvtxs); /* degrees */ - idxwspacefree(ctrl, nvtxs); /* tperm */ - - CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - - - -/************************************************************************* -* This function checks if v+u2 provides a better balance in the weight -* vector that v+u1 -**************************************************************************/ -float BetterVBalance(idxtype ncon, idxtype norm, float *vwgt, float *u1wgt, float *u2wgt) -{ - idxtype i; - float sum1, sum2, max1, max2, min1, min2, diff1, diff2; - - if (norm == -1) { - max1 = min1 = vwgt[0]+u1wgt[0]; - max2 = min2 = vwgt[0]+u2wgt[0]; - sum1 = vwgt[0]+u1wgt[0]; - sum2 = vwgt[0]+u2wgt[0]; - - for (i=1; i vwgt[i]+u1wgt[i]) - min1 = vwgt[i]+u1wgt[i]; - - if (max2 < vwgt[i]+u2wgt[i]) - max2 = vwgt[i]+u2wgt[i]; - if (min2 > vwgt[i]+u2wgt[i]) - min2 = vwgt[i]+u2wgt[i]; - - sum1 += vwgt[i]+u1wgt[i]; - sum2 += vwgt[i]+u2wgt[i]; - } - - if (sum1 == 0.0) - return 1; - else if (sum2 == 0.0) - return -1; - else - return ((max1-min1)/sum1) - ((max2-min2)/sum2); - } - else if (norm == 1) { - sum1 = sum2 = 0.0; - for (i=0; i limit) - return 0; - - return 1; -} - diff --git a/src/metis/mpmetis.c b/src/metis/mpmetis.c deleted file mode 100644 index 8867ac7..0000000 --- a/src/metis/mpmetis.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * mpmetis.c - * - * This file contains the top level routines for the multilevel recursive - * bisection algorithm PMETIS. - * - * Started 7/24/97 - * George - * - * $Id: mpmetis.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - * - */ - -#include - - - -/************************************************************************* -* This function is the entry point for PWMETIS that accepts exact weights -* for the target partitions -**************************************************************************/ -void METIS_mCPartGraphRecursive(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, - idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - SetUpGraph(&graph, OP_PMETIS, *nvtxs, *ncon, xadj, adjncy, vwgt, adjwgt, *wgtflag); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = McPMETIS_CTYPE; - ctrl.IType = McPMETIS_ITYPE; - ctrl.RType = McPMETIS_RTYPE; - ctrl.dbglvl = McPMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_PMETIS; - ctrl.CoarsenTo = 100; - - ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo); - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MCMlevelRecursiveBisection(&ctrl, &graph, *nparts, part, 1.000, 0); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); -} - - - -/************************************************************************* -* This function is the entry point for PWMETIS that accepts exact weights -* for the target partitions -**************************************************************************/ -void METIS_mCHPartGraphRecursive(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, - idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - float *ubvec, idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - float *myubvec; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - SetUpGraph(&graph, OP_PMETIS, *nvtxs, *ncon, xadj, adjncy, vwgt, adjwgt, *wgtflag); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = PMETIS_CTYPE; - ctrl.IType = PMETIS_ITYPE; - ctrl.RType = PMETIS_RTYPE; - ctrl.dbglvl = PMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_PMETIS; - ctrl.CoarsenTo = 100; - - ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo); - - myubvec = gk_fmalloc(*ncon, "PWMETIS: mytpwgts"); - gk_fcopy(*ncon, ubvec, myubvec); - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MCHMlevelRecursiveBisection(&ctrl, &graph, *nparts, part, myubvec, 0); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - gk_free((void **)&myubvec, LTERM); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); -} - - - -/************************************************************************* -* This function is the entry point for PWMETIS that accepts exact weights -* for the target partitions -**************************************************************************/ -void METIS_mCPartGraphRecursiveInternal(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, - float *nvwgt, idxtype *adjwgt, idxtype *nparts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - - SetUpGraph2(&graph, *nvtxs, *ncon, xadj, adjncy, nvwgt, adjwgt); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = PMETIS_CTYPE; - ctrl.IType = PMETIS_ITYPE; - ctrl.RType = PMETIS_RTYPE; - ctrl.dbglvl = PMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_PMETIS; - ctrl.CoarsenTo = 100; - - ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo); - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MCMlevelRecursiveBisection(&ctrl, &graph, *nparts, part, 1.000, 0); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - -} - - -/************************************************************************* -* This function is the entry point for PWMETIS that accepts exact weights -* for the target partitions -**************************************************************************/ -void METIS_mCHPartGraphRecursiveInternal(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, - float *nvwgt, idxtype *adjwgt, idxtype *nparts, float *ubvec, idxtype *options, idxtype *edgecut, - idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - float *myubvec; - - SetUpGraph2(&graph, *nvtxs, *ncon, xadj, adjncy, nvwgt, adjwgt); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = PMETIS_CTYPE; - ctrl.IType = PMETIS_ITYPE; - ctrl.RType = PMETIS_RTYPE; - ctrl.dbglvl = PMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_PMETIS; - ctrl.CoarsenTo = 100; - - ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo); - - myubvec = gk_fmalloc(*ncon, "PWMETIS: mytpwgts"); - gk_fcopy(*ncon, ubvec, myubvec); - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MCHMlevelRecursiveBisection(&ctrl, &graph, *nparts, part, myubvec, 0); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - gk_free((void **)&myubvec, LTERM); - -} - - - - -/************************************************************************* -* This function takes a graph and produces a bisection of it -**************************************************************************/ -idxtype MCMlevelRecursiveBisection(CtrlType *ctrl, GraphType *graph, idxtype nparts, idxtype *part, - float ubfactor, idxtype fpart) -{ - idxtype i, j, nvtxs, ncon, cut; - idxtype *label, *where; - GraphType lgraph, rgraph; - float tpwgts[2]; - - nvtxs = graph->nvtxs; - if (nvtxs == 0) { - mprintf("\t***Cannot bisect a graph with 0 vertices!\n\t***You are trying to partition a graph into too many parts!\n"); - return 0; - } - - /* Determine the weights of the partitions */ - tpwgts[0] = 1.0*(nparts>>1)/(1.0*nparts); - tpwgts[1] = 1.0 - tpwgts[0]; - - MCMlevelEdgeBisection(ctrl, graph, tpwgts, ubfactor); - cut = graph->mincut; - - label = graph->label; - where = graph->where; - for (i=0; i 2) - SplitGraphPart(ctrl, graph, &lgraph, &rgraph); - - /* Free the memory of the top level graph */ - FreeGraph(graph, 0); - - - /* Do the recursive call */ - if (nparts > 3) { - cut += MCMlevelRecursiveBisection(ctrl, &lgraph, nparts/2, part, ubfactor, fpart); - cut += MCMlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, ubfactor, fpart+nparts/2); - } - else if (nparts == 3) { - cut += MCMlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, ubfactor, fpart+nparts/2); - FreeGraph(&lgraph, 0); - } - - return cut; - -} - - - -/************************************************************************* -* This function takes a graph and produces a bisection of it -**************************************************************************/ -idxtype MCHMlevelRecursiveBisection(CtrlType *ctrl, GraphType *graph, idxtype nparts, idxtype *part, - float *ubvec, idxtype fpart) -{ - idxtype i, j, nvtxs, ncon, cut; - idxtype *label, *where; - GraphType lgraph, rgraph; - float tpwgts[2], *npwgts, *lubvec, *rubvec; - - lubvec = rubvec = NULL; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - if (nvtxs == 0) { - mprintf("\t***Cannot bisect a graph with 0 vertices!\n\t***You are trying to partition a graph into too many parts!\n"); - return 0; - } - - /* Determine the weights of the partitions */ - tpwgts[0] = 1.0*(nparts>>1)/(1.0*nparts); - tpwgts[1] = 1.0 - tpwgts[0]; - - /* For now, relax at the coarsest level only */ - if (nparts == 2) - MCHMlevelEdgeBisection(ctrl, graph, tpwgts, ubvec); - else - MCMlevelEdgeBisection(ctrl, graph, tpwgts, 1.000); - cut = graph->mincut; - - label = graph->label; - where = graph->where; - for (i=0; i 2) { - /* Adjust the ubvecs before the split */ - npwgts = graph->npwgts; - lubvec = gk_fmalloc(ncon, "MCHMlevelRecursiveBisection"); - rubvec = gk_fmalloc(ncon, "MCHMlevelRecursiveBisection"); - - for (i=0; i 3) { - cut += MCHMlevelRecursiveBisection(ctrl, &lgraph, nparts/2, part, lubvec, fpart); - cut += MCHMlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, rubvec, fpart+nparts/2); - } - else if (nparts == 3) { - cut += MCHMlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, rubvec, fpart+nparts/2); - FreeGraph(&lgraph, 0); - } - - gk_free((void **)&lubvec, &rubvec, LTERM); - - return cut; - -} - - - - -/************************************************************************* -* This function performs multilevel bisection -**************************************************************************/ -void MCMlevelEdgeBisection(CtrlType *ctrl, GraphType *graph, float *tpwgts, float ubfactor) -{ - GraphType *cgraph; - - cgraph = MCCoarsen2Way(ctrl, graph); - - MocInit2WayPartition(ctrl, cgraph, tpwgts, ubfactor); - - MocRefine2Way(ctrl, graph, cgraph, tpwgts, ubfactor); - -} - - - -/************************************************************************* -* This function performs multilevel bisection -**************************************************************************/ -void MCHMlevelEdgeBisection(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec) -{ - idxtype i; - GraphType *cgraph; - -/* - for (i=0; incon; i++) - mprintf("%.4f ", ubvec[i]); - mprintf("\n"); -*/ - - cgraph = MCCoarsen2Way(ctrl, graph); - - MocInit2WayPartition2(ctrl, cgraph, tpwgts, ubvec); - - MocRefine2Way2(ctrl, graph, cgraph, tpwgts, ubvec); - -} - - diff --git a/src/metis/mrefine.c b/src/metis/mrefine.c deleted file mode 100644 index a3199e0..0000000 --- a/src/metis/mrefine.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * refine.c - * - * This file contains the driving routines for multilevel refinement - * - * Started 7/24/97 - * George - * - * $Id: mrefine.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - */ - -#include - - -/************************************************************************* -* This function is the entry point of refinement -**************************************************************************/ -void MocRefine2Way(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, float *tpwgts, float ubfactor) -{ - idxtype i; - float tubvec[MAXNCON]; - - for (i=0; incon; i++) - tubvec[i] = 1.0; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); - - /* Compute the parameters of the coarsest graph */ - MocCompute2WayPartitionParams(ctrl, graph); - - for (;;) { - ASSERT(CheckBnd(graph)); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->RefTmr)); - switch (ctrl->RType) { - case RTYPE_FM: - MocBalance2Way(ctrl, graph, tpwgts, 1.03); - MocFM_2WayEdgeRefine(ctrl, graph, tpwgts, 8); - break; - case 2: - MocBalance2Way(ctrl, graph, tpwgts, 1.03); - MocFM_2WayEdgeRefine2(ctrl, graph, tpwgts, tubvec, 8); - break; - default: - errexit("Unknown refinement type: %d\n", ctrl->RType); - } - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); - - if (graph == orggraph) - break; - - graph = graph->finer; - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); - MocProject2WayPartition(ctrl, graph); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); - } - - MocBalance2Way(ctrl, graph, tpwgts, 1.01); - MocFM_2WayEdgeRefine(ctrl, graph, tpwgts, 8); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); -} - - -/************************************************************************* -* This function allocates memory for 2-way edge refinement -**************************************************************************/ -void MocAllocate2WayPartitionMemory(CtrlType *ctrl, GraphType *graph) -{ - idxtype nvtxs, ncon; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - - graph->npwgts = gk_fmalloc(2*ncon, "MocAllocate2WayPartitionMemory: npwgts"); - graph->where = idxmalloc(nvtxs, "MocAllocate2WayPartitionMemory: where"); - graph->id = idxmalloc(nvtxs, "MocAllocate2WayPartitionMemory: id"); - graph->ed = idxmalloc(nvtxs, "MocAllocate2WayPartitionMemory: ed"); - graph->bndptr = idxmalloc(nvtxs, "MocAllocate2WayPartitionMemory: bndptr"); - graph->bndind = idxmalloc(nvtxs, "MocAllocate2WayPartitionMemory: bndind"); -} - - -/************************************************************************* -* This function computes the initial id/ed -**************************************************************************/ -void MocCompute2WayPartitionParams(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, j, k, l, nvtxs, ncon, nbnd, mincut; - idxtype *xadj, *adjncy, *adjwgt; - float *nvwgt, *npwgts; - idxtype *id, *ed, *where; - idxtype *bndptr, *bndind; - idxtype me, other; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - npwgts = gk_fset(2*ncon, 0.0, graph->npwgts); - id = idxset(nvtxs, 0, graph->id); - ed = idxset(nvtxs, 0, graph->ed); - bndptr = idxset(nvtxs, -1, graph->bndptr); - bndind = graph->bndind; - - - /*------------------------------------------------------------ - / Compute now the id/ed degrees - /------------------------------------------------------------*/ - nbnd = mincut = 0; - for (i=0; i= 0 && where[i] <= 1); - me = where[i]; - gk_faxpy(ncon, 1.0, nvwgt+i*ncon, 1, npwgts+me*ncon, 1); - - for (j=xadj[i]; j 0 || xadj[i] == xadj[i+1]) { - mincut += ed[i]; - bndptr[i] = nbnd; - bndind[nbnd++] = i; - } - } - - graph->mincut = mincut/2; - graph->nbnd = nbnd; - -} - - - -/************************************************************************* -* This function projects a partition, and at the same time computes the -* parameters for refinement. -**************************************************************************/ -void MocProject2WayPartition(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, j, k, nvtxs, nbnd, me; - idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum; - idxtype *cmap, *where, *id, *ed, *bndptr, *bndind; - idxtype *cwhere, *cid, *ced, *cbndptr; - GraphType *cgraph; - - cgraph = graph->coarser; - cwhere = cgraph->where; - cid = cgraph->id; - ced = cgraph->ed; - cbndptr = cgraph->bndptr; - - nvtxs = graph->nvtxs; - cmap = graph->cmap; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - - MocAllocate2WayPartitionMemory(ctrl, graph); - - where = graph->where; - id = idxset(nvtxs, 0, graph->id); - ed = idxset(nvtxs, 0, graph->ed); - bndptr = idxset(nvtxs, -1, graph->bndptr); - bndind = graph->bndind; - - - /* Go through and project partition and compute id/ed for the nodes */ - for (i=0; i 0 || xadj[i] == xadj[i+1]) { - bndptr[i] = nbnd; - bndind[nbnd++] = i; - } - } - } - } - - graph->mincut = cgraph->mincut; - graph->nbnd = nbnd; - gk_fcopy(2*graph->ncon, cgraph->npwgts, graph->npwgts); - - FreeGraph(graph->coarser, 1); - graph->coarser = NULL; - -} - diff --git a/src/metis/mrefine2.c b/src/metis/mrefine2.c deleted file mode 100644 index 2142be1..0000000 --- a/src/metis/mrefine2.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * mrefine2.c - * - * This file contains the driving routines for multilevel refinement - * - * Started 7/24/97 - * George - * - * $Id: mrefine2.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - */ - -#include - - -/************************************************************************* -* This function is the entry point of refinement -**************************************************************************/ -void MocRefine2Way2(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, float *tpwgts, - float *ubvec) -{ - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); - - /* Compute the parameters of the coarsest graph */ - MocCompute2WayPartitionParams(ctrl, graph); - - for (;;) { - ASSERT(CheckBnd(graph)); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->RefTmr)); - switch (ctrl->RType) { - case RTYPE_FM: - MocBalance2Way2(ctrl, graph, tpwgts, ubvec); - MocFM_2WayEdgeRefine2(ctrl, graph, tpwgts, ubvec, 8); - break; - default: - errexit("Unknown refinement type: %d\n", ctrl->RType); - } - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); - - if (graph == orggraph) - break; - - graph = graph->finer; - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); - MocProject2WayPartition(ctrl, graph); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); -} - - diff --git a/src/metis/mrkmetis.c b/src/metis/mrkmetis.c deleted file mode 100644 index 3c3f78c..0000000 --- a/src/metis/mrkmetis.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * mrkmetis.c - * - * This file contains the top level routines for the multilevel k-way partitioning - * algorithm KMETIS. - * - * Started 7/28/97 - * George - * - * $Id: mrkmetis.c,v 1.2 2003/04/04 23:25:10 karypis Exp $ - * - */ - -#include - - - -/************************************************************************* -* This function is the entry point for KWMETIS -**************************************************************************/ -void METIS_mCRefineGraphKway(idxtype *nvtxs, idxtype *ncon, idxtype *xadj, idxtype *adjncy, - idxtype *vwgt, idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, - idxtype *nparts, float *rubvec, idxtype *options, idxtype *edgecut, - idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - SetUpGraph(&graph, OP_KMETIS, *nvtxs, *ncon, xadj, adjncy, vwgt, adjwgt, *wgtflag); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = McKMETIS_CTYPE; - ctrl.IType = McKMETIS_ITYPE; - ctrl.RType = McKMETIS_RTYPE; - ctrl.dbglvl = McKMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_KMETIS; - ctrl.CoarsenTo = amax((*nvtxs)/(20*gk_log2(*nparts)), 30*(*nparts)); - ctrl.nmaxvwgt = 0.0; /* GK-MOD: Ensure that no coarsening will take place */ - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MCMlevelKWayRefinement(&ctrl, &graph, *nparts, part, rubvec); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); -} - - -/************************************************************************* -* This function takes a graph and produces a bisection of it -**************************************************************************/ -idxtype MCMlevelKWayRefinement(CtrlType *ctrl, GraphType *graph, idxtype nparts, idxtype *part, - float *rubvec) -{ - idxtype i, j, nvtxs; - GraphType *cgraph; - idxtype options[10], edgecut; - - cgraph = MCCoarsen2Way(ctrl, graph); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); - MocAllocateKWayPartitionMemory(ctrl, cgraph, nparts); - - if (cgraph->nvtxs != graph->nvtxs) - errexit("GK-MOD Failed: %d %d\n", cgraph->nvtxs, graph->nvtxs); - - for (i=0; invtxs; i++) - cgraph->where[graph->cmap[i]] = part[i]; - - MocRefineKWayHorizontal(ctrl, graph, cgraph, nparts, rubvec); - - idxcopy(graph->nvtxs, graph->where, part); - - FreeGraph(graph, 0); - - return graph->mincut; - -} - diff --git a/src/metis/mutil.c b/src/metis/mutil.c deleted file mode 100644 index 17dd023..0000000 --- a/src/metis/mutil.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * mutil.c - * - * This file contains various utility functions for the MOC portion of the - * code - * - * Started 2/15/98 - * George - * - * $Id: mutil.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function checks if the vertex weights of two vertices are below -* a given set of values -**************************************************************************/ -idxtype AreAllVwgtsBelow(idxtype ncon, float alpha, float *vwgt1, float beta, float *vwgt2, float limit) -{ - idxtype i; - - for (i=0; i limit) - return 0; - - return 1; -} - - -/************************************************************************* -* This function checks if the vertex weights of two vertices are below -* a given set of values -**************************************************************************/ -idxtype AreAnyVwgtsBelow(idxtype ncon, float alpha, float *vwgt1, float beta, float *vwgt2, float limit) -{ - idxtype i; - - for (i=0; i max) - max = npwgts[j*ncon+i]; - } - if (max*nparts > lb) - lb = max*nparts; - } - - return lb; -} - -/************************************************************************* -* This function checks if the vertex weights of two vertices are below -* a given set of values -**************************************************************************/ -idxtype AreAllBelow(idxtype ncon, float *v1, float *v2) -{ - idxtype i; - - for (i=0; i v2[i]) - return 0; - - return 1; -} diff --git a/src/metis/myqsort.c b/src/metis/myqsort.c deleted file mode 100644 index 6d69bb3..0000000 --- a/src/metis/myqsort.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * myqsort.c - * - * This file contains a fast idxtype increasing qsort algorithm. - * Addopted from TeX - * - * Started 10/18/96 - * George - * - * $Id: myqsort.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - */ - -#include /* only for type declarations */ - - - - -/************************************************************************* -* Entry point of idxtype increasing sort -**************************************************************************/ -void iidxsort(size_t n, idxtype *base) -{ -#define idxtype_lt(a, b) ((*a) < (*b)) - GKQSORT(idxtype, base, n, idxtype_lt); -} - - -/************************************************************************* -* Entry point of KeyVal increasing sort, ONLY key part -**************************************************************************/ -void ikeysort(size_t n, KeyValueType *base) -{ -#define keyvalue_lt(a, b) ((a)->key < (b)->key) - GKQSORT(KeyValueType, base, n, keyvalue_lt); -} - - - -/************************************************************************* -* Entry point of KeyVal increasing sort, BOTH key and val part -**************************************************************************/ -void ikeyvalsort(size_t n, KeyValueType *base) -{ -#define keyvalueboth_lt(a, b) ((a)->key < (b)->key || ((a)->key == (b)->key && (a)->val < (b)->val)) - GKQSORT(KeyValueType, base, n, keyvalueboth_lt); -} - - -/************************************************************************* -* Entry point of DKeyValueType increasing sort based on keys -**************************************************************************/ -void idkeysort(size_t n, DKeyValueType *base) -{ -#define dkeyvalue_lt(a, b) ((a)->key < (b)->key) - GKQSORT(DKeyValueType, base, n, dkeyvalue_lt); -} diff --git a/src/metis/ometis.c b/src/metis/ometis.c deleted file mode 100644 index f2c9517..0000000 --- a/src/metis/ometis.c +++ /dev/null @@ -1,762 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * ometis.c - * - * This file contains the top level routines for the multilevel recursive - * bisection algorithm PMETIS. - * - * Started 7/24/97 - * George - * - * $Id: ometis.c,v 1.4 2003/07/31 06:04:52 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function is the entry point for OEMETIS -**************************************************************************/ -void METIS_EdgeND(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, - idxtype *perm, idxtype *iperm) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - SetUpGraph(&graph, OP_OEMETIS, *nvtxs, 1, xadj, adjncy, NULL, NULL, 0); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = OEMETIS_CTYPE; - ctrl.IType = OEMETIS_ITYPE; - ctrl.RType = OEMETIS_RTYPE; - ctrl.dbglvl = OEMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.oflags = 0; - ctrl.pfactor = -1; - ctrl.nseps = 1; - - ctrl.optype = OP_OEMETIS; - ctrl.CoarsenTo = 20; - ctrl.maxvwgt = 1.5*(idxsum(*nvtxs, graph.vwgt, 1)/ctrl.CoarsenTo); - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, 2); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - MlevelNestedDissection(&ctrl, &graph, iperm, ORDER_UNBALANCE_FRACTION, *nvtxs); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - for (i=0; i<*nvtxs; i++) - perm[iperm[i]] = i; - - FreeWorkSpace(&ctrl, &graph); - - if (*numflag == 1) - Change2FNumberingOrder(*nvtxs, xadj, adjncy, perm, iperm); -} - - -/************************************************************************* -* This function is the entry point for ONCMETIS -**************************************************************************/ -void METIS_NodeND(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *numflag, idxtype *options, - idxtype *perm, idxtype *iperm) -{ - idxtype i, ii, j, l, wflag, nflag; - GraphType graph; - CtrlType ctrl; - idxtype *cptr, *cind, *piperm; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = ONMETIS_CTYPE; - ctrl.IType = ONMETIS_ITYPE; - ctrl.RType = ONMETIS_RTYPE; - ctrl.dbglvl = ONMETIS_DBGLVL; - ctrl.oflags = ONMETIS_OFLAGS; - ctrl.pfactor = ONMETIS_PFACTOR; - ctrl.nseps = ONMETIS_NSEPS; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - ctrl.oflags = options[OPTION_OFLAGS]; - ctrl.pfactor = options[OPTION_PFACTOR]; - ctrl.nseps = options[OPTION_NSEPS]; - } - if (ctrl.nseps < 1) - ctrl.nseps = 1; - - ctrl.optype = OP_ONMETIS; - ctrl.CoarsenTo = 100; - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - InitRandom(-1); - - if (ctrl.pfactor > 0) { - /*============================================================ - * Prune the dense columns - ==============================================================*/ - piperm = idxmalloc(*nvtxs, "ONMETIS: piperm"); - - PruneGraph(&ctrl, &graph, *nvtxs, xadj, adjncy, piperm, (float)(0.1*ctrl.pfactor)); - } - else if (ctrl.oflags&OFLAG_COMPRESS) { - /*============================================================ - * Compress the graph - ==============================================================*/ - cptr = idxmalloc(*nvtxs+1, "ONMETIS: cptr"); - cind = idxmalloc(*nvtxs, "ONMETIS: cind"); - - CompressGraph(&ctrl, &graph, *nvtxs, xadj, adjncy, cptr, cind); - - if (graph.nvtxs >= COMPRESSION_FRACTION*(*nvtxs)) { - ctrl.oflags--; /* We actually performed no compression */ - gk_free((void **)&cptr, &cind, LTERM); - } - else if (2*graph.nvtxs < *nvtxs && ctrl.nseps == 1) - ctrl.nseps = 2; - } - else { - SetUpGraph(&graph, OP_ONMETIS, *nvtxs, 1, xadj, adjncy, NULL, NULL, 0); - } - - - /*============================================================= - * Do the nested dissection ordering - --=============================================================*/ - ctrl.maxvwgt = 1.5*(idxsum(graph.nvtxs, graph.vwgt, 1)/ctrl.CoarsenTo); - AllocateWorkSpace(&ctrl, &graph, 2); - - if (ctrl.oflags&OFLAG_CCMP) - MlevelNestedDissectionCC(&ctrl, &graph, iperm, ORDER_UNBALANCE_FRACTION, graph.nvtxs); - else - MlevelNestedDissection(&ctrl, &graph, iperm, ORDER_UNBALANCE_FRACTION, graph.nvtxs); - - FreeWorkSpace(&ctrl, &graph); - - if (ctrl.pfactor > 0) { /* Order any prunned vertices */ - if (graph.nvtxs < *nvtxs) { - idxcopy(graph.nvtxs, iperm, perm); /* Use perm as an auxiliary array */ - for (i=0; invtxs; - - /* Determine the weights of the partitions */ - tvwgt = idxsum(nvtxs, graph->vwgt, 1); - tpwgts2[0] = tvwgt/2; - tpwgts2[1] = tvwgt-tpwgts2[0]; - - switch (ctrl->optype) { - case OP_OEMETIS: - MlevelEdgeBisection(ctrl, graph, tpwgts2, ubfactor); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->SepTmr)); - ConstructMinCoverSeparator(ctrl, graph, ubfactor); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->SepTmr)); - - break; - case OP_ONMETIS: - MlevelNodeBisectionMultiple(ctrl, graph, tpwgts2, ubfactor); - - IFSET(ctrl->dbglvl, DBG_SEPINFO, mprintf("Nvtxs: %6D, [%6D %6D %6D]\n", graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2])); - - break; - } - - /* Order the nodes in the separator */ - nbnd = graph->nbnd; - bndind = graph->bndind; - label = graph->label; - for (i=0; i MMDSWITCH) - MlevelNestedDissection(ctrl, &rgraph, order, ubfactor, lastvtx); - else { - MMDOrder(ctrl, &rgraph, order, lastvtx); - FreeGraph(&rgraph, 0); - } - if (lgraph.nvtxs > MMDSWITCH) - MlevelNestedDissection(ctrl, &lgraph, order, ubfactor, lastvtx-rgraph.nvtxs); - else { - MMDOrder(ctrl, &lgraph, order, lastvtx-rgraph.nvtxs); - FreeGraph(&lgraph, 0); - } -} - - -/************************************************************************* -* This function takes a graph and produces a bisection of it -**************************************************************************/ -void MlevelNestedDissectionCC(CtrlType *ctrl, GraphType *graph, idxtype *order, float ubfactor, idxtype lastvtx) -{ - idxtype i, j, nvtxs, nbnd, tvwgt, tpwgts2[2], nsgraphs, ncmps, rnvtxs; - idxtype *label, *bndind; - idxtype *cptr, *cind; - GraphType *sgraphs; - - nvtxs = graph->nvtxs; - - /* Determine the weights of the partitions */ - tvwgt = idxsum(nvtxs, graph->vwgt, 1); - tpwgts2[0] = tvwgt/2; - tpwgts2[1] = tvwgt-tpwgts2[0]; - - MlevelNodeBisectionMultiple(ctrl, graph, tpwgts2, ubfactor); - IFSET(ctrl->dbglvl, DBG_SEPINFO, mprintf("Nvtxs: %6D, [%6D %6D %6D]\n", graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2])); - - /* Order the nodes in the separator */ - nbnd = graph->nbnd; - bndind = graph->bndind; - label = graph->label; - for (i=0; i 2) - mprintf("[%5D] has %3D components\n", nvtxs, ncmps); -*/ - - sgraphs = (GraphType *)gk_malloc(ncmps*sizeof(GraphType), "MlevelNestedDissectionCC: sgraphs"); - - nsgraphs = SplitGraphOrderCC(ctrl, graph, sgraphs, ncmps, cptr, cind); - - gk_free((void **)&cptr, &cind, LTERM); - - /* Free the memory of the top level graph */ - FreeGraph(graph, 0); - - /* Go and process the subgraphs */ - for (rnvtxs=i=0; inseps == 1 || graph->nvtxs < (ctrl->oflags&OFLAG_COMPRESS ? 1000 : 2000)) { - MlevelNodeBisection(ctrl, graph, tpwgts, ubfactor); - return; - } - - nvtxs = graph->nvtxs; - - if (ctrl->oflags&OFLAG_COMPRESS) { /* Multiple separators at the original graph */ - bestwhere = idxmalloc(nvtxs, "MlevelNodeBisection2: bestwhere"); - - for (i=ctrl->nseps; i>0; i--) { - MlevelNodeBisection(ctrl, graph, tpwgts, ubfactor); - - /* mprintf("%5D ", cgraph->mincut); */ - - if (i==ctrl->nseps || graph->mincut < mincut) { - mincut = graph->mincut; - idxcopy(nvtxs, graph->where, bestwhere); - } - - FreeRData(graph); - - if (mincut == 0) - break; - } - /* mprintf("[%5D]\n", mincut); */ - - Allocate2WayNodePartitionMemory(ctrl, graph); - idxcopy(nvtxs, bestwhere, graph->where); - gk_free((void **)&bestwhere, LTERM); - - Compute2WayNodePartitionParams(ctrl, graph); - } - else { /* Coarsen it a bit */ - ctrl->CoarsenTo = nvtxs-1; - - cgraph = Coarsen2Way(ctrl, graph); - - cnvtxs = cgraph->nvtxs; - - bestwhere = idxmalloc(cnvtxs, "MlevelNodeBisection2: bestwhere"); - - for (i=ctrl->nseps; i>0; i--) { - ctrl->CType += 20; /* This is a hack. Look at coarsen.c */ - MlevelNodeBisection(ctrl, cgraph, tpwgts, ubfactor); - - /* mprintf("%5D ", cgraph->mincut); */ - - if (i==ctrl->nseps || cgraph->mincut < mincut) { - mincut = cgraph->mincut; - idxcopy(cnvtxs, cgraph->where, bestwhere); - } - - FreeRData(graph); - - if (mincut == 0) - break; - } - /* mprintf("[%5D]\n", mincut); */ - - Allocate2WayNodePartitionMemory(ctrl, cgraph); - idxcopy(cnvtxs, bestwhere, cgraph->where); - gk_free((void **)&bestwhere, LTERM); - - Compute2WayNodePartitionParams(ctrl, cgraph); - - Refine2WayNode(ctrl, graph, cgraph, ubfactor); - } - -} - -/************************************************************************* -* This function performs multilevel bisection -**************************************************************************/ -void MlevelNodeBisection(CtrlType *ctrl, GraphType *graph, idxtype *tpwgts, float ubfactor) -{ - GraphType *cgraph; - - ctrl->CoarsenTo = graph->nvtxs/8; - if (ctrl->CoarsenTo > 100) - ctrl->CoarsenTo = 100; - else if (ctrl->CoarsenTo < 40) - ctrl->CoarsenTo = 40; - ctrl->maxvwgt = 1.5*((tpwgts[0]+tpwgts[1])/ctrl->CoarsenTo); - - cgraph = Coarsen2Way(ctrl, graph); - - switch (ctrl->IType) { - case ITYPE_GGPKL: - Init2WayPartition(ctrl, cgraph, tpwgts, ubfactor); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->SepTmr)); - - Compute2WayPartitionParams(ctrl, cgraph); - ConstructSeparator(ctrl, cgraph, ubfactor); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->SepTmr)); - break; - case ITYPE_GGPKLNODE: - InitSeparator(ctrl, cgraph, ubfactor); - break; - } - - Refine2WayNode(ctrl, graph, cgraph, ubfactor); - -} - - - - -/************************************************************************* -* This function takes a graph and a bisection and splits it into two graphs. -* This function relies on the fact that adjwgt is all equal to 1. -**************************************************************************/ -void SplitGraphOrder(CtrlType *ctrl, GraphType *graph, GraphType *lgraph, GraphType *rgraph) -{ - idxtype i, ii, j, k, l, istart, iend, mypart, nvtxs, snvtxs[3], snedges[3]; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *adjwgtsum, *label, *where, *bndptr, *bndind; - idxtype *sxadj[2], *svwgt[2], *sadjncy[2], *sadjwgt[2], *sadjwgtsum[2], *slabel[2]; - idxtype *rename; - idxtype *auxadjncy, *auxadjwgt; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->SplitTmr)); - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - label = graph->label; - where = graph->where; - bndptr = graph->bndptr; - bndind = graph->bndind; - ASSERT(bndptr != NULL); - - rename = idxwspacemalloc(ctrl, nvtxs); - - snvtxs[0] = snvtxs[1] = snvtxs[2] = snedges[0] = snedges[1] = snedges[2] = 0; - for (i=0; ixadj; - svwgt[0] = lgraph->vwgt; - sadjwgtsum[0] = lgraph->adjwgtsum; - sadjncy[0] = lgraph->adjncy; - sadjwgt[0] = lgraph->adjwgt; - slabel[0] = lgraph->label; - - SetUpSplitGraph(graph, rgraph, snvtxs[1], snedges[1]); - sxadj[1] = rgraph->xadj; - svwgt[1] = rgraph->vwgt; - sadjwgtsum[1] = rgraph->adjwgtsum; - sadjncy[1] = rgraph->adjncy; - sadjwgt[1] = rgraph->adjwgt; - slabel[1] = rgraph->label; - - /* Go and use bndptr to also mark the boundary nodes in the two partitions */ - for (ii=0; iinbnd; ii++) { - i = bndind[ii]; - for (j=xadj[i]; jnvtxs = snvtxs[0]; - lgraph->nedges = snedges[0]; - rgraph->nvtxs = snvtxs[1]; - rgraph->nedges = snedges[1]; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->SplitTmr)); - - idxwspacefree(ctrl, nvtxs); - -} - -/************************************************************************* -* This function uses MMD to order the graph. The vertices are numbered -* from lastvtx downwards -**************************************************************************/ -void MMDOrder(CtrlType *ctrl, GraphType *graph, idxtype *order, idxtype lastvtx) -{ - idxtype i, j, k, nvtxs, nofsub, firstvtx; - idxtype *xadj, *adjncy, *label; - idxtype *perm, *iperm, *head, *qsize, *list, *marker; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - - /* Relabel the vertices so that it starts from 1 */ - k = xadj[nvtxs]; - for (i=0; ilabel; - firstvtx = lastvtx-nvtxs; - for (i=0; idbglvl, DBG_TIME, gk_startcputimer(ctrl->SplitTmr)); - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - label = graph->label; - where = graph->where; - bndptr = graph->bndptr; - bndind = graph->bndind; - ASSERT(bndptr != NULL); - - /* Go and use bndptr to also mark the boundary nodes in the two partitions */ - for (ii=0; iinbnd; ii++) { - i = bndind[ii]; - for (j=xadj[i]; jdbglvl, DBG_TIME, gk_stopcputimer(ctrl->SplitTmr)); - - idxwspacefree(ctrl, nvtxs); - - return ncmps; - -} - - - - - diff --git a/src/metis/parmetis.c b/src/metis/parmetis.c deleted file mode 100644 index 87f6466..0000000 --- a/src/metis/parmetis.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * parmetis.c - * - * This file contains top level routines that are used by ParMETIS - * - * Started 10/14/97 - * George - * - * $Id: parmetis.c,v 1.5 2002/08/13 07:16:43 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function is the entry point for KMETIS with seed specification -* in options[7] -**************************************************************************/ -void METIS_PartGraphKway2(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i; - float *tpwgts; - - tpwgts = gk_fmalloc(*nparts, "KMETIS: tpwgts"); - for (i=0; i<*nparts; i++) - tpwgts[i] = 1.0/(1.0*(*nparts)); - - METIS_WPartGraphKway2(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, - tpwgts, options, edgecut, part); - - gk_free((void **)&tpwgts, LTERM); -} - - -/************************************************************************* -* This function is the entry point for KWMETIS with seed specification -* in options[7] -**************************************************************************/ -void METIS_WPartGraphKway2(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - SetUpGraph(&graph, OP_KMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, *wgtflag); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = KMETIS_CTYPE; - ctrl.IType = KMETIS_ITYPE; - ctrl.RType = KMETIS_RTYPE; - ctrl.dbglvl = KMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_KMETIS; - ctrl.CoarsenTo = 20*(*nparts); - ctrl.maxvwgt = 1.5*((graph.vwgt ? idxsum(*nvtxs, graph.vwgt, 1) : (*nvtxs))/ctrl.CoarsenTo); - - InitRandom(options[7]); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MlevelKWayPartitioning(&ctrl, &graph, *nparts, part, tpwgts, 1.03); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); -} - - -/************************************************************************* -* This function is the entry point for the node ND code for ParMETIS -**************************************************************************/ -void METIS_NodeNDP(idxtype nvtxs, idxtype *xadj, idxtype *adjncy, idxtype npes, - idxtype *options, idxtype *perm, idxtype *iperm, idxtype *sizes) -{ - idxtype i, ii, j, l, wflag, nflag; - GraphType graph; - CtrlType ctrl; - idxtype *cptr, *cind; - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = ONMETIS_CTYPE; - ctrl.IType = ONMETIS_ITYPE; - ctrl.RType = ONMETIS_RTYPE; - ctrl.dbglvl = ONMETIS_DBGLVL; - ctrl.oflags = ONMETIS_OFLAGS; - ctrl.pfactor = ONMETIS_PFACTOR; - ctrl.nseps = ONMETIS_NSEPS; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - ctrl.oflags = options[OPTION_OFLAGS]; - ctrl.pfactor = options[OPTION_PFACTOR]; - ctrl.nseps = options[OPTION_NSEPS]; - } - if (ctrl.nseps < 1) - ctrl.nseps = 1; - - ctrl.optype = OP_ONMETIS; - ctrl.CoarsenTo = 100; - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - InitRandom(-1); - - if (ctrl.oflags&OFLAG_COMPRESS) { - /*============================================================ - * Compress the graph - ==============================================================*/ - cptr = idxmalloc(nvtxs+1, "ONMETIS: cptr"); - cind = idxmalloc(nvtxs, "ONMETIS: cind"); - - CompressGraph(&ctrl, &graph, nvtxs, xadj, adjncy, cptr, cind); - - if (graph.nvtxs >= COMPRESSION_FRACTION*(nvtxs)) { - ctrl.oflags--; /* We actually performed no compression */ - gk_free((void **)&cptr, &cind, LTERM); - } - else if (2*graph.nvtxs < nvtxs && ctrl.nseps == 1) - ctrl.nseps = 2; - } - else { - SetUpGraph(&graph, OP_ONMETIS, nvtxs, 1, xadj, adjncy, NULL, NULL, 0); - } - - - /*============================================================= - * Do the nested dissection ordering - --=============================================================*/ - ctrl.maxvwgt = 1.5*(idxsum(graph.nvtxs, graph.vwgt, 1)/ctrl.CoarsenTo); - AllocateWorkSpace(&ctrl, &graph, 2); - - idxset(2*npes-1, 0, sizes); - MlevelNestedDissectionP(&ctrl, &graph, iperm, graph.nvtxs, npes, 0, sizes); - - FreeWorkSpace(&ctrl, &graph); - - if (ctrl.oflags&OFLAG_COMPRESS) { /* Uncompress the ordering */ - if (graph.nvtxs < COMPRESSION_FRACTION*(nvtxs)) { - /* construct perm from iperm */ - for (i=0; invtxs; - - if (nvtxs == 0) { - FreeGraph(graph, 0); - return; - } - - /* Determine the weights of the partitions */ - tvwgt = idxsum(nvtxs, graph->vwgt, 1); - tpwgts2[0] = tvwgt/2; - tpwgts2[1] = tvwgt-tpwgts2[0]; - - if (cpos >= npes-1) - ubfactor = ORDER_UNBALANCE_FRACTION; - else - ubfactor = 1.05; - - - MlevelNodeBisectionMultiple(ctrl, graph, tpwgts2, ubfactor); - - IFSET(ctrl->dbglvl, DBG_SEPINFO, mprintf("Nvtxs: %6D, [%6D %6D %6D]\n", graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2])); - - if (cpos < npes-1) { - sizes[2*npes-2-cpos] = graph->pwgts[2]; - sizes[2*npes-2-(2*cpos+1)] = graph->pwgts[1]; - sizes[2*npes-2-(2*cpos+2)] = graph->pwgts[0]; - } - - /* Order the nodes in the separator */ - nbnd = graph->nbnd; - bndind = graph->bndind; - label = graph->label; - for (i=0; i MMDSWITCH || 2*cpos+1 < npes-1) - MlevelNestedDissectionP(ctrl, &rgraph, order, lastvtx, npes, 2*cpos+1, sizes); - else { - MMDOrder(ctrl, &rgraph, order, lastvtx); - FreeGraph(&rgraph, 0); - } - if (lgraph.nvtxs > MMDSWITCH || 2*cpos+2 < npes-1) - MlevelNestedDissectionP(ctrl, &lgraph, order, lastvtx-rgraph.nvtxs, npes, 2*cpos+2, sizes); - else { - MMDOrder(ctrl, &lgraph, order, lastvtx-rgraph.nvtxs); - FreeGraph(&lgraph, 0); - } -} - - - - -/************************************************************************* -* This function is the entry point for ONWMETIS. It requires weights on the -* vertices. It is for the case that the matrix has been pre-compressed. -**************************************************************************/ -void METIS_NodeComputeSeparator(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *options, idxtype *sepsize, idxtype *part) -{ - idxtype i, j, tvwgt, tpwgts[2]; - GraphType graph; - CtrlType ctrl; - - SetUpGraph(&graph, OP_ONMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, 3); - tvwgt = idxsum(*nvtxs, graph.vwgt, 1); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = ONMETIS_CTYPE; - ctrl.IType = ONMETIS_ITYPE; - ctrl.RType = ONMETIS_RTYPE; - ctrl.dbglvl = ONMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - - ctrl.oflags = 0; - ctrl.pfactor = 0; - ctrl.nseps = 3; - ctrl.optype = OP_ONMETIS; - ctrl.CoarsenTo = amin(100, *nvtxs-1); - ctrl.maxvwgt = 1.5*tvwgt/ctrl.CoarsenTo; - - InitRandom(options[7]); - - AllocateWorkSpace(&ctrl, &graph, 2); - - /*============================================================ - * Perform the bisection - *============================================================*/ - tpwgts[0] = tvwgt/2; - tpwgts[1] = tvwgt-tpwgts[0]; - - MlevelNodeBisectionMultiple(&ctrl, &graph, tpwgts, 1.02); - - *sepsize = graph.pwgts[2]; - idxcopy(*nvtxs, graph.where, part); - - FreeGraph(&graph, 0); - - FreeWorkSpace(&ctrl, &graph); - -} - - - -/************************************************************************* -* This function is the entry point for ONWMETIS. It requires weights on the -* vertices. It is for the case that the matrix has been pre-compressed. -**************************************************************************/ -void METIS_EdgeComputeSeparator(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *options, idxtype *sepsize, idxtype *part) -{ - idxtype i, j, tvwgt, tpwgts[2]; - GraphType graph; - CtrlType ctrl; - - SetUpGraph(&graph, OP_ONMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, 3); - tvwgt = idxsum(*nvtxs, graph.vwgt, 1); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = ONMETIS_CTYPE; - ctrl.IType = ONMETIS_ITYPE; - ctrl.RType = ONMETIS_RTYPE; - ctrl.dbglvl = ONMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - - ctrl.oflags = 0; - ctrl.pfactor = 0; - ctrl.nseps = 1; - ctrl.optype = OP_OEMETIS; - ctrl.CoarsenTo = amin(100, *nvtxs-1); - ctrl.maxvwgt = 1.5*tvwgt/ctrl.CoarsenTo; - - InitRandom(options[7]); - - AllocateWorkSpace(&ctrl, &graph, 2); - - /*============================================================ - * Perform the bisection - *============================================================*/ - tpwgts[0] = tvwgt/2; - tpwgts[1] = tvwgt-tpwgts[0]; - - MlevelEdgeBisection(&ctrl, &graph, tpwgts, 1.05); - ConstructMinCoverSeparator(&ctrl, &graph, 1.05); - - *sepsize = graph.pwgts[2]; - idxcopy(*nvtxs, graph.where, part); - - FreeGraph(&graph, 0); - - FreeWorkSpace(&ctrl, &graph); -} diff --git a/src/metis/pmetis.c b/src/metis/pmetis.c deleted file mode 100644 index f3611dc..0000000 --- a/src/metis/pmetis.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * pmetis.c - * - * This file contains the top level routines for the multilevel recursive - * bisection algorithm PMETIS. - * - * Started 7/24/97 - * George - * - * $Id: pmetis.c,v 1.2 2002/08/10 06:29:33 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function is the entry point for PMETIS -**************************************************************************/ -void METIS_PartGraphRecursive(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i; - float *tpwgts; - - tpwgts = gk_fmalloc(*nparts, "KMETIS: tpwgts"); - for (i=0; i<*nparts; i++) - tpwgts[i] = 1.0/(1.0*(*nparts)); - - METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, - tpwgts, options, edgecut, part); - - gk_free((void **)&tpwgts, LTERM); -} - - - -/************************************************************************* -* This function is the entry point for PWMETIS that accepts exact weights -* for the target partitions -**************************************************************************/ -void METIS_WPartGraphRecursive(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - float *mytpwgts; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - SetUpGraph(&graph, OP_PMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, *wgtflag); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = PMETIS_CTYPE; - ctrl.IType = PMETIS_ITYPE; - ctrl.RType = PMETIS_RTYPE; - ctrl.dbglvl = PMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_PMETIS; - ctrl.CoarsenTo = 20; - ctrl.maxvwgt = 1.5*(idxsum(*nvtxs, graph.vwgt, 1)/ctrl.CoarsenTo); - - mytpwgts = gk_fmalloc(*nparts, "PWMETIS: mytpwgts"); - for (i=0; i<*nparts; i++) - mytpwgts[i] = tpwgts[i]; - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MlevelRecursiveBisection(&ctrl, &graph, *nparts, part, mytpwgts, 1.000, 0); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - gk_free((void **)&mytpwgts, LTERM); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); -} - - - -/************************************************************************* -* This function takes a graph and produces a bisection of it -**************************************************************************/ -idxtype MlevelRecursiveBisection(CtrlType *ctrl, GraphType *graph, - idxtype nparts, idxtype *part, float *tpwgts, float ubfactor, - idxtype fpart) -{ - idxtype i, j, nvtxs, cut, tvwgt, tpwgts2[2]; - idxtype *label, *where; - GraphType lgraph, rgraph; - float wsum; - - nvtxs = graph->nvtxs; - if (nvtxs == 0) { - mprintf("\t***Cannot bisect a graph with 0 vertices!\n\t***You are trying to partition a graph into too many parts!\n"); - return 0; - } - - /* Determine the weights of the partitions */ - tvwgt = idxsum(nvtxs, graph->vwgt, 1); - tpwgts2[0] = tvwgt*gk_fsum(nparts/2, tpwgts, 1); - tpwgts2[1] = tvwgt-tpwgts2[0]; - - MlevelEdgeBisection(ctrl, graph, tpwgts2, ubfactor); - cut = graph->mincut; - - /* mprintf("%5D %5D %5D [%5D %f]\n", tpwgts2[0], tpwgts2[1], cut, tvwgt, gk_fsum(nparts/2, tpwgts, 1));*/ - - label = graph->label; - where = graph->where; - for (i=0; i 2) { - SplitGraphPart(ctrl, graph, &lgraph, &rgraph); - /* mprintf("%D %D\n", lgraph.nvtxs, rgraph.nvtxs); */ - } - - - /* Free the memory of the top level graph */ - FreeGraph(graph, 0); - - /* Scale the fractions in the tpwgts according to the true weight */ - wsum = gk_fsum(nparts/2, tpwgts, 1); - gk_fscale(nparts/2, 1.0/wsum, tpwgts, 1); - gk_fscale(nparts-nparts/2, 1.0/(1.0-wsum), tpwgts+nparts/2, 1); - /* - for (i=0; i 3) { - cut += MlevelRecursiveBisection(ctrl, &lgraph, nparts/2, part, tpwgts, ubfactor, fpart); - cut += MlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, tpwgts+nparts/2, ubfactor, fpart+nparts/2); - } - else if (nparts == 3) { - cut += MlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, tpwgts+nparts/2, ubfactor, fpart+nparts/2); - FreeGraph(&lgraph, 0); - } - - return cut; -} - - -/************************************************************************* -* This function performs multilevel bisection -**************************************************************************/ -void MlevelEdgeBisection(CtrlType *ctrl, GraphType *graph, idxtype *tpwgts, float ubfactor) -{ - GraphType *cgraph; - - cgraph = Coarsen2Way(ctrl, graph); - - Init2WayPartition(ctrl, cgraph, tpwgts, ubfactor); - - Refine2Way(ctrl, graph, cgraph, tpwgts, ubfactor); - -/* - IsConnectedSubdomain(ctrl, graph, 0); - IsConnectedSubdomain(ctrl, graph, 1); -*/ -} - - - - -/************************************************************************* -* This function takes a graph and a bisection and splits it into two graphs. -**************************************************************************/ -void SplitGraphPart(CtrlType *ctrl, GraphType *graph, GraphType *lgraph, GraphType *rgraph) -{ - idxtype i, j, k, kk, l, istart, iend, mypart, nvtxs, ncon, snvtxs[2], snedges[2], sum; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *adjwgtsum, *label, *where, *bndptr; - idxtype *sxadj[2], *svwgt[2], *sadjncy[2], *sadjwgt[2], *sadjwgtsum[2], *slabel[2]; - idxtype *rename; - idxtype *auxadjncy, *auxadjwgt; - float *nvwgt, *snvwgt[2], *npwgts; - - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->SplitTmr)); - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - vwgt = graph->vwgt; - nvwgt = graph->nvwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - label = graph->label; - where = graph->where; - bndptr = graph->bndptr; - npwgts = graph->npwgts; - - ASSERT(bndptr != NULL); - - rename = idxwspacemalloc(ctrl, nvtxs); - - snvtxs[0] = snvtxs[1] = snedges[0] = snedges[1] = 0; - for (i=0; ixadj; - svwgt[0] = lgraph->vwgt; - snvwgt[0] = lgraph->nvwgt; - sadjwgtsum[0] = lgraph->adjwgtsum; - sadjncy[0] = lgraph->adjncy; - sadjwgt[0] = lgraph->adjwgt; - slabel[0] = lgraph->label; - - SetUpSplitGraph(graph, rgraph, snvtxs[1], snedges[1]); - sxadj[1] = rgraph->xadj; - svwgt[1] = rgraph->vwgt; - snvwgt[1] = rgraph->nvwgt; - sadjwgtsum[1] = rgraph->adjwgtsum; - sadjncy[1] = rgraph->adjncy; - sadjwgt[1] = rgraph->adjwgt; - slabel[1] = rgraph->label; - - snvtxs[0] = snvtxs[1] = snedges[0] = snedges[1] = 0; - sxadj[0][0] = sxadj[1][0] = 0; - for (i=0; inedges = snedges[0]; - rgraph->nedges = snedges[1]; - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->SplitTmr)); - - idxwspacefree(ctrl, nvtxs); -} - - -/************************************************************************* -* Setup the various arrays for the splitted graph -**************************************************************************/ -void SetUpSplitGraph(GraphType *graph, GraphType *sgraph, idxtype snvtxs, idxtype snedges) -{ - InitGraph(sgraph); - sgraph->nvtxs = snvtxs; - sgraph->nedges = snedges; - sgraph->ncon = graph->ncon; - - /* Allocate memory for the splitted graph */ - sgraph->xadj = idxmalloc(snvtxs+1, "SetUpSplitGraph: xadj"); - sgraph->adjwgtsum = idxmalloc(snvtxs, "SetUpSplitGraph: adjwgtsum"); - sgraph->cmap = idxmalloc(snvtxs, "SetUpSplitGraph: cmap"); - sgraph->adjncy = idxmalloc(snedges, "SetUpSplitGraph: adjncy"); - sgraph->adjwgt = idxmalloc(snedges, "SetUpSplitGraph: adjwgt"); - sgraph->label = idxmalloc(snvtxs, "SetUpSplitGraph: label"); - - if (graph->ncon == 1) - sgraph->vwgt = idxmalloc(snvtxs, "SetUpSplitGraph: vwgt"); - else - sgraph->nvwgt = gk_fmalloc(graph->ncon*snvtxs, "SetUpSplitGraph: nvwgt"); -} - diff --git a/src/metis/pqueue.c b/src/metis/pqueue.c deleted file mode 100644 index ba7b4f3..0000000 --- a/src/metis/pqueue.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * pqueue.c - * - * This file contains functions for manipulating the bucket list - * representation of the gains associated with each vertex in a graph. - * These functions are used by the refinement algorithms - * - * Started 9/2/94 - * George - * - * $Id: pqueue.c,v 1.2 2002/08/10 06:29:34 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function initializes the data structures of the priority queue -**************************************************************************/ -void PQueueInit(CtrlType *ctrl, PQueueType *queue, idxtype maxnodes, idxtype maxgain) -{ - idxtype i, j, ncore; - - queue->nnodes = 0; - queue->maxnodes = maxnodes; - - queue->buckets = NULL; - queue->nodes = NULL; - queue->heap = NULL; - queue->locator = NULL; - - if (maxgain > PLUS_GAINSPAN || maxnodes < 500) - queue->type = 2; - else - queue->type = 1; - - if (queue->type == 1) { - queue->pgainspan = amin(PLUS_GAINSPAN, maxgain); - queue->ngainspan = amin(NEG_GAINSPAN, maxgain); - - j = queue->ngainspan+queue->pgainspan+1; - - ncore = 2 + (sizeof(ListNodeType)/sizeof(idxtype))*maxnodes + (sizeof(ListNodeType *)/sizeof(idxtype))*j; - - if (WspaceAvail(ctrl) > ncore) { - queue->nodes = (ListNodeType *)idxwspacemalloc(ctrl, (sizeof(ListNodeType)/sizeof(idxtype))*maxnodes); - queue->buckets = (ListNodeType **)idxwspacemalloc(ctrl, (sizeof(ListNodeType *)/sizeof(idxtype))*j); - queue->mustfree = 0; - } - else { /* Not enough memory in the wspace, allocate it */ - queue->nodes = (ListNodeType *)gk_malloc(maxnodes*sizeof(ListNodeType), "PQueueInit: queue->nodes"); - queue->buckets = (ListNodeType **)gk_malloc(j*sizeof(ListNodeType *), "PQueueInit: queue->buckets"); - queue->mustfree = 1; - } - - for (i=0; inodes[i].id = i; - - for (i=0; ibuckets[i] = NULL; - - queue->buckets += queue->ngainspan; /* Advance buckets by the ngainspan proper indexing */ - queue->maxgain = -queue->ngainspan; - } - else { - queue->heap = (KeyValueType *)idxwspacemalloc(ctrl, (sizeof(KeyValueType)/sizeof(idxtype))*maxnodes); - queue->locator = idxwspacemalloc(ctrl, maxnodes); - idxset(maxnodes, -1, queue->locator); - } -} - - -/************************************************************************* -* This function resets the buckets -**************************************************************************/ -void PQueueReset(PQueueType *queue) -{ - idxtype i, j; - queue->nnodes = 0; - - if (queue->type == 1) { - queue->maxgain = -queue->ngainspan; - - j = queue->ngainspan+queue->pgainspan+1; - queue->buckets -= queue->ngainspan; - for (i=0; ibuckets[i] = NULL; - queue->buckets += queue->ngainspan; - } - else { - idxset(queue->maxnodes, -1, queue->locator); - } - -} - - -/************************************************************************* -* This function frees the buckets -**************************************************************************/ -void PQueueFree(CtrlType *ctrl, PQueueType *queue) -{ - - if (queue->type == 1) { - if (queue->mustfree) { - queue->buckets -= queue->ngainspan; - gk_free((void **)&queue->nodes, &queue->buckets, LTERM); - } - else { - idxwspacefree(ctrl, sizeof(ListNodeType *)*(queue->ngainspan+queue->pgainspan+1)/sizeof(idxtype)); - idxwspacefree(ctrl, sizeof(ListNodeType)*queue->maxnodes/sizeof(idxtype)); - } - } - else { - idxwspacefree(ctrl, sizeof(KeyValueType)*queue->maxnodes/sizeof(idxtype)); - idxwspacefree(ctrl, queue->maxnodes); - } - - queue->maxnodes = 0; -} - - -/************************************************************************* -* This function returns the number of nodes in the queue -**************************************************************************/ -idxtype PQueueGetSize(PQueueType *queue) -{ - return queue->nnodes; -} - - -/************************************************************************* -* This function adds a node of certain gain into a partition -**************************************************************************/ -idxtype PQueueInsert(PQueueType *queue, idxtype node, idxtype gain) -{ - idxtype i, j, k; - idxtype *locator; - ListNodeType *newnode; - KeyValueType *heap; - - if (queue->type == 1) { - ASSERT(gain >= -queue->ngainspan && gain <= queue->pgainspan); - - /* Allocate and add the node */ - queue->nnodes++; - newnode = queue->nodes + node; - - /* Attach this node in the doubly-linked list */ - newnode->next = queue->buckets[gain]; - newnode->prev = NULL; - if (newnode->next != NULL) - newnode->next->prev = newnode; - queue->buckets[gain] = newnode; - - if (queue->maxgain < gain) - queue->maxgain = gain; - } - else { - ASSERT(CheckHeap(queue)); - - heap = queue->heap; - locator = queue->locator; - - ASSERT(locator[node] == -1); - - i = queue->nnodes++; - while (i > 0) { - j = (i-1)/2; - if (heap[j].key < gain) { - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else - break; - } - ASSERT(i >= 0); - heap[i].key = gain; - heap[i].val = node; - locator[node] = i; - - ASSERT(CheckHeap(queue)); - } - - return 0; -} - - -/************************************************************************* -* This function deletes a node from a partition and reinserts it with -* an updated gain -**************************************************************************/ -idxtype PQueueDelete(PQueueType *queue, idxtype node, idxtype gain) -{ - idxtype i, j, newgain, oldgain; - idxtype *locator; - ListNodeType *newnode, **buckets; - KeyValueType *heap; - - if (queue->type == 1) { - ASSERT(gain >= -queue->ngainspan && gain <= queue->pgainspan); - ASSERT(queue->nnodes > 0); - - buckets = queue->buckets; - queue->nnodes--; - newnode = queue->nodes+node; - - /* Remove newnode from the doubly-linked list */ - if (newnode->prev != NULL) - newnode->prev->next = newnode->next; - else - buckets[gain] = newnode->next; - if (newnode->next != NULL) - newnode->next->prev = newnode->prev; - - if (buckets[gain] == NULL && gain == queue->maxgain) { - if (queue->nnodes == 0) - queue->maxgain = -queue->ngainspan; - else - for (; buckets[queue->maxgain]==NULL; queue->maxgain--); - } - } - else { /* Heap Priority Queue */ - heap = queue->heap; - locator = queue->locator; - - ASSERT(locator[node] != -1); - ASSERT(heap[locator[node]].val == node); - - ASSERT(CheckHeap(queue)); - - i = locator[node]; - locator[node] = -1; - - if (--queue->nnodes > 0 && heap[queue->nnodes].val != node) { - node = heap[queue->nnodes].val; - newgain = heap[queue->nnodes].key; - oldgain = heap[i].key; - - if (oldgain < newgain) { /* Filter-up */ - while (i > 0) { - j = (i-1)>>1; - if (heap[j].key < newgain) { - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else - break; - } - } - else { /* Filter down */ - while ((j=2*i+1) < queue->nnodes) { - if (heap[j].key > newgain) { - if (j+1 < queue->nnodes && heap[j+1].key > heap[j].key) - j = j+1; - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else if (j+1 < queue->nnodes && heap[j+1].key > newgain) { - j = j+1; - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else - break; - } - } - - heap[i].key = newgain; - heap[i].val = node; - locator[node] = i; - } - - ASSERT(CheckHeap(queue)); - } - - return 0; -} - - - -/************************************************************************* -* This function deletes a node from a partition and reinserts it with -* an updated gain -**************************************************************************/ -idxtype PQueueUpdate(PQueueType *queue, idxtype node, idxtype oldgain, idxtype newgain) -{ - idxtype i, j; - idxtype *locator; - ListNodeType *newnode; - KeyValueType *heap; - - if (oldgain == newgain) - return 0; - - if (queue->type == 1) { - /* First delete the node and then insert it */ - PQueueDelete(queue, node, oldgain); - return PQueueInsert(queue, node, newgain); - } - else { /* Heap Priority Queue */ - heap = queue->heap; - locator = queue->locator; - - ASSERT(locator[node] != -1); - ASSERT(heap[locator[node]].val == node); - ASSERT(heap[locator[node]].key == oldgain); - ASSERT(CheckHeap(queue)); - - i = locator[node]; - - if (oldgain < newgain) { /* Filter-up */ - while (i > 0) { - j = (i-1)>>1; - if (heap[j].key < newgain) { - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else - break; - } - } - else { /* Filter down */ - while ((j=2*i+1) < queue->nnodes) { - if (heap[j].key > newgain) { - if (j+1 < queue->nnodes && heap[j+1].key > heap[j].key) - j = j+1; - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else if (j+1 < queue->nnodes && heap[j+1].key > newgain) { - j = j+1; - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else - break; - } - } - - heap[i].key = newgain; - heap[i].val = node; - locator[node] = i; - - ASSERT(CheckHeap(queue)); - } - - return 0; -} - - - -/************************************************************************* -* This function deletes a node from a partition and reinserts it with -* an updated gain -**************************************************************************/ -void PQueueUpdateUp(PQueueType *queue, idxtype node, idxtype oldgain, idxtype newgain) -{ - idxtype i, j; - idxtype *locator; - ListNodeType *newnode, **buckets; - KeyValueType *heap; - - if (oldgain == newgain) - return; - - if (queue->type == 1) { - ASSERT(oldgain >= -queue->ngainspan && oldgain <= queue->pgainspan); - ASSERT(newgain >= -queue->ngainspan && newgain <= queue->pgainspan); - ASSERT(queue->nnodes > 0); - - buckets = queue->buckets; - newnode = queue->nodes+node; - - /* First delete the node */ - if (newnode->prev != NULL) - newnode->prev->next = newnode->next; - else - buckets[oldgain] = newnode->next; - if (newnode->next != NULL) - newnode->next->prev = newnode->prev; - - /* Attach this node in the doubly-linked list */ - newnode->next = buckets[newgain]; - newnode->prev = NULL; - if (newnode->next != NULL) - newnode->next->prev = newnode; - buckets[newgain] = newnode; - - if (queue->maxgain < newgain) - queue->maxgain = newgain; - } - else { /* Heap Priority Queue */ - heap = queue->heap; - locator = queue->locator; - - ASSERT(locator[node] != -1); - ASSERT(heap[locator[node]].val == node); - ASSERT(heap[locator[node]].key == oldgain); - ASSERT(CheckHeap(queue)); - - - /* Here we are just filtering up since the newgain is greater than the oldgain */ - i = locator[node]; - while (i > 0) { - j = (i-1)>>1; - if (heap[j].key < newgain) { - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else - break; - } - - heap[i].key = newgain; - heap[i].val = node; - locator[node] = i; - - ASSERT(CheckHeap(queue)); - } - -} - - -/************************************************************************* -* This function returns the vertex with the largest gain from a partition -* and removes the node from the bucket list -**************************************************************************/ -idxtype PQueueGetMax(PQueueType *queue) -{ - idxtype vtx, i, j, gain, node; - idxtype *locator; - ListNodeType *tptr; - KeyValueType *heap; - - if (queue->nnodes == 0) - return -1; - - queue->nnodes--; - - if (queue->type == 1) { - tptr = queue->buckets[queue->maxgain]; - queue->buckets[queue->maxgain] = tptr->next; - if (tptr->next != NULL) { - tptr->next->prev = NULL; - } - else { - if (queue->nnodes == 0) { - queue->maxgain = -queue->ngainspan; - } - else - for (; queue->buckets[queue->maxgain]==NULL; queue->maxgain--); - } - - return tptr->id; - } - else { - heap = queue->heap; - locator = queue->locator; - - vtx = heap[0].val; - locator[vtx] = -1; - - if ((i = queue->nnodes) > 0) { - gain = heap[i].key; - node = heap[i].val; - i = 0; - while ((j=2*i+1) < queue->nnodes) { - if (heap[j].key > gain) { - if (j+1 < queue->nnodes && heap[j+1].key > heap[j].key) - j = j+1; - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else if (j+1 < queue->nnodes && heap[j+1].key > gain) { - j = j+1; - heap[i] = heap[j]; - locator[heap[i].val] = i; - i = j; - } - else - break; - } - - heap[i].key = gain; - heap[i].val = node; - locator[node] = i; - } - - ASSERT(CheckHeap(queue)); - return vtx; - } -} - - -/************************************************************************* -* This function returns the vertex with the largest gain from a partition -**************************************************************************/ -idxtype PQueueSeeMax(PQueueType *queue) -{ - idxtype vtx; - - if (queue->nnodes == 0) - return -1; - - if (queue->type == 1) - vtx = queue->buckets[queue->maxgain]->id; - else - vtx = queue->heap[0].val; - - return vtx; -} - - -/************************************************************************* -* This function returns the vertex with the largest gain from a partition -**************************************************************************/ -idxtype PQueueGetKey(PQueueType *queue) -{ - idxtype key; - - if (queue->nnodes == 0) - return -1; - - if (queue->type == 1) - key = queue->maxgain; - else - key = queue->heap[0].key; - - return key; -} - - - - -/************************************************************************* -* This functions checks the consistency of the heap -**************************************************************************/ -idxtype CheckHeap(PQueueType *queue) -{ - idxtype i, j, nnodes; - idxtype *locator; - KeyValueType *heap; - - heap = queue->heap; - locator = queue->locator; - nnodes = queue->nnodes; - - if (nnodes == 0) - return 1; - - ASSERT(locator[heap[0].val] == 0); - for (i=1; imaxnodes; i++) { - if (locator[i] != -1) - j++; - } - ASSERTP(j == nnodes, ("%d %d\n", j, nnodes)); - - return 1; -} diff --git a/src/metis/programs/CMakeLists.txt b/src/metis/programs/CMakeLists.txt new file mode 100644 index 0000000..3aaf357 --- /dev/null +++ b/src/metis/programs/CMakeLists.txt @@ -0,0 +1,31 @@ +# These programs use internal metis data structures. +include_directories(../libmetis) +link_directories(/home/karypis/local/lib) +# Build program. +add_executable(gpmetis gpmetis.c cmdline_gpmetis.c io.c stat.c) +add_executable(ndmetis ndmetis.c cmdline_ndmetis.c io.c smbfactor.c) +add_executable(mpmetis mpmetis.c cmdline_mpmetis.c io.c stat.c) +add_executable(m2gmetis m2gmetis.c cmdline_m2gmetis.c io.c) +add_executable(graphchk graphchk.c io.c) +add_executable(cmpfillin cmpfillin.c io.c smbfactor.c) +foreach(prog gpmetis ndmetis mpmetis m2gmetis graphchk cmpfillin) + target_link_libraries(${prog} metis) +# target_link_libraries(${prog} metis profiler) +endforeach(prog) + +if(METIS_INSTALL) + install(TARGETS gpmetis ndmetis mpmetis m2gmetis graphchk cmpfillin + RUNTIME DESTINATION bin) +endif() + +# Try to find subversion revision. +set(SVNREV "") +file(TO_NATIVE_PATH ${PROJECT_SOURCE_DIR}/.svn svn_dir) +if(IS_DIRECTORY ${svn_dir}) + include(FindSubversion) + if(Subversion_FOUND) + Subversion_WC_INFO(${PROJECT_SOURCE_DIR} metis) + set(SVNREV ${metis_WC_REVISION}) + endif(Subversion_FOUND) +endif() +add_definitions(-DSVNINFO="${SVNREV}") diff --git a/src/metis/programs/cmdline_gpmetis.c b/src/metis/programs/cmdline_gpmetis.c new file mode 100644 index 0000000..d2a193c --- /dev/null +++ b/src/metis/programs/cmdline_gpmetis.c @@ -0,0 +1,391 @@ +/*! +\file cmdline_gpmetis.c +\brief Command-line argument parsing for gpmetis + +\date 12/24/2008 +\author George +\version\verbatim $Id: cmdline_gpmetis.c 13901 2013-03-24 16:17:03Z karypis $\endverbatim +*/ + +#include "metisbin.h" + + +/*------------------------------------------------------------------- + * Command-line options + *-------------------------------------------------------------------*/ +static struct gk_option long_options[] = { + {"ptype", 1, 0, METIS_OPTION_PTYPE}, + {"objtype", 1, 0, METIS_OPTION_OBJTYPE}, + + {"ctype", 1, 0, METIS_OPTION_CTYPE}, + {"iptype", 1, 0, METIS_OPTION_IPTYPE}, +/* {"rtype", 1, 0, METIS_OPTION_RTYPE}, */ + +/* {"balanced", 0, 0, METIS_OPTION_BALANCE}, */ + + {"no2hop", 0, 0, METIS_OPTION_NO2HOP}, + {"minconn", 0, 0, METIS_OPTION_MINCONN}, + {"contig", 0, 0, METIS_OPTION_CONTIG}, + + {"nooutput", 0, 0, METIS_OPTION_NOOUTPUT}, + + {"ufactor", 1, 0, METIS_OPTION_UFACTOR}, + {"niter", 1, 0, METIS_OPTION_NITER}, + {"ncuts", 1, 0, METIS_OPTION_NCUTS}, + + {"tpwgts", 1, 0, METIS_OPTION_TPWGTS}, + {"ubvec", 1, 0, METIS_OPTION_UBVEC}, + + {"seed", 1, 0, METIS_OPTION_SEED}, + + {"dbglvl", 1, 0, METIS_OPTION_DBGLVL}, + + {"help", 0, 0, METIS_OPTION_HELP}, + {0, 0, 0, 0} +}; + + + +/*------------------------------------------------------------------- + * Mappings for the various parameter values + *-------------------------------------------------------------------*/ +static gk_StringMap_t ptype_options[] = { + {"rb", METIS_PTYPE_RB}, + {"kway", METIS_PTYPE_KWAY}, + {NULL, 0} +}; + +static gk_StringMap_t objtype_options[] = { + {"cut", METIS_OBJTYPE_CUT}, + {"vol", METIS_OBJTYPE_VOL}, + {NULL, 0} +}; + +static gk_StringMap_t ctype_options[] = { + {"rm", METIS_CTYPE_RM}, + {"shem", METIS_CTYPE_SHEM}, + {NULL, 0} +}; + +static gk_StringMap_t iptype_options[] = { + {"grow", METIS_IPTYPE_GROW}, + {"random", METIS_IPTYPE_RANDOM}, + {NULL, 0} +}; + +static gk_StringMap_t rtype_options[] = { + {"fm", METIS_RTYPE_FM}, + {"greedy", METIS_RTYPE_GREEDY}, + {NULL, 0} +}; + + + +/*------------------------------------------------------------------- + * Mini help + *-------------------------------------------------------------------*/ +static char helpstr[][100] = +{ +" ", +"Usage: gpmetis [options] graphfile nparts", +" ", +" Required parameters", +" graphfile Stores the graph to be partitioned.", +" nparts The number of partitions to split the graph.", +" ", +" Optional parameters", +" -ptype=string", +" Specifies the scheme to be used for computing the k-way partitioning.", +" The possible values are:", +" rb - Recursive bisectioning", +" kway - Direct k-way partitioning [default]", +" ", +" -ctype=string", +" Specifies the scheme to be used to match the vertices of the graph", +" during the coarsening.", +" The possible values are:", +" rm - Random matching", +" shem - Sorted heavy-edge matching [default]", +" ", +" -iptype=string [applies only when -ptype=rb]", +" Specifies the scheme to be used to compute the initial partitioning", +" of the graph.", +" The possible values are:", +" grow - Grow a bisection using a greedy scheme [default for ncon=1]", +" random - Compute a bisection at random [default for ncon>1]", +" ", +" -objtype=string [applies only when -ptype=kway]", +" Specifies the objective that the partitioning routines will optimize.", +" The possible values are:", +" cut - Minimize the edgecut [default]", +" vol - Minimize the total communication volume", +" ", +/* +" -rtype=string", +" Specifies the scheme to be used for refinement.", +" The possible values are:", +" fm - 2-way FM refinement [default for -ptype=rb]", +" random - Random k-way refinement", +" greedy - Greedy k-way refinement [default for -ptype=kway]", +" ", +*/ +" -no2hop", +" Specifies that the coarsening will not perform any 2-hop matchings", +" when the standard matching fails to sufficiently contract the graph.", +" ", +" -contig [applies only when -ptype=kway]", +" Specifies that the partitioning routines should try to produce", +" partitions that are contiguous. Note that if the input graph is not", +" connected this option is ignored.", +" ", +" -minconn [applies only when -ptype=kway]", +" Specifies that the partitioning routines should try to minimize the", +" maximum degree of the subdomain graph, i.e., the graph in which each", +" partition is a node, and edges connect subdomains with a shared", +" interface.", +" ", +" -tpwgts=filename", +" Specifies the name of the file that stores the target weights for", +" each partition. By default, all partitions are assumed to be of ", +" the same size.", +" ", +" -ufactor=int", +" Specifies the maximum allowed load imbalance among the partitions.", +" A value of x indicates that the allowed load imbalance is 1+x/1000.", +" For ptype=rb, the load imbalance is measured as the ratio of the ", +" 2*max(left,right)/(left+right), where left and right are the sizes", +" of the respective partitions at each bisection. ", +" For ptype=kway, the load imbalance is measured as the ratio of ", +" max_i(pwgts[i])/avgpwgt, where pwgts[i] is the weight of the ith", +" partition and avgpwgt is the sum of the total vertex weights divided", +" by the number of partitions requested.", +" For ptype=rb, the default value is 1 (i.e., load imbalance of 1.001).", +" For ptype=kway, the default value is 30 (i.e., load imbalance of 1.03).", +" ", +" -ubvec=string", +" Applies only for multi-constraint partitioning and specifies the per", +" constraint allowed load imbalance among partitions. The required ", +" parameter corresponds to a space separated set of floating point", +" numbers, one for each of the constraints. For example, for three", +" constraints, the string can be \"1.02 1.2 1.35\" indicating a ", +" desired maximum load imbalance of 2%, 20%, and 35%, respectively.", +" The load imbalance is defined in a way similar to ufactor.", +" If supplied, this parameter takes priority over ufactor.", +" ", +" -niter=int", +" Specifies the number of iterations for the refinement algorithms", +" at each stage of the uncoarsening process. Default is 10.", +" ", +" -ncuts=int", +" Specifies the number of different partitionings that it will compute.", +" The final partitioning is the one that achieves the best edgecut or", +" communication volume. Default is 1.", +" ", +" -nooutput", +" Specifies that no partitioning file should be generated.", +" ", +/* +" -balance", +" Specifies that the final partitioning should contain nparts-1 equal", +" size partitions with the last partition having upto nparts-1 fewer", +" vertices.", +" ", +*/ +" -seed=int", +" Selects the seed of the random number generator. ", +" ", +" -dbglvl=int ", +" Selects the dbglvl. ", +" ", +" -help", +" Prints this message.", +"" +}; + +static char shorthelpstr[][100] = { +" ", +" Usage: gpmetis [options] ", +" use 'gpmetis -help' for a summary of the options.", +"" +}; + + + +/************************************************************************* +* This is the entry point of the command-line argument parser +**************************************************************************/ +params_t *parse_cmdline(int argc, char *argv[]) +{ + int i, j, k; + int c, option_index; + params_t *params; + + params = (params_t *)gk_malloc(sizeof(params_t), "parse_cmdline"); + memset((void *)params, 0, sizeof(params_t)); + + /* initialize the params data structure */ + params->ptype = METIS_PTYPE_KWAY; + params->objtype = METIS_OBJTYPE_CUT; + params->ctype = METIS_CTYPE_SHEM; + params->iptype = -1; + params->rtype = -1; + + params->no2hop = 0; + params->minconn = 0; + params->contig = 0; + + params->nooutput = 0; + params->wgtflag = 3; + + params->ncuts = 1; + params->niter = 10; + + params->dbglvl = 0; + params->balance = 0; + params->seed = -1; + params->dbglvl = 0; + + params->tpwgtsfile = NULL; + + params->filename = NULL; + params->nparts = 1; + + params->ufactor = -1; + + params->ubvecstr = NULL; + params->ubvec = NULL; + + + gk_clearcputimer(params->iotimer); + gk_clearcputimer(params->parttimer); + gk_clearcputimer(params->reporttimer); + + + /* Parse the command line arguments */ + while ((c = gk_getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case METIS_OPTION_PTYPE: + if (gk_optarg) + if ((params->ptype = gk_GetStringID(ptype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + case METIS_OPTION_OBJTYPE: + if (gk_optarg) + if ((params->objtype = gk_GetStringID(objtype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + case METIS_OPTION_CTYPE: + if (gk_optarg) + if ((params->ctype = gk_GetStringID(ctype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + case METIS_OPTION_IPTYPE: + if (gk_optarg) + if ((params->iptype = gk_GetStringID(iptype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + +/* + case METIS_OPTION_RTYPE: + if (gk_optarg) + if ((params->rtype = gk_GetStringID(rtype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; +*/ + + case METIS_OPTION_NO2HOP: + params->no2hop = 1; + break; + + case METIS_OPTION_CONTIG: + params->contig = 1; + break; + + case METIS_OPTION_MINCONN: + params->minconn = 1; + break; + + case METIS_OPTION_NOOUTPUT: + params->nooutput = 1; + break; + + case METIS_OPTION_BALANCE: + params->balance = 1; + break; + + case METIS_OPTION_TPWGTS: + if (gk_optarg) params->tpwgtsfile = gk_strdup(gk_optarg); + break; + + case METIS_OPTION_UBVEC: + if (gk_optarg) params->ubvecstr = gk_strdup(gk_optarg); + break; + + case METIS_OPTION_NCUTS: + if (gk_optarg) params->ncuts = (idx_t)atoi(gk_optarg); + break; + case METIS_OPTION_NITER: + if (gk_optarg) params->niter = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_UFACTOR: + if (gk_optarg) params->ufactor = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_SEED: + if (gk_optarg) params->seed = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_DBGLVL: + if (gk_optarg) params->dbglvl = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_HELP: + for (i=0; strlen(helpstr[i]) > 0; i++) + printf("%s\n", helpstr[i]); + exit(0); + break; + case '?': + default: + errexit("Illegal command-line option(s)\n" + "Use %s -help for a summary of the options.\n", argv[0]); + } + } + + if (argc-gk_optind != 2) { + printf("Missing parameters."); + for (i=0; strlen(shorthelpstr[i]) > 0; i++) + printf("%s\n", shorthelpstr[i]); + exit(0); + } + + params->filename = gk_strdup(argv[gk_optind++]); + params->nparts = atoi(argv[gk_optind++]); + + if (params->nparts < 2) + errexit("The number of partitions should be greater than 1!\n"); + + + /* Set the ptype-specific defaults */ + if (params->ptype == METIS_PTYPE_RB) { + params->rtype = METIS_RTYPE_FM; + } + if (params->ptype == METIS_PTYPE_KWAY) { + params->iptype = METIS_IPTYPE_METISRB; + params->rtype = METIS_RTYPE_GREEDY; + } + + /* Check for invalid parameter combination */ + if (params->ptype == METIS_PTYPE_RB) { + if (params->contig) + errexit("***The -contig option cannot be specified with rb partitioning. Will be ignored.\n"); + if (params->minconn) + errexit("***The -minconn option cannot be specified with rb partitioning. Will be ignored. \n"); + if (params->objtype == METIS_OBJTYPE_VOL) + errexit("The -objtype=vol option cannot be specified with rb partitioning.\n"); + } + + return params; +} + + diff --git a/src/metis/programs/cmdline_m2gmetis.c b/src/metis/programs/cmdline_m2gmetis.c new file mode 100644 index 0000000..75610c3 --- /dev/null +++ b/src/metis/programs/cmdline_m2gmetis.c @@ -0,0 +1,148 @@ +/*! +\file cmdline_m2gmetis.c + +\brief Command-line argument parsing for m2gmetis + +\date 12/24/2008 +\author George +\version\verbatim $Id: cmdline_m2gmetis.c 10046 2011-06-01 14:13:40Z karypis $\endverbatim +*/ + +#include "metisbin.h" + + +/*------------------------------------------------------------------- + * Command-line options + *-------------------------------------------------------------------*/ +static struct gk_option long_options[] = { + {"gtype", 1, 0, METIS_OPTION_GTYPE}, + {"ncommon", 1, 0, METIS_OPTION_NCOMMON}, + + {"dbglvl", 1, 0, METIS_OPTION_DBGLVL}, + + {"help", 0, 0, METIS_OPTION_HELP}, + {0, 0, 0, 0} +}; + + + +/*------------------------------------------------------------------- + * Mappings for the various parameter values + *-------------------------------------------------------------------*/ +static gk_StringMap_t gtype_options[] = { + {"dual", METIS_GTYPE_DUAL}, + {"nodal", METIS_GTYPE_NODAL}, + {NULL, 0} +}; + + +/*------------------------------------------------------------------- + * Mini help + *-------------------------------------------------------------------*/ +static char helpstr[][100] = +{ +" ", +"Usage: m2gmetis [options] ", +" ", +" Required parameters", +" meshfile Stores the input mesh.", +" graphfile The filename of the output graph.", +" ", +" Optional parameters", +" -gtype=string", +" Specifies the graph that will be generated.", +" The possible values are:", +" dual - Generate dual graph of the mesh [default]", +" nodal - Generate the nodal graph of the mesh", +" ", +" -ncommon=int [applies when gtype=dual]", +" Specifies the common number of nodes that two elements must have", +" in order to put an edge between them in the dual graph. Default is 1.", +" ", +" -dbglvl=int ", +" Selects the dbglvl.", +" ", +" -help", +" Prints this message.", +"" +}; + +static char shorthelpstr[][100] = { +" ", +" Usage: m2gmetis [options] ", +" use 'm2gmetis -help' for a summary of the options.", +"" +}; + + + +/************************************************************************* +* This is the entry point of the command-line argument parser +**************************************************************************/ +params_t *parse_cmdline(int argc, char *argv[]) +{ + int i, j, k; + int c, option_index; + params_t *params; + + params = (params_t *)gk_malloc(sizeof(params_t), "parse_cmdline"); + memset((void *)params, 0, sizeof(params_t)); + + /* initialize the params data structure */ + params->gtype = METIS_GTYPE_DUAL; + params->ncommon = 1; + params->dbglvl = 0; + params->filename = NULL; + params->outfile = NULL; + + + gk_clearcputimer(params->iotimer); + gk_clearcputimer(params->parttimer); + gk_clearcputimer(params->reporttimer); + + + /* Parse the command line arguments */ + while ((c = gk_getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case METIS_OPTION_GTYPE: + if (gk_optarg) + if ((params->gtype = gk_GetStringID(gtype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + + case METIS_OPTION_NCOMMON: + if (gk_optarg) params->ncommon = (idx_t)atoi(gk_optarg); + if (params->ncommon < 1) + errexit("The -ncommon option should specify a number >= 1.\n"); + break; + + case METIS_OPTION_DBGLVL: + if (gk_optarg) params->dbglvl = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_HELP: + for (i=0; strlen(helpstr[i]) > 0; i++) + printf("%s\n", helpstr[i]); + exit(0); + break; + case '?': + default: + errexit("Illegal command-line option(s)\n" + "Use %s -help for a summary of the options.\n", argv[0]); + } + } + + if (argc-gk_optind != 2) { + printf("Missing parameters."); + for (i=0; strlen(shorthelpstr[i]) > 0; i++) + printf("%s\n", shorthelpstr[i]); + exit(0); + } + + params->filename = gk_strdup(argv[gk_optind++]); + params->outfile = gk_strdup(argv[gk_optind++]); + + return params; +} + + diff --git a/src/metis/programs/cmdline_mpmetis.c b/src/metis/programs/cmdline_mpmetis.c new file mode 100644 index 0000000..99c48ef --- /dev/null +++ b/src/metis/programs/cmdline_mpmetis.c @@ -0,0 +1,366 @@ +/*! +\file cmdline_mpmetis.c + +\brief Command-line argument parsing for mpmetis + +\date 12/24/2008 +\author George +\version\verbatim $Id: cmdline_mpmetis.c 13905 2013-03-25 13:21:20Z karypis $\endverbatim +*/ + +#include "metisbin.h" + + +/*------------------------------------------------------------------- + * Command-line options + *-------------------------------------------------------------------*/ +static struct gk_option long_options[] = { + {"gtype", 1, 0, METIS_OPTION_GTYPE}, + {"ptype", 1, 0, METIS_OPTION_PTYPE}, + {"objtype", 1, 0, METIS_OPTION_OBJTYPE}, + + {"ctype", 1, 0, METIS_OPTION_CTYPE}, + {"iptype", 1, 0, METIS_OPTION_IPTYPE}, + + {"minconn", 0, 0, METIS_OPTION_MINCONN}, + {"contig", 0, 0, METIS_OPTION_CONTIG}, + + {"nooutput", 0, 0, METIS_OPTION_NOOUTPUT}, + + {"ufactor", 1, 0, METIS_OPTION_UFACTOR}, + {"niter", 1, 0, METIS_OPTION_NITER}, + {"ncuts", 1, 0, METIS_OPTION_NCUTS}, + {"ncommon", 1, 0, METIS_OPTION_NCOMMON}, + + {"tpwgts", 1, 0, METIS_OPTION_TPWGTS}, + + {"seed", 1, 0, METIS_OPTION_SEED}, + + {"dbglvl", 1, 0, METIS_OPTION_DBGLVL}, + + {"help", 0, 0, METIS_OPTION_HELP}, + {0, 0, 0, 0} +}; + + + +/*------------------------------------------------------------------- + * Mappings for the various parameter values + *-------------------------------------------------------------------*/ +static gk_StringMap_t gtype_options[] = { + {"dual", METIS_GTYPE_DUAL}, + {"nodal", METIS_GTYPE_NODAL}, + {NULL, 0} +}; + +static gk_StringMap_t ptype_options[] = { + {"rb", METIS_PTYPE_RB}, + {"kway", METIS_PTYPE_KWAY}, + {NULL, 0} +}; + +static gk_StringMap_t objtype_options[] = { + {"cut", METIS_OBJTYPE_CUT}, + {"vol", METIS_OBJTYPE_VOL}, + {NULL, 0} +}; + +static gk_StringMap_t ctype_options[] = { + {"rm", METIS_CTYPE_RM}, + {"shem", METIS_CTYPE_SHEM}, + {NULL, 0} +}; + +static gk_StringMap_t iptype_options[] = { + {"grow", METIS_IPTYPE_GROW}, + {"random", METIS_IPTYPE_RANDOM}, + {NULL, 0} +}; + + +/*------------------------------------------------------------------- + * Mini help + *-------------------------------------------------------------------*/ +static char helpstr[][100] = +{ +" ", +"Usage: mpmetis [options] meshfile nparts", +" ", +" Required parameters", +" meshfile Stores the mesh to be partitioned.", +" nparts The number of partitions to split the mesh.", +" ", +" Optional parameters", +" -gtype=string", +" Specifies the graph to be used for computing the partitioning", +" The possible values are:", +" dual - Partition the dual graph of the mesh [default]", +" nodal - Partition the nodal graph of the mesh", +" ", +" -ptype=string", +" Specifies the scheme to be used for computing the k-way partitioning.", +" The possible values are:", +" rb - Recursive bisectioning", +" kway - Direct k-way partitioning [default]", +" ", +" -ctype=string", +" Specifies the scheme to be used to match the vertices of the graph", +" during the coarsening.", +" The possible values are:", +" rm - Random matching", +" shem - Sorted heavy-edge matching [default]", +" ", +" -iptype=string [applies only when -ptype=rb]", +" Specifies the scheme to be used to compute the initial partitioning", +" of the graph.", +" The possible values are:", +" grow - Grow a bisection using a greedy strategy [default]", +" random - Compute a bisection at random", +" ", +" -objtype=string [applies only when -ptype=kway]", +" Specifies the objective that the partitioning routines will optimize.", +" The possible values are:", +" cut - Minimize the edgecut [default]", +" vol - Minimize the total communication volume", +" ", +" -contig [applies only when -ptype=kway]", +" Specifies that the partitioning routines should try to produce", +" partitions that are contiguous. Note that if the input graph is not", +" connected this option is ignored.", +" ", +" -minconn [applies only when -ptype=kway]", +" Specifies that the partitioning routines should try to minimize the", +" maximum degree of the subdomain graph, i.e., the graph in which each", +" partition is a node, and edges connect subdomains with a shared", +" interface.", +" ", +" -tpwgts=filename", +" Specifies the name of the file that stores the target weights for", +" each partition. By default, all partitions are assumed to be of ", +" the same size.", +" ", +" -ufactor=int", +" Specifies the maximum allowed load imbalance among the partitions.", +" A value of x indicates that the allowed load imbalance is 1+x/1000.", +" For ptype=rb, the load imbalance is measured as the ratio of the ", +" 2*max(left,right)/(left+right), where left and right are the sizes", +" of the respective partitions at each bisection. ", +" For ptype=kway, the load imbalance is measured as the ratio of ", +" max_i(pwgts[i])/avgpwgt, where pwgts[i] is the weight of the ith", +" partition and avgpwgt is the sum of the total vertex weights divided", +" by the number of partitions requested.", +" For ptype=rb, the default value is 1 (i.e., load imbalance of 1.001).", +" For ptype=kway, the default value is 30 (i.e., load imbalance of 1.03).", +" ", +" -ncommon=int", +" Specifies the common number of nodes that two elements must have", +" in order to put an edge between them in the dual graph. Default is 1.", +" ", +" -niter=int", +" Specifies the number of iterations for the refinement algorithms", +" at each stage of the uncoarsening process. Default is 10.", +" ", +" -ncuts=int", +" Specifies the number of different partitionings that it will compute.", +" The final partitioning is the one that achieves the best edgecut or", +" communication volume. Default is 1.", +" ", +" -nooutput", +" Specifies that no partitioning file should be generated.", +" ", +" -seed=int", +" Selects the seed of the random number generator. ", +" ", +" -dbglvl=int ", +" Selects the dbglvl. ", +" ", +" -help", +" Prints this message.", +"" +}; + +static char shorthelpstr[][100] = { +" ", +" Usage: mpmetis [options] ", +" use 'mpmetis -help' for a summary of the options.", +"" +}; + + + +/************************************************************************* +* This is the entry point of the command-line argument parser +**************************************************************************/ +params_t *parse_cmdline(int argc, char *argv[]) +{ + int i, j, k; + int c, option_index; + params_t *params; + + params = (params_t *)gk_malloc(sizeof(params_t), "parse_cmdline"); + memset((void *)params, 0, sizeof(params_t)); + + /* initialize the params data structure */ + params->gtype = METIS_GTYPE_DUAL; + params->ptype = METIS_PTYPE_KWAY; + params->objtype = METIS_OBJTYPE_CUT; + params->ctype = METIS_CTYPE_SHEM; + params->iptype = METIS_IPTYPE_GROW; + params->rtype = -1; + + params->minconn = 0; + params->contig = 0; + + params->nooutput = 0; + params->wgtflag = 3; + + params->ncuts = 1; + params->niter = 10; + params->ncommon = 1; + + params->dbglvl = 0; + params->balance = 0; + params->seed = -1; + params->dbglvl = 0; + + params->tpwgtsfile = NULL; + + params->filename = NULL; + params->nparts = 1; + + params->ufactor = -1; + + gk_clearcputimer(params->iotimer); + gk_clearcputimer(params->parttimer); + gk_clearcputimer(params->reporttimer); + + + /* Parse the command line arguments */ + while ((c = gk_getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case METIS_OPTION_GTYPE: + if (gk_optarg) + if ((params->gtype = gk_GetStringID(gtype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + case METIS_OPTION_PTYPE: + if (gk_optarg) + if ((params->ptype = gk_GetStringID(ptype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + case METIS_OPTION_OBJTYPE: + if (gk_optarg) + if ((params->objtype = gk_GetStringID(objtype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + case METIS_OPTION_CTYPE: + if (gk_optarg) + if ((params->ctype = gk_GetStringID(ctype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + case METIS_OPTION_IPTYPE: + if (gk_optarg) + if ((params->iptype = gk_GetStringID(iptype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + +/* + case METIS_OPTION_RTYPE: + if (gk_optarg) + if ((params->rtype = gk_GetStringID(rtype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; +*/ + + case METIS_OPTION_CONTIG: + params->contig = 1; + break; + + case METIS_OPTION_MINCONN: + params->minconn = 1; + break; + + case METIS_OPTION_NOOUTPUT: + params->nooutput = 1; + break; + + case METIS_OPTION_BALANCE: + params->balance = 1; + break; + + case METIS_OPTION_TPWGTS: + if (gk_optarg) params->tpwgtsfile = gk_strdup(gk_optarg); + break; + + case METIS_OPTION_NCUTS: + if (gk_optarg) params->ncuts = (idx_t)atoi(gk_optarg); + break; + case METIS_OPTION_NITER: + if (gk_optarg) params->niter = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_NCOMMON: + if (gk_optarg) params->ncommon = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_UFACTOR: + if (gk_optarg) params->ufactor = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_SEED: + if (gk_optarg) params->seed = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_DBGLVL: + if (gk_optarg) params->dbglvl = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_HELP: + for (i=0; strlen(helpstr[i]) > 0; i++) + printf("%s\n", helpstr[i]); + exit(0); + break; + case '?': + default: + errexit("Illegal command-line option(s)\n" + "Use %s -help for a summary of the options.\n", argv[0]); + } + } + + if (argc-gk_optind != 2) { + printf("Missing parameters."); + for (i=0; strlen(shorthelpstr[i]) > 0; i++) + printf("%s\n", shorthelpstr[i]); + exit(0); + } + + params->filename = gk_strdup(argv[gk_optind++]); + params->nparts = atoi(argv[gk_optind++]); + + if (params->nparts < 2) + errexit("The number of partitions should be greater than 1!\n"); + + + /* Set the ptype-specific defaults */ + if (params->ptype == METIS_PTYPE_RB) { + params->rtype = METIS_RTYPE_FM; + } + if (params->ptype == METIS_PTYPE_KWAY) { + params->iptype = METIS_IPTYPE_METISRB; + params->rtype = METIS_RTYPE_GREEDY; + } + + /* Check for invalid parameter combination */ + if (params->ptype == METIS_PTYPE_RB) { + if (params->contig) + errexit("The -contig option cannot be specified with rb partitioning.\n"); + if (params->minconn) + errexit("The -minconn option cannot be specified with rb partitioning.\n"); + if (params->objtype == METIS_OBJTYPE_VOL) + errexit("The -objtype=vol option cannot be specified with rb partitioning.\n"); + } + + return params; +} + + diff --git a/src/metis/programs/cmdline_ndmetis.c b/src/metis/programs/cmdline_ndmetis.c new file mode 100644 index 0000000..5b0d6d0 --- /dev/null +++ b/src/metis/programs/cmdline_ndmetis.c @@ -0,0 +1,272 @@ +/*! +\file cmdline_ndmetis.c +\brief Command-line argument parsing for ndmetis + +\date 12/24/2008 +\author George +\version\verbatim $Id: cmdline_ndmetis.c 13900 2013-03-24 15:27:07Z karypis $\endverbatim +*/ + +#include "metisbin.h" + + +/*------------------------------------------------------------------- + * Command-line options + *-------------------------------------------------------------------*/ +static struct gk_option long_options[] = { + {"ctype", 1, 0, METIS_OPTION_CTYPE}, + {"iptype", 1, 0, METIS_OPTION_IPTYPE}, + {"rtype", 1, 0, METIS_OPTION_RTYPE}, + {"ufactor", 1, 0, METIS_OPTION_UFACTOR}, + {"pfactor", 1, 0, METIS_OPTION_PFACTOR}, + {"nocompress", 0, 0, METIS_OPTION_COMPRESS}, + {"ccorder", 0, 0, METIS_OPTION_CCORDER}, + {"no2hop", 0, 0, METIS_OPTION_NO2HOP}, + {"nooutput", 0, 0, METIS_OPTION_NOOUTPUT}, + {"niter", 1, 0, METIS_OPTION_NITER}, + {"nseps", 1, 0, METIS_OPTION_NSEPS}, + {"seed", 1, 0, METIS_OPTION_SEED}, + {"dbglvl", 1, 0, METIS_OPTION_DBGLVL}, + {"help", 0, 0, METIS_OPTION_HELP}, + {0, 0, 0, 0} +}; + + +static gk_StringMap_t ctype_options[] = { + {"rm", METIS_CTYPE_RM}, + {"shem", METIS_CTYPE_SHEM}, + {NULL, 0} +}; + +static gk_StringMap_t iptype_options[] = { + {"edge", METIS_IPTYPE_EDGE}, + {"node", METIS_IPTYPE_NODE}, + {NULL, 0} +}; + +static gk_StringMap_t rtype_options[] = { + {"2sided", METIS_RTYPE_SEP2SIDED}, + {"1sided", METIS_RTYPE_SEP1SIDED}, + {NULL, 0} +}; + + + +/*------------------------------------------------------------------- + * Mini help + *-------------------------------------------------------------------*/ +static char helpstr[][100] = +{ +" ", +"Usage: ndmetis [options] ", +" ", +" Required parameters", +" filename Stores the graph to be partitioned.", +" ", +" Optional parameters", +" -ctype=string", +" Specifies the scheme to be used to match the vertices of the graph", +" during the coarsening.", +" The possible values are:", +" rm - Random matching", +" shem - Sorted heavy-edge matching [default]", +" ", +" -iptype=string [applies only when -ptype=rb]", +" Specifies the scheme to be used to compute the initial bisection", +" of the graph.", +" The possible values are:", +" edge - Separator from an edge cut", +" node - Separator from a greedy node-based strategy [default]", +" ", +" -rtype=string", +" Specifies the scheme to be used for refinement.", +" The possible values are:", +" 1sided - 1-sided node-based refinement [default]", +" 2sided - 2-sided node-based refinement", +" ", +" -ufactor=int", +" Specifies the maximum allowed load imbalance between the left and", +" right partitions during each bisection. The load imbalanced is", +" measured as the ratio of the 2*max(left,right)/(left+right), where", +" left and right are the sizes of the respective partitions. ", +" A value of x indicates that the allowed load imbalance is 1+x/1000.", +" Default is 200, indicating a load imbalance of 1.20.", +" ", +" -pfactor=int", +" Specifies the minimum degree of the vertices that will be ordered ", +" last. If the specified value is x>0, then any vertices with a degree", +" greater than 0.1*x*(average degree) are removed from the graph, an", +" ordering of the rest of the vertices is computed, and an overall ", +" ordering is computed by ordering the removed vertices at the end ", +" of the overall ordering.", +" Default value is 0, indicating that no vertices are removed", +" ", +" -no2hop", +" Specifies that the coarsening will not perform any 2-hop matchings", +" when the standard matching fails to sufficiently contract the graph.", +" ", +" -nocompress", +" Specifies that the graph should not be compressed by combining", +" together vertices that have identical adjacency lists.", +" ", +" -ccorder", +" Specifies if the connected components of the graph should first be ", +" identified and ordered separately.", +" ", +" -niter=int", +" Specifies the maximum number of iterations for the refinement ", +" algorithms at each stage of the uncoarsening process. Default is 10.", +" ", +" -nseps=int", +" Specifies the number of different separators that it will compute at", +" each level of the nested dissection. The final separator that is used", +" is the smallest one. Default is 1.", +" ", +" -nooutput", +" Specifies that no ordering file should be generated.", +" ", +" -seed=int", +" Selects the seed of the random number generator. ", +" ", +" -dbglvl=int ", +" Selects the dbglvl. ", +" ", +" -help", +" Prints this message.", +"" +}; + +static char shorthelpstr[][100] = { +" ", +" Usage: ndmetis [options] ", +" use 'ndmetis -help' for a summary of the options.", +"" +}; + + + +/*************************************************************************/ +/*! This is the entry point of the command-line argument parser */ +/*************************************************************************/ +params_t *parse_cmdline(int argc, char *argv[]) +{ + int i, j, k; + int c, option_index; + params_t *params; + + params = (params_t *)gk_malloc(sizeof(params_t), "parse_cmdline"); + memset((void *)params, 0, sizeof(params_t)); + + /* initialize the params data structure */ + params->ctype = METIS_CTYPE_SHEM; + params->iptype = METIS_IPTYPE_NODE; + params->rtype = METIS_RTYPE_SEP1SIDED; + + params->ufactor = OMETIS_DEFAULT_UFACTOR; + params->pfactor = 0; + params->compress = 1; + params->ccorder = 0; + params->no2hop = 0; + + params->nooutput = 0; + params->wgtflag = 1; + + params->nseps = 1; + params->niter = 10; + + params->seed = -1; + params->dbglvl = 0; + + params->filename = NULL; + params->nparts = 1; + + + gk_clearcputimer(params->iotimer); + gk_clearcputimer(params->parttimer); + gk_clearcputimer(params->reporttimer); + + + /* Parse the command line arguments */ + while ((c = gk_getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case METIS_OPTION_CTYPE: + if (gk_optarg) + if ((params->ctype = gk_GetStringID(ctype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + case METIS_OPTION_IPTYPE: + if (gk_optarg) + if ((params->iptype = gk_GetStringID(iptype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + + case METIS_OPTION_RTYPE: + if (gk_optarg) + if ((params->rtype = gk_GetStringID(rtype_options, gk_optarg)) == -1) + errexit("Invalid option -%s=%s\n", long_options[option_index].name, gk_optarg); + break; + + case METIS_OPTION_UFACTOR: + if (gk_optarg) params->ufactor = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_PFACTOR: + if (gk_optarg) params->pfactor = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_COMPRESS: + params->compress = 0; + break; + + case METIS_OPTION_CCORDER: + params->ccorder = 1; + break; + + case METIS_OPTION_NO2HOP: + params->no2hop = 1; + break; + + case METIS_OPTION_NOOUTPUT: + params->nooutput = 1; + break; + + case METIS_OPTION_NSEPS: + if (gk_optarg) params->nseps = (idx_t)atoi(gk_optarg); + break; + case METIS_OPTION_NITER: + if (gk_optarg) params->niter = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_SEED: + if (gk_optarg) params->seed = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_DBGLVL: + if (gk_optarg) params->dbglvl = (idx_t)atoi(gk_optarg); + break; + + case METIS_OPTION_HELP: + for (i=0; strlen(helpstr[i]) > 0; i++) + printf("%s\n", helpstr[i]); + exit(0); + break; + case '?': + default: + errexit("Illegal command-line option(s)\n" + "Use %s -help for a summary of the options.\n", argv[0]); + } + } + + if (argc-gk_optind != 1) { + printf("Missing parameters."); + for (i=0; strlen(shorthelpstr[i]) > 0; i++) + printf("%s\n", shorthelpstr[i]); + exit(0); + } + + params->filename = gk_strdup(argv[gk_optind++]); + + return params; +} + + diff --git a/src/metis/programs/cmpfillin.c b/src/metis/programs/cmpfillin.c new file mode 100644 index 0000000..e1de447 --- /dev/null +++ b/src/metis/programs/cmpfillin.c @@ -0,0 +1,73 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * cmpfillin.c + * + * This file takes a graph and a fill-reducing ordering and computes + * the fillin. + * + * Started 9/1/2004 + * George + * + * $Id: cmpfillin.c 9982 2011-05-25 17:18:00Z karypis $ + * + */ + +#include "metisbin.h" + + + +/************************************************************************* +* Let the game begin +**************************************************************************/ +int main(int argc, char *argv[]) +{ + idx_t i; + idx_t *perm, *iperm; + graph_t *graph; + params_t params; + size_t maxlnz, opc; + + if (argc != 3) { + printf("Usage: %s nvtxs <= 0) { + printf("Empty graph. Nothing to do.\n"); + exit(0); + } + if (graph->ncon != 1) { + printf("Ordering can only be applied to graphs with one constraint.\n"); + exit(0); + } + + + /* Read the external iperm vector */ + perm = imalloc(graph->nvtxs, "main: perm"); + iperm = imalloc(graph->nvtxs, "main: iperm"); + ReadPOVector(graph, argv[2], iperm); + + for (i=0; invtxs; i++) + perm[iperm[i]] = i; + + printf("**********************************************************************\n"); + printf("%s", METISTITLE); + printf("Graph Information ---------------------------------------------------\n"); + printf(" Name: %s, #Vertices: %"PRIDX", #Edges: %"PRIDX"\n\n", argv[1], + graph->nvtxs, graph->nedges/2); + printf("Fillin... -----------------------------------------------------------\n"); + + ComputeFillIn(graph, perm, iperm, &maxlnz, &opc); + + printf(" Nonzeros: %6.3le \tOperation Count: %6.3le\n", (double)maxlnz, (double)opc); + + + printf("**********************************************************************\n"); + + FreeGraph(&graph); +} + + diff --git a/src/metis/programs/defs.h b/src/metis/programs/defs.h new file mode 100644 index 0000000..401a817 --- /dev/null +++ b/src/metis/programs/defs.h @@ -0,0 +1,59 @@ +/* + * defs.h + * + * This file contains various constant definitions + * + * Started 8/9/02 + * George + * + */ + +#define CMD_PTYPE 1 +#define CMD_OTYPE 2 +#define CMD_CTYPE 5 +#define CMD_ITYPE 6 +#define CMD_RTYPE 7 + +#define CMD_BALANCE 10 +#define CMD_CONTIG 11 +#define CMD_MINCONN 12 +#define CMD_MINVOL 13 + +#define CMD_NITER 20 +#define CMD_NTRIALS 21 +#define CMD_NSEPS 22 + +#define CMD_TPWGTS 30 +#define CMD_SDIFF 31 + +#define CMD_DEGREE 40 +#define CMD_COMPRESS 41 + +#define CMD_SEED 50 + +#define CMD_OUTPUT 100 +#define CMD_NOOUTPUT 101 + +#define CMD_DBGLVL 1000 +#define CMD_HELP 1001 + + + + +/* The text labels for PTypes */ +static char ptypenames[][15] = {"rb", "kway"}; + +/* The text labels for ObjTypes */ +static char objtypenames[][15] = {"cut", "vol", "node"}; + +/* The text labels for CTypes */ +static char ctypenames[][15] = {"rm", "shem"}; + +/* The text labels for RTypes */ +static char rtypenames[][15] = {"fm", "greedy", "2sided", "1sided"}; + +/* The text labels for ITypes */ +static char iptypenames[][15] = {"grow", "random", "edge", "node", "metisrb"}; + +/* The text labels for GTypes */ +static char gtypenames[][15] = {"dual", "nodal"}; diff --git a/src/metis/programs/gpmetis.c b/src/metis/programs/gpmetis.c new file mode 100644 index 0000000..ad119ff --- /dev/null +++ b/src/metis/programs/gpmetis.c @@ -0,0 +1,222 @@ +/* + * Copyright 1994-2011, Regents of the University of Minnesota + * + * gpmetis.c + * + * Drivers for the partitioning routines + * + * Started 8/28/94 + * George + * + * $Id: gpmetis.c 13900 2013-03-24 15:27:07Z karypis $ + * + */ + +#include "metisbin.h" + + + +/*************************************************************************/ +/*! Let the game begin! */ +/*************************************************************************/ +int main(int argc, char *argv[]) +{ + idx_t i; + char *curptr, *newptr; + idx_t options[METIS_NOPTIONS]; + graph_t *graph; + idx_t *part; + idx_t objval; + params_t *params; + int status=0; + + params = parse_cmdline(argc, argv); + + gk_startcputimer(params->iotimer); + graph = ReadGraph(params); + + ReadTPwgts(params, graph->ncon); + gk_stopcputimer(params->iotimer); + + /* Check if the graph is contiguous */ + if (params->contig && !IsConnected(graph, 0)) { + printf("***The input graph is not contiguous.\n" + "***The specified -contig option will be ignored.\n"); + params->contig = 0; + } + + /* Get ubvec if supplied */ + if (params->ubvecstr) { + params->ubvec = rmalloc(graph->ncon, "main"); + curptr = params->ubvecstr; + for (i=0; incon; i++) { + params->ubvec[i] = strtoreal(curptr, &newptr); + if (curptr == newptr) + errexit("Error parsing entry #%"PRIDX" of ubvec [%s] (possibly missing).\n", + i, params->ubvecstr); + curptr = newptr; + } + } + + /* Setup iptype */ + if (params->iptype == -1) { + if (params->ptype == METIS_PTYPE_RB) { + if (graph->ncon == 1) + params->iptype = METIS_IPTYPE_GROW; + else + params->iptype = METIS_IPTYPE_RANDOM; + } + } + + GPPrintInfo(params, graph); + + part = imalloc(graph->nvtxs, "main: part"); + + METIS_SetDefaultOptions(options); + options[METIS_OPTION_OBJTYPE] = params->objtype; + options[METIS_OPTION_CTYPE] = params->ctype; + options[METIS_OPTION_IPTYPE] = params->iptype; + options[METIS_OPTION_RTYPE] = params->rtype; + options[METIS_OPTION_NO2HOP] = params->no2hop; + options[METIS_OPTION_MINCONN] = params->minconn; + options[METIS_OPTION_CONTIG] = params->contig; + options[METIS_OPTION_SEED] = params->seed; + options[METIS_OPTION_NITER] = params->niter; + options[METIS_OPTION_NCUTS] = params->ncuts; + options[METIS_OPTION_UFACTOR] = params->ufactor; + options[METIS_OPTION_DBGLVL] = params->dbglvl; + + gk_malloc_init(); + gk_startcputimer(params->parttimer); + + switch (params->ptype) { + case METIS_PTYPE_RB: + status = METIS_PartGraphRecursive(&graph->nvtxs, &graph->ncon, graph->xadj, + graph->adjncy, graph->vwgt, graph->vsize, graph->adjwgt, + ¶ms->nparts, params->tpwgts, params->ubvec, options, + &objval, part); + break; + + case METIS_PTYPE_KWAY: + status = METIS_PartGraphKway(&graph->nvtxs, &graph->ncon, graph->xadj, + graph->adjncy, graph->vwgt, graph->vsize, graph->adjwgt, + ¶ms->nparts, params->tpwgts, params->ubvec, options, + &objval, part); + break; + + } + + gk_stopcputimer(params->parttimer); + + if (gk_GetCurMemoryUsed() != 0) + printf("***It seems that Metis did not free all of its memory! Report this.\n"); + params->maxmemory = gk_GetMaxMemoryUsed(); + gk_malloc_cleanup(0); + + + if (status != METIS_OK) { + printf("\n***Metis returned with an error.\n"); + } + else { + if (!params->nooutput) { + /* Write the solution */ + gk_startcputimer(params->iotimer); + WritePartition(params->filename, part, graph->nvtxs, params->nparts); + gk_stopcputimer(params->iotimer); + } + + GPReportResults(params, graph, part, objval); + } + + FreeGraph(&graph); + gk_free((void **)&part, LTERM); + gk_free((void **)¶ms->filename, ¶ms->tpwgtsfile, ¶ms->tpwgts, + ¶ms->ubvecstr, ¶ms->ubvec, ¶ms, LTERM); + +} + + +/*************************************************************************/ +/*! This function prints run parameters */ +/*************************************************************************/ +void GPPrintInfo(params_t *params, graph_t *graph) +{ + idx_t i; + + if (params->ufactor == -1) { + if (params->ptype == METIS_PTYPE_KWAY) + params->ufactor = KMETIS_DEFAULT_UFACTOR; + else if (graph->ncon == 1) + params->ufactor = PMETIS_DEFAULT_UFACTOR; + else + params->ufactor = MCPMETIS_DEFAULT_UFACTOR; + } + + printf("******************************************************************************\n"); + printf("%s", METISTITLE); + printf(" (HEAD: %s, Built on: %s, %s)\n", SVNINFO, __DATE__, __TIME__); + printf(" size of idx_t: %zubits, real_t: %zubits, idx_t *: %zubits\n", + 8*sizeof(idx_t), 8*sizeof(real_t), 8*sizeof(idx_t *)); + printf("\n"); + printf("Graph Information -----------------------------------------------------------\n"); + printf(" Name: %s, #Vertices: %"PRIDX", #Edges: %"PRIDX", #Parts: %"PRIDX"\n", + params->filename, graph->nvtxs, graph->nedges/2, params->nparts); + if (graph->ncon > 1) + printf(" Balancing constraints: %"PRIDX"\n", graph->ncon); + + printf("\n"); + printf("Options ---------------------------------------------------------------------\n"); + printf(" ptype=%s, objtype=%s, ctype=%s, rtype=%s, iptype=%s\n", + ptypenames[params->ptype], objtypenames[params->objtype], ctypenames[params->ctype], + rtypenames[params->rtype], iptypenames[params->iptype]); + + printf(" dbglvl=%"PRIDX", ufactor=%.3f, no2hop=%s, minconn=%s, contig=%s, nooutput=%s\n", + params->dbglvl, + I2RUBFACTOR(params->ufactor), + (params->no2hop ? "YES" : "NO"), + (params->minconn ? "YES" : "NO"), + (params->contig ? "YES" : "NO"), + (params->nooutput ? "YES" : "NO") + ); + + printf(" seed=%"PRIDX", niter=%"PRIDX", ncuts=%"PRIDX"\n", + params->seed, params->niter, params->ncuts); + + if (params->ubvec) { + printf(" ubvec=("); + for (i=0; incon; i++) + printf("%s%.2e", (i==0?"":" "), (double)params->ubvec[i]); + printf(")\n"); + } + + printf("\n"); + switch (params->ptype) { + case METIS_PTYPE_RB: + printf("Recursive Partitioning ------------------------------------------------------\n"); + break; + case METIS_PTYPE_KWAY: + printf("Direct k-way Partitioning ---------------------------------------------------\n"); + break; + } +} + + +/*************************************************************************/ +/*! This function does any post-partitioning reporting */ +/*************************************************************************/ +void GPReportResults(params_t *params, graph_t *graph, idx_t *part, idx_t objval) +{ + gk_startcputimer(params->reporttimer); + ComputePartitionInfo(params, graph, part); + + gk_stopcputimer(params->reporttimer); + + printf("\nTiming Information ----------------------------------------------------------\n"); + printf(" I/O: \t\t %7.3"PRREAL" sec\n", gk_getcputimer(params->iotimer)); + printf(" Partitioning: \t\t %7.3"PRREAL" sec (METIS time)\n", gk_getcputimer(params->parttimer)); + printf(" Reporting: \t\t %7.3"PRREAL" sec\n", gk_getcputimer(params->reporttimer)); + printf("\nMemory Information ----------------------------------------------------------\n"); + printf(" Max memory used:\t\t %7.3"PRREAL" MB\n", (real_t)(params->maxmemory/(1024.0*1024.0))); + printf("******************************************************************************\n"); + +} diff --git a/src/metis/programs/graphchk.c b/src/metis/programs/graphchk.c new file mode 100644 index 0000000..b4b3a89 --- /dev/null +++ b/src/metis/programs/graphchk.c @@ -0,0 +1,74 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * graphchk.c + * + * This file checks the validity of a graph + * + * Started 8/28/94 + * George + * + * $Id: graphchk.c 9982 2011-05-25 17:18:00Z karypis $ + * + */ + +#include "metisbin.h" + + + +/*************************************************************************/ +/*! Let entry point of the checker */ +/*************************************************************************/ +int main(int argc, char *argv[]) +{ + graph_t *graph, *fgraph; + char filename[256]; + idx_t wgtflag; + params_t params; + + if (argc != 2 && argc != 3) { + printf("Usage: %s [FixedGraphFile (for storing the fixed graph)]\n", argv[0]); + exit(0); + } + + memset((void *)¶ms, 0, sizeof(params_t)); + params.filename = gk_strdup(argv[1]); + + graph = ReadGraph(¶ms); + if (graph->nvtxs == 0) { + printf("Empty graph!\n"); + exit(0); + } + + printf("**********************************************************************\n"); + printf("%s", METISTITLE); + printf(" (HEAD: %s, Built on: %s, %s)\n", SVNINFO, __DATE__, __TIME__); + printf(" size of idx_t: %zubits, real_t: %zubits, idx_t *: %zubits\n", + 8*sizeof(idx_t), 8*sizeof(real_t), 8*sizeof(idx_t *)); + printf("\n"); + printf("Graph Information ---------------------------------------------------\n"); + printf(" Name: %s, #Vertices: %"PRIDX", #Edges: %"PRIDX"\n\n", + params.filename, graph->nvtxs, graph->nedges/2); + printf("Checking Graph... ---------------------------------------------------\n"); + + if (CheckGraph(graph, 1, 1)) { + printf(" The format of the graph is correct!\n"); + } + else { + printf(" The format of the graph is incorrect!\n"); + if (argc == 3) { + fgraph = FixGraph(graph); + WriteGraph(fgraph, argv[2]); + FreeGraph(&fgraph); + printf(" A corrected version was stored at %s\n", argv[2]); + } + } + + printf("\n**********************************************************************\n"); + + + FreeGraph(&graph); + gk_free((void **)¶ms.filename, ¶ms.tpwgtsfile, ¶ms.tpwgts, LTERM); +} + + diff --git a/src/metis/programs/io.c b/src/metis/programs/io.c new file mode 100644 index 0000000..4696819 --- /dev/null +++ b/src/metis/programs/io.c @@ -0,0 +1,568 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * io.c + * + * This file contains routines related to I/O + * + * Started 8/28/94 + * George + * + * $Id: io.c 11932 2012-05-10 18:18:23Z dominique $ + * + */ + +#include "metisbin.h" + + + +/*************************************************************************/ +/*! This function reads in a sparse graph */ +/*************************************************************************/ +graph_t *ReadGraph(params_t *params) +{ + idx_t i, j, k, l, fmt, ncon, nfields, readew, readvw, readvs, edge, ewgt; + idx_t *xadj, *adjncy, *vwgt, *adjwgt, *vsize; + char *line=NULL, fmtstr[256], *curstr, *newstr; + size_t lnlen=0; + FILE *fpin; + graph_t *graph; + + if (!gk_fexists(params->filename)) + errexit("File %s does not exist!\n", params->filename); + + graph = CreateGraph(); + + fpin = gk_fopen(params->filename, "r", "ReadGRaph: Graph"); + + /* Skip comment lines until you get to the first valid line */ + do { + if (gk_getline(&line, &lnlen, fpin) == -1) + errexit("Premature end of input file: file: %s\n", params->filename); + } while (line[0] == '%'); + + + fmt = ncon = 0; + nfields = sscanf(line, "%"SCIDX" %"SCIDX" %"SCIDX" %"SCIDX, + &(graph->nvtxs), &(graph->nedges), &fmt, &ncon); + + if (nfields < 2) + errexit("The input file does not specify the number of vertices and edges.\n"); + + if (graph->nvtxs <= 0 || graph->nedges <= 0) + errexit("The supplied nvtxs:%"PRIDX" and nedges:%"PRIDX" must be positive.\n", + graph->nvtxs, graph->nedges); + + if (fmt > 111) + errexit("Cannot read this type of file format [fmt=%"PRIDX"]!\n", fmt); + + sprintf(fmtstr, "%03"PRIDX, fmt%1000); + readvs = (fmtstr[0] == '1'); + readvw = (fmtstr[1] == '1'); + readew = (fmtstr[2] == '1'); + + /*printf("%s %"PRIDX" %"PRIDX" %"PRIDX"\n", fmtstr, readvs, readvw, readew); */ + + + if (ncon > 0 && !readvw) + errexit( + "------------------------------------------------------------------------------\n" + "*** I detected an error in your input file ***\n\n" + "You specified ncon=%"PRIDX", but the fmt parameter does not specify vertex weights\n" + "Make sure that the fmt parameter is set to either 10 or 11.\n" + "------------------------------------------------------------------------------\n", ncon); + + graph->nedges *=2; + ncon = graph->ncon = (ncon == 0 ? 1 : ncon); + + xadj = graph->xadj = ismalloc(graph->nvtxs+1, 0, "ReadGraph: xadj"); + adjncy = graph->adjncy = imalloc(graph->nedges, "ReadGraph: adjncy"); + vwgt = graph->vwgt = ismalloc(ncon*graph->nvtxs, 1, "ReadGraph: vwgt"); + adjwgt = graph->adjwgt = ismalloc(graph->nedges, 1, "ReadGraph: adjwgt"); + vsize = graph->vsize = ismalloc(graph->nvtxs, 1, "ReadGraph: vsize"); + + + /*---------------------------------------------------------------------- + * Read the sparse graph file + *---------------------------------------------------------------------*/ + for (xadj[0]=0, k=0, i=0; invtxs; i++) { + do { + if (gk_getline(&line, &lnlen, fpin) == -1) + errexit("Premature end of input file while reading vertex %"PRIDX".\n", i+1); + } while (line[0] == '%'); + + curstr = line; + newstr = NULL; + + /* Read vertex sizes */ + if (readvs) { + vsize[i] = strtoidx(curstr, &newstr, 10); + if (newstr == curstr) + errexit("The line for vertex %"PRIDX" does not have vsize information\n", i+1); + if (vsize[i] < 0) + errexit("The size for vertex %"PRIDX" must be >= 0\n", i+1); + curstr = newstr; + } + + + /* Read vertex weights */ + if (readvw) { + for (l=0; l= 0\n", i+1, l); + curstr = newstr; + } + } + + while (1) { + edge = strtoidx(curstr, &newstr, 10); + if (newstr == curstr) + break; /* End of line */ + curstr = newstr; + + if (edge < 1 || edge > graph->nvtxs) + errexit("Edge %"PRIDX" for vertex %"PRIDX" is out of bounds\n", edge, i+1); + + ewgt = 1; + if (readew) { + ewgt = strtoidx(curstr, &newstr, 10); + if (newstr == curstr) + errexit("Premature end of line for vertex %"PRIDX"\n", i+1); + if (ewgt <= 0) + errexit("The weight (%"PRIDX") for edge (%"PRIDX", %"PRIDX") must be positive.\n", + ewgt, i+1, edge); + curstr = newstr; + } + + if (k == graph->nedges) + errexit("There are more edges in the file than the %"PRIDX" specified.\n", + graph->nedges/2); + + adjncy[k] = edge-1; + adjwgt[k] = ewgt; + k++; + } + xadj[i+1] = k; + } + gk_fclose(fpin); + + if (k != graph->nedges) { + printf("------------------------------------------------------------------------------\n"); + printf("*** I detected an error in your input file ***\n\n"); + printf("In the first line of the file, you specified that the graph contained\n" + "%"PRIDX" edges. However, I only found %"PRIDX" edges in the file.\n", + graph->nedges/2, k/2); + if (2*k == graph->nedges) { + printf("\n *> I detected that you specified twice the number of edges that you have in\n"); + printf(" the file. Remember that the number of edges specified in the first line\n"); + printf(" counts each edge between vertices v and u only once.\n\n"); + } + printf("Please specify the correct number of edges in the first line of the file.\n"); + printf("------------------------------------------------------------------------------\n"); + exit(0); + } + + gk_free((void *)&line, LTERM); + + return graph; +} + + +/*************************************************************************/ +/*! This function reads in a mesh */ +/*************************************************************************/ +mesh_t *ReadMesh(params_t *params) +{ + idx_t i, j, k, l, nfields, ncon, node; + idx_t *eptr, *eind, *ewgt; + size_t nlines, ntokens; + char *line=NULL, *curstr, *newstr; + size_t lnlen=0; + FILE *fpin; + mesh_t *mesh; + + if (!gk_fexists(params->filename)) + errexit("File %s does not exist!\n", params->filename); + + mesh = CreateMesh(); + + /* get some file stats */ + gk_getfilestats(params->filename, &nlines, &ntokens, NULL, NULL); + + fpin = gk_fopen(params->filename, "r", __func__); + + /* Skip comment lines until you get to the first valid line */ + do { + if (gk_getline(&line, &lnlen, fpin) == -1) + errexit("Premature end of input file: file: %s\n", params->filename); + } while (line[0] == '%'); + + + mesh->ncon = 0; + nfields = sscanf(line, "%"SCIDX" %"SCIDX, &(mesh->ne), &(mesh->ncon)); + + if (nfields < 1) + errexit("The input file does not specify the number of elements.\n"); + + if (mesh->ne <= 0) + errexit("The supplied number of elements:%"PRIDX" must be positive.\n", mesh->ne); + + if (mesh->ne > nlines) + errexit("The file has %zu lines which smaller than the number of " + "elements of %"PRIDX" specified in the header line.\n", nlines, mesh->ne); + + ncon = mesh->ncon; + eptr = mesh->eptr = ismalloc(mesh->ne+1, 0, "ReadMesh: eptr"); + eind = mesh->eind = imalloc(ntokens, "ReadMesh: eind"); + ewgt = mesh->ewgt = ismalloc((ncon == 0 ? 1 : ncon)*mesh->ne, 1, "ReadMesh: ewgt"); + + + /*---------------------------------------------------------------------- + * Read the mesh file + *---------------------------------------------------------------------*/ + for (eptr[0]=0, k=0, i=0; ine; i++) { + do { + if (gk_getline(&line, &lnlen, fpin) == -1) + errexit("Premature end of input file while reading element %"PRIDX".\n", i+1); + } while (line[0] == '%'); + + curstr = line; + newstr = NULL; + + /* Read element weights */ + for (l=0; l= 0\n", i+1, l); + curstr = newstr; + } + + while (1) { + node = strtoidx(curstr, &newstr, 10); + if (newstr == curstr) + break; /* End of line */ + curstr = newstr; + + if (node < 1) + errexit("Node %"PRIDX" for element %"PRIDX" is out of bounds\n", node, i+1); + + eind[k++] = node-1; + } + eptr[i+1] = k; + } + gk_fclose(fpin); + + mesh->ncon = (ncon == 0 ? 1 : ncon); + mesh->nn = imax(eptr[mesh->ne], eind)+1; + + gk_free((void *)&line, LTERM); + + return mesh; +} + + +/*************************************************************************/ +/*! This function reads in the target partition weights. If no file is + specified the weights are set to 1/nparts */ +/*************************************************************************/ +void ReadTPwgts(params_t *params, idx_t ncon) +{ + idx_t i, j, from, to, fromcnum, tocnum, nleft; + real_t awgt=0.0, twgt; + char *line=NULL, *curstr, *newstr; + size_t lnlen=0; + FILE *fpin; + + params->tpwgts = rsmalloc(params->nparts*ncon, -1.0, "ReadTPwgts: tpwgts"); + + if (params->tpwgtsfile == NULL) { + for (i=0; inparts; i++) { + for (j=0; jtpwgts[i*ncon+j] = 1.0/params->nparts; + } + return; + } + + if (!gk_fexists(params->tpwgtsfile)) + errexit("Graph file %s does not exist!\n", params->tpwgtsfile); + + fpin = gk_fopen(params->tpwgtsfile, "r", "ReadTPwgts: tpwgtsfile"); + + while (gk_getline(&line, &lnlen, fpin) != -1) { + gk_strchr_replace(line, " ", ""); + /* start extracting the fields */ + + curstr = line; + newstr = NULL; + + from = strtoidx(curstr, &newstr, 10); + if (newstr == curstr) + errexit("The 'from' component of line <%s> in the tpwgts file is incorrect.\n", line); + curstr = newstr; + + if (curstr[0] == '-') { + to = strtoidx(curstr+1, &newstr, 10); + if (newstr == curstr) + errexit("The 'to' component of line <%s> in the tpwgts file is incorrect.\n", line); + curstr = newstr; + } + else { + to = from; + } + + if (curstr[0] == ':') { + fromcnum = strtoidx(curstr+1, &newstr, 10); + if (newstr == curstr) + errexit("The 'fromcnum' component of line <%s> in the tpwgts file is incorrect.\n", line); + curstr = newstr; + + if (curstr[0] == '-') { + tocnum = strtoidx(curstr+1, &newstr, 10); + if (newstr == curstr) + errexit("The 'tocnum' component of line <%s> in the tpwgts file is incorrect.\n", line); + curstr = newstr; + } + else { + tocnum = fromcnum; + } + } + else { + fromcnum = 0; + tocnum = ncon-1; + } + + if (curstr[0] == '=') { + awgt = strtoreal(curstr+1, &newstr); + if (newstr == curstr) + errexit("The 'wgt' component of line <%s> in the tpwgts file is incorrect.\n", line); + curstr = newstr; + } + else { + errexit("The 'wgt' component of line <%s> in the tpwgts file is missing.\n", line); + } + + /*printf("Read: %"PRIDX"-%"PRIDX":%"PRIDX"-%"PRIDX"=%"PRREAL"\n", + from, to, fromcnum, tocnum, awgt);*/ + + if (from < 0 || to < 0 || from >= params->nparts || to >= params->nparts) + errexit("Invalid partition range for %"PRIDX":%"PRIDX"\n", from, to); + if (fromcnum < 0 || tocnum < 0 || fromcnum >= ncon || tocnum >= ncon) + errexit("Invalid constraint number range for %"PRIDX":%"PRIDX"\n", + fromcnum, tocnum); + if (awgt <= 0.0 || awgt >= 1.0) + errexit("Invalid partition weight of %"PRREAL"\n", awgt); + for (i=from; i<=to; i++) { + for (j=fromcnum; j<=tocnum; j++) + params->tpwgts[i*ncon+j] = awgt; + } + } + + gk_fclose(fpin); + + /* Assign weight to the unspecified constraints x partitions */ + for (j=0; jnparts, i=0; inparts; i++) { + if (params->tpwgts[i*ncon+j] > 0) { + twgt += params->tpwgts[i*ncon+j]; + nleft--; + } + } + + /* Rescale the weights to be on the safe side */ + if (nleft == 0) + rscale(params->nparts, 1.0/twgt, params->tpwgts+j, ncon); + + /* Assign the left-over weight to the remaining partitions */ + if (nleft > 0) { + if (twgt > 1) + errexit("The total specified target partition weights for constraint #%"PRIDX + " of %"PRREAL" exceeds 1.0.\n", j, twgt); + + awgt = (1.0 - twgt)/nleft; + for (i=0; inparts; i++) + params->tpwgts[i*ncon+j] = + (params->tpwgts[i*ncon+j] < 0 ? awgt : params->tpwgts[i*ncon+j]); + } + } + #ifdef HAVE_GETLINE + free(line); + line = NULL; /* set to null to match behavior of gk_free() */ + #else + gk_free((void *)&line, LTERM); + #endif +} + + +/*************************************************************************/ +/*! This function reads in a partition/ordering vector */ +/**************************************************************************/ +void ReadPOVector(graph_t *graph, char *filename, idx_t *vector) +{ + idx_t i; + FILE *fpin; + + fpin = gk_fopen(filename, "r", __func__); + for (i=0; invtxs; i++) { + if (fscanf(fpin, "%"SCIDX"\n", vector+i) != 1) + errexit("[%s] Premature end of file %s at line %d [nvtxs: %d]\n", + __func__, filename, i, graph->nvtxs); + } + gk_fclose(fpin); +} + + +/*************************************************************************/ +/*! This function writes out the partition vector */ +/*************************************************************************/ +void WritePartition(char *fname, idx_t *part, idx_t n, idx_t nparts) +{ + FILE *fpout; + idx_t i; + char filename[MAXLINE]; + + sprintf(filename, "%s.part.%"PRIDX, fname, nparts); + + fpout = gk_fopen(filename, "w", __func__); + + for (i=0; invtxs; + ncon = graph->ncon; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + vsize = graph->vsize; + adjwgt = graph->adjwgt; + + /* determine if the graph has non-unity vwgt, vsize, or adjwgt */ + if (vwgt) { + for (i=0; incon); + } + + + /* write the rest of the graph */ + for (i=0; iiotimer); + mesh = ReadMesh(params); + + gk_stopcputimer(params->iotimer); + + if (mesh->ncon > 1) { + printf("*** Meshes with more than one balancing constraint are not supported yet.\n"); + exit(0); + } + + M2GPrintInfo(params, mesh); + + graph = CreateGraph(); + + gk_malloc_init(); + gk_startcputimer(params->parttimer); + + switch (params->gtype) { + case METIS_GTYPE_DUAL: + status = METIS_MeshToDual(&mesh->ne, &mesh->nn, mesh->eptr, mesh->eind, + ¶ms->ncommon, ¶ms->numflag, &graph->xadj, &graph->adjncy); + + if (status == METIS_OK) { + graph->nvtxs = mesh->ne; + graph->nedges = graph->xadj[graph->nvtxs]; + graph->ncon = 1; + } + break; + + case METIS_GTYPE_NODAL: + status = METIS_MeshToNodal(&mesh->ne, &mesh->nn, mesh->eptr, mesh->eind, + ¶ms->numflag, &graph->xadj, &graph->adjncy); + + if (status == METIS_OK) { + graph->nvtxs = mesh->nn; + graph->nedges = graph->xadj[graph->nvtxs]; + graph->ncon = 1; + } + break; + } + + gk_stopcputimer(params->parttimer); + if (gk_GetCurMemoryUsed() != 0) + printf("***It seems that Metis did not free all of its memory! Report this.\n"); + params->maxmemory = gk_GetMaxMemoryUsed(); + gk_malloc_cleanup(0); + + if (status != METIS_OK) { + printf("\n***Metis returned with an error.\n"); + } + else { + /* Write the graph */ + gk_startcputimer(params->iotimer); + WriteGraph(graph, params->outfile); + gk_stopcputimer(params->iotimer); + + M2GReportResults(params, mesh, graph); + } + + FreeGraph(&graph); + FreeMesh(&mesh); + gk_free((void **)¶ms->filename, ¶ms->outfile, ¶ms, LTERM); +} + + +/*************************************************************************/ +/*! This function prints run parameters */ +/*************************************************************************/ +void M2GPrintInfo(params_t *params, mesh_t *mesh) +{ + printf("******************************************************************************\n"); + printf("%s", METISTITLE); + printf(" (HEAD: %s, Built on: %s, %s)\n", SVNINFO, __DATE__, __TIME__); + printf(" size of idx_t: %zubits, real_t: %zubits, idx_t *: %zubits\n", + 8*sizeof(idx_t), 8*sizeof(real_t), 8*sizeof(idx_t *)); + printf("\n"); + printf("Mesh Information ------------------------------------------------------------\n"); + printf(" Name: %s, #Elements: %"PRIDX", #Nodes: %"PRIDX"\n", + params->filename, mesh->ne, mesh->nn); + + printf("Options ---------------------------------------------------------------------\n"); + printf(" gtype=%s, ncommon=%"PRIDX", outfile=%s\n", + gtypenames[params->gtype], params->ncommon, params->outfile); + + printf("\n"); +} + + +/*************************************************************************/ +/*! This function does any post-metis reporting */ +/*************************************************************************/ +void M2GReportResults(params_t *params, mesh_t *mesh, graph_t *graph) +{ + + gk_startcputimer(params->reporttimer); + + printf(" - #nvtxs: %"PRIDX", #edges: %"PRIDX"\n", graph->nvtxs, graph->nedges); + + gk_stopcputimer(params->reporttimer); + + + printf("\nTiming Information ----------------------------------------------------------\n"); + printf(" I/O: \t\t %7.3"PRREAL" sec\n", gk_getcputimer(params->iotimer)); + printf(" Partitioning: \t\t %7.3"PRREAL" sec (METIS time)\n", gk_getcputimer(params->parttimer)); + printf(" Reporting: \t\t %7.3"PRREAL" sec\n", gk_getcputimer(params->reporttimer)); + printf("\nMemory Information ----------------------------------------------------------\n"); + printf(" Max memory used:\t\t %7.3"PRREAL" MB\n", (real_t)(params->maxmemory/(1024.0*1024.0))); + printf("******************************************************************************\n"); + +} diff --git a/src/metis/programs/metisbin.h b/src/metis/programs/metisbin.h new file mode 100644 index 0000000..330b0f4 --- /dev/null +++ b/src/metis/programs/metisbin.h @@ -0,0 +1,52 @@ +/* + * metisbin.h + * + * This file contains the various header inclusions + * + * Started 8/9/02 + * George + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(ENABLE_OPENMP) + #include +#endif + + +#include +#include "../libmetis/rename.h" +#include "../libmetis/gklib_defs.h" +#include "../libmetis/defs.h" +#include "../libmetis/struct.h" +#include "../libmetis/macros.h" +#include "../libmetis/proto.h" +#include "defs.h" +#include "struct.h" +#include "proto.h" + + +#if defined(COMPILER_GCC) +extern char* strdup (const char *); +#endif + +#if defined(COMPILER_MSC) +#if defined(rint) + #undef rint +#endif +#define rint(x) ((idx_t)((x)+0.5)) /* MSC does not have rint() function */ +#define __func__ "dummy-function" +#endif diff --git a/src/metis/programs/mpmetis.c b/src/metis/programs/mpmetis.c new file mode 100644 index 0000000..00e5613 --- /dev/null +++ b/src/metis/programs/mpmetis.c @@ -0,0 +1,190 @@ +/* + * Copyright 1994-2011, Regents of the University of Minnesota + * + * mpmetis.c + * + * Drivers for the mesh partitioning routines + * + * Started 8/28/94 + * George + * + * $Id: mpmetis.c 10567 2011-07-13 16:17:07Z karypis $ + * + */ + +#include "metisbin.h" + + + +/*************************************************************************/ +/*! Let the game begin! */ +/*************************************************************************/ +int main(int argc, char *argv[]) +{ + idx_t options[METIS_NOPTIONS]; + mesh_t *mesh; + idx_t *epart, *npart; + idx_t objval; + params_t *params; + int status=0; + + params = parse_cmdline(argc, argv); + + gk_startcputimer(params->iotimer); + mesh = ReadMesh(params); + + if (mesh->ncon > 1) { + printf("*** Meshes with more than one balancing constraint are not supported yet.\n"); + exit(0); + } + + ReadTPwgts(params, mesh->ncon); + gk_stopcputimer(params->iotimer); + + MPPrintInfo(params, mesh); + + epart = imalloc(mesh->ne, "main: epart"); + npart = imalloc(mesh->nn, "main: npart"); + + METIS_SetDefaultOptions(options); + options[METIS_OPTION_PTYPE] = params->ptype; + options[METIS_OPTION_OBJTYPE] = params->objtype; + options[METIS_OPTION_CTYPE] = params->ctype; + options[METIS_OPTION_IPTYPE] = params->iptype; + options[METIS_OPTION_RTYPE] = params->rtype; + options[METIS_OPTION_DBGLVL] = params->dbglvl; + options[METIS_OPTION_UFACTOR] = params->ufactor; + options[METIS_OPTION_MINCONN] = params->minconn; + options[METIS_OPTION_CONTIG] = params->contig; + options[METIS_OPTION_SEED] = params->seed; + options[METIS_OPTION_NITER] = params->niter; + options[METIS_OPTION_NCUTS] = params->ncuts; + + + gk_malloc_init(); + gk_startcputimer(params->parttimer); + + switch (params->gtype) { + case METIS_GTYPE_DUAL: + status = METIS_PartMeshDual(&mesh->ne, &mesh->nn, mesh->eptr, mesh->eind, + mesh->ewgt, NULL, ¶ms->ncommon, ¶ms->nparts, + params->tpwgts, options, &objval, epart, npart); + break; + + case METIS_GTYPE_NODAL: + status = METIS_PartMeshNodal(&mesh->ne, &mesh->nn, mesh->eptr, mesh->eind, + NULL, NULL, ¶ms->nparts, params->tpwgts, options, &objval, + epart, npart); + break; + } + + gk_stopcputimer(params->parttimer); + if (gk_GetCurMemoryUsed() != 0) + printf("***It seems that Metis did not free all of its memory! Report this.\n"); + params->maxmemory = gk_GetMaxMemoryUsed(); + gk_malloc_cleanup(0); + + if (status != METIS_OK) { + printf("\n***Metis returned with an error.\n"); + } + else { + if (!params->nooutput) { + /* Write the solution */ + gk_startcputimer(params->iotimer); + WriteMeshPartition(params->filename, params->nparts, mesh->ne, epart, mesh->nn, npart); + gk_stopcputimer(params->iotimer); + } + + MPReportResults(params, mesh, epart, npart, objval); + } + + FreeMesh(&mesh); + gk_free((void **)&epart, &npart, LTERM); + gk_free((void **)¶ms->filename, ¶ms->tpwgtsfile, ¶ms->tpwgts, + ¶ms->ubvecstr, ¶ms->ubvec, ¶ms, LTERM); + +} + + +/*************************************************************************/ +/*! This function prints run parameters */ +/*************************************************************************/ +void MPPrintInfo(params_t *params, mesh_t *mesh) +{ + if (params->ufactor == -1) { + if (params->ptype == METIS_PTYPE_KWAY) + params->ufactor = KMETIS_DEFAULT_UFACTOR; + else + params->ufactor = PMETIS_DEFAULT_UFACTOR; + } + + printf("******************************************************************************\n"); + printf("%s", METISTITLE); + printf(" (HEAD: %s, Built on: %s, %s)\n", SVNINFO, __DATE__, __TIME__); + printf(" size of idx_t: %zubits, real_t: %zubits, idx_t *: %zubits\n", + 8*sizeof(idx_t), 8*sizeof(real_t), 8*sizeof(idx_t *)); + printf("\n"); + printf("Mesh Information ------------------------------------------------------------\n"); + printf(" Name: %s, #Elements: %"PRIDX", #Nodes: %"PRIDX", #Parts: %"PRIDX"\n", + params->filename, mesh->ne, mesh->nn, params->nparts); + if (mesh->ncon > 1) + printf(" Balancing Constraints: %"PRIDX"\n", mesh->ncon); + + printf("\n"); + printf("Options ---------------------------------------------------------------------\n"); + printf(" ptype=%s, objtype=%s, ctype=%s, rtype=%s, iptype=%s\n", + ptypenames[params->ptype], objtypenames[params->objtype], ctypenames[params->ctype], + rtypenames[params->rtype], iptypenames[params->iptype]); + + printf(" dbglvl=%"PRIDX", ufactor=%.3f, minconn=%s, contig=%s, nooutput=%s\n", + params->dbglvl, + I2RUBFACTOR(params->ufactor), + (params->minconn ? "YES" : "NO"), + (params->contig ? "YES" : "NO"), + (params->nooutput ? "YES" : "NO") + ); + + printf(" seed=%"PRIDX", niter=%"PRIDX", ncuts=%"PRIDX"\n", + params->seed, params->niter, params->ncuts); + + printf(" gtype=%s, ncommon=%"PRIDX", niter=%"PRIDX", ncuts=%"PRIDX"\n", + gtypenames[params->gtype], params->ncommon, params->niter, params->ncuts); + + printf("\n"); + switch (params->ptype) { + case METIS_PTYPE_RB: + printf("Recursive Partitioning ------------------------------------------------------\n"); + break; + case METIS_PTYPE_KWAY: + printf("Direct k-way Partitioning ---------------------------------------------------\n"); + break; + } +} + + +/*************************************************************************/ +/*! This function does any post-partitioning reporting */ +/*************************************************************************/ +void MPReportResults(params_t *params, mesh_t *mesh, idx_t *epart, idx_t *npart, + idx_t objval) +{ + + gk_startcputimer(params->reporttimer); + + /* ComputePartitionInfo(params, graph, part); */ + + printf(" - %s: %"PRIDX".\n\n", + (params->objtype == METIS_OBJTYPE_CUT ? "Edgecut" : "Volume"), objval); + + gk_stopcputimer(params->reporttimer); + + + printf("\nTiming Information ----------------------------------------------------------\n"); + printf(" I/O: \t\t %7.3"PRREAL" sec\n", gk_getcputimer(params->iotimer)); + printf(" Partitioning: \t\t %7.3"PRREAL" sec (METIS time)\n", gk_getcputimer(params->parttimer)); + printf(" Reporting: \t\t %7.3"PRREAL" sec\n", gk_getcputimer(params->reporttimer)); + printf("\nMemory Information ----------------------------------------------------------\n"); + printf(" Max memory used:\t\t %7.3"PRREAL" MB\n", (real_t)(params->maxmemory/(1024.0*1024.0))); + printf("******************************************************************************\n"); + +} diff --git a/src/metis/programs/ndmetis.c b/src/metis/programs/ndmetis.c new file mode 100644 index 0000000..941c777 --- /dev/null +++ b/src/metis/programs/ndmetis.c @@ -0,0 +1,169 @@ +/* + * Copyright 1994-2011, Regents of the University of Minnesota + * + * ndmetis.c + * + * Driver programs for nested disection ordering + * + * Started 8/28/94 + * George + * + * $Id: ndmetis.c 13900 2013-03-24 15:27:07Z karypis $ + * + */ + +#include "metisbin.h" + + + +/*************************************************************************/ +/*! Let the game begin! */ +/*************************************************************************/ +int main(int argc, char *argv[]) +{ + idx_t options[METIS_NOPTIONS]; + graph_t *graph; + idx_t *perm, *iperm; + params_t *params; + int status=0; + + params = parse_cmdline(argc, argv); + + gk_startcputimer(params->iotimer); + graph = ReadGraph(params); + gk_stopcputimer(params->iotimer); + + /* This is just for internal use to clean up some files + { + char fileout[8192]; + + gk_free((void **)&graph->vwgt, &graph->adjwgt, &graph->vsize, LTERM); + sprintf(fileout, "ND/%s", params->filename); + if (graph->nvtxs > 25000) + WriteGraph(graph, fileout); + exit(0); + } + */ + + /* Check if the graph is contiguous */ + if (graph->ncon != 1) { + printf("***The input graph contains %"PRIDX" constraints..\n" + "***Ordering requires a graph with one constraint.\n", graph->ncon); + exit(0); + } + + NDPrintInfo(params, graph); + + perm = imalloc(graph->nvtxs, "main: perm"); + iperm = imalloc(graph->nvtxs, "main: iperm"); + + METIS_SetDefaultOptions(options); + options[METIS_OPTION_CTYPE] = params->ctype; + options[METIS_OPTION_IPTYPE] = params->iptype; + options[METIS_OPTION_RTYPE] = params->rtype; + options[METIS_OPTION_DBGLVL] = params->dbglvl; + options[METIS_OPTION_UFACTOR] = params->ufactor; + options[METIS_OPTION_NO2HOP] = params->no2hop; + options[METIS_OPTION_COMPRESS] = params->compress; + options[METIS_OPTION_CCORDER] = params->ccorder; + options[METIS_OPTION_SEED] = params->seed; + options[METIS_OPTION_NITER] = params->niter; + options[METIS_OPTION_NSEPS] = params->nseps; + options[METIS_OPTION_PFACTOR] = params->pfactor; + + gk_malloc_init(); + gk_startcputimer(params->parttimer); + + status = METIS_NodeND(&graph->nvtxs, graph->xadj, graph->adjncy, graph->vwgt, + options, perm, iperm); + + gk_stopcputimer(params->parttimer); + if (gk_GetCurMemoryUsed() != 0) + printf("***It seems that Metis did not free all of its memory! Report this.\n"); + params->maxmemory = gk_GetMaxMemoryUsed(); + gk_malloc_cleanup(0); + + + if (status != METIS_OK) { + printf("\n***Metis returned with an error.\n"); + } + else { + if (!params->nooutput) { + /* Write the solution */ + gk_startcputimer(params->iotimer); + WritePermutation(params->filename, iperm, graph->nvtxs); + gk_stopcputimer(params->iotimer); + } + + NDReportResults(params, graph, perm, iperm); + } + + FreeGraph(&graph); + gk_free((void **)&perm, &iperm, LTERM); + gk_free((void **)¶ms->filename, ¶ms->tpwgtsfile, ¶ms->tpwgts, + ¶ms->ubvec, ¶ms, LTERM); + +} + + +/*************************************************************************/ +/*! This function prints run parameters */ +/*************************************************************************/ +void NDPrintInfo(params_t *params, graph_t *graph) +{ + printf("******************************************************************************\n"); + printf("%s", METISTITLE); + printf(" (HEAD: %s, Built on: %s, %s)\n", SVNINFO, __DATE__, __TIME__); + printf(" size of idx_t: %zubits, real_t: %zubits, idx_t *: %zubits\n", + 8*sizeof(idx_t), 8*sizeof(real_t), 8*sizeof(idx_t *)); + printf("\n"); + printf("Graph Information -----------------------------------------------------------\n"); + printf(" Name: %s, #Vertices: %"PRIDX", #Edges: %"PRIDX"\n", + params->filename, graph->nvtxs, graph->nedges/2); + + printf("\n"); + printf("Options ---------------------------------------------------------------------\n"); + printf(" ctype=%s, rtype=%s, iptype=%s, seed=%"PRIDX", dbglvl=%"PRIDX"\n", + ctypenames[params->ctype], rtypenames[params->rtype], + iptypenames[params->iptype], params->seed, params->dbglvl); + + printf(" ufactor=%.3f, pfactor=%.2f, no2hop=%s, ccorder=%s, compress=%s, , nooutput=%s\n", + I2RUBFACTOR(params->ufactor), + 0.1*params->pfactor, + (params->no2hop ? "YES" : "NO"), + (params->ccorder ? "YES" : "NO"), + (params->compress ? "YES" : "NO"), + (params->nooutput ? "YES" : "NO") + ); + + printf(" niter=%"PRIDX", nseps=%"PRIDX"\n", params->niter, params->nseps); + + printf("\n"); + printf("Node-based Nested Dissection ------------------------------------------------\n"); +} + + +/*************************************************************************/ +/*! This function does any post-ordering reporting */ +/*************************************************************************/ +void NDReportResults(params_t *params, graph_t *graph, idx_t *perm, + idx_t *iperm) +{ + size_t maxlnz, opc; + + gk_startcputimer(params->reporttimer); + ComputeFillIn(graph, perm, iperm, &maxlnz, &opc); + printf(" Nonzeros: %6.3le \tOperation Count: %6.3le\n", (double)maxlnz, (double)opc); + + gk_stopcputimer(params->reporttimer); + + + printf("\nTiming Information ----------------------------------------------------------\n"); + printf(" I/O: \t\t %7.3"PRREAL" sec\n", gk_getcputimer(params->iotimer)); + printf(" Ordering: \t\t %7.3"PRREAL" sec (METIS time)\n", gk_getcputimer(params->parttimer)); + printf(" Reporting: \t\t %7.3"PRREAL" sec\n", gk_getcputimer(params->reporttimer)); + printf("\nMemory Information ----------------------------------------------------------\n"); + printf(" Max memory used:\t\t %7.3"PRREAL" MB\n", (real_t)(params->maxmemory/(1024.0*1024.0))); + printf("******************************************************************************\n"); + +} diff --git a/src/metis/programs/proto.h b/src/metis/programs/proto.h new file mode 100644 index 0000000..1e9e6d5 --- /dev/null +++ b/src/metis/programs/proto.h @@ -0,0 +1,60 @@ +/* + * proto.h + * + * This file contains function prototypes + * + * Started 11/1/99 + * George + * + * $Id: proto.h 10513 2011-07-07 22:06:03Z karypis $ + * + */ + +#ifndef _PROTOBIN_H_ +#define _PROTOBIN_H_ + + +/* io.c */ +graph_t *ReadGraph(params_t *); +mesh_t *ReadMesh(params_t *); +void ReadTPwgts(params_t *params, idx_t ncon); +void ReadPOVector(graph_t *graph, char *filename, idx_t *vector); +void WritePartition(char *, idx_t *, idx_t, idx_t); +void WriteMeshPartition(char *, idx_t, idx_t, idx_t *, idx_t, idx_t *); +void WritePermutation(char *, idx_t *, idx_t); +void WriteGraph(graph_t *graph, char *filename); + + +/* smbfactor.c */ +void ComputeFillIn(graph_t *graph, idx_t *perm, idx_t *iperm, + size_t *r_maxlnz, size_t *r_opc); +idx_t smbfct(idx_t neqns, idx_t *xadj, idx_t *adjncy, idx_t *perm, + idx_t *invp, idx_t *xlnz, idx_t *maxlnz, idx_t *xnzsub, + idx_t *nzsub, idx_t *maxsub); + + +/* cmdline.c */ +params_t *parse_cmdline(int argc, char *argv[]); + +/* gpmetis.c */ +void GPPrintInfo(params_t *params, graph_t *graph); +void GPReportResults(params_t *params, graph_t *graph, idx_t *part, idx_t edgecut); + +/* ndmetis.c */ +void NDPrintInfo(params_t *params, graph_t *graph); +void NDReportResults(params_t *params, graph_t *graph, idx_t *perm, idx_t *iperm); + +/* mpmetis.c */ +void MPPrintInfo(params_t *params, mesh_t *mesh); +void MPReportResults(params_t *params, mesh_t *mesh, idx_t *epart, idx_t *npart, + idx_t edgecut); + +/* m2gmetis.c */ +void M2GPrintInfo(params_t *params, mesh_t *mesh); +void M2GReportResults(params_t *params, mesh_t *mesh, graph_t *graph); + +/* stat.c */ +void ComputePartitionInfo(params_t *params, graph_t *graph, idx_t *where); + + +#endif diff --git a/src/metis/programs/smbfactor.c b/src/metis/programs/smbfactor.c new file mode 100644 index 0000000..597bb5d --- /dev/null +++ b/src/metis/programs/smbfactor.c @@ -0,0 +1,332 @@ +/* + * Copyright 1997, Regents of the University of Minnesota + * + * smbfactor.c + * + * This file performs the symbolic factorization of a matrix + * + * Started 8/1/97 + * George + * + * $Id: smbfactor.c 10154 2011-06-09 21:27:35Z karypis $ + * + */ + +#include "metisbin.h" + + +/*************************************************************************/ +/*! This function sets up data structures for fill-in computations */ +/*************************************************************************/ +void ComputeFillIn(graph_t *graph, idx_t *perm, idx_t *iperm, + size_t *r_maxlnz, size_t *r_opc) +{ + idx_t i, j, k, nvtxs, maxlnz, maxsub; + idx_t *xadj, *adjncy; + idx_t *xlnz, *xnzsub, *nzsub; + size_t opc; + +/* + printf("\nSymbolic factorization... --------------------------------------------\n"); +*/ + + nvtxs = graph->nvtxs; + xadj = graph->xadj; + adjncy = graph->adjncy; + + maxsub = 8*(nvtxs+xadj[nvtxs]); + + /* Relabel the vertices so that it starts from 1 */ + for (i=0; i 0 && mrgk <= neqns); + marker[k] = marker[mrgk]; + } + + if (xadj[node] >= xadj[node+1]) { + xlnz[k+1] = xlnz[k]; + continue; + } + + /* USE RCHLNK TO LINK THROUGH THE STRUCTURE OF A(*,K) BELOW DIAGONAL */ + assert(k <= neqns && k > 0); + rchlnk[k] = neqns+1; + for (j=xadj[node]; j 0 && m <= neqns); + rchm = rchlnk[m]; + } while (rchm <= nabor); + + knz++; + assert(m > 0 && m <= neqns); + rchlnk[m] = nabor; + assert(nabor > 0 && nabor <= neqns); + rchlnk[nabor] = rchm; + assert(k > 0 && k <= neqns); + if (marker[nabor] != marker[k]) + mrkflg = 1; + } + + + /* TEST FOR MASS SYMBOLIC ELIMINATION */ + lmax = 0; + assert(mrgk >= 0 && mrgk <= neqns); + if (mrkflg != 0 || mrgk == 0 || mrglnk[mrgk] != 0) + goto L350; + xnzsub[k] = xnzsub[mrgk] + 1; + knz = xlnz[mrgk + 1] - (xlnz[mrgk] + 1); + goto L1400; + + +L350: + /* LINK THROUGH EACH COLUMN I THAT AFFECTS L(*,K) */ + i = k; + assert(i > 0 && i <= neqns); + while ((i = mrglnk[i]) != 0) { + assert(i > 0 && i <= neqns); + inz = xlnz[i+1] - (xlnz[i]+1); + jstrt = xnzsub[i] + 1; + jstop = xnzsub[i] + inz; + + if (inz > lmax) { + lmax = inz; + xnzsub[k] = jstrt; + } + + /* MERGE STRUCTURE OF L(*,I) IN NZSUB INTO RCHLNK. */ + rchm = k; + for (j=jstrt; j<=jstop; j++) { + nabor = nzsub[j]; + do { + m = rchm; + assert(m > 0 && m <= neqns); + rchm = rchlnk[m]; + } while (rchm < nabor); + + if (rchm != nabor) { + knz++; + assert(m > 0 && m <= neqns); + rchlnk[m] = nabor; + assert(nabor > 0 && nabor <= neqns); + rchlnk[nabor] = rchm; + rchm = nabor; + } + } + } + + + /* CHECK IF SUBSCRIPTS DUPLICATE THOSE OF ANOTHER COLUMN */ + if (knz == lmax) + goto L1400; + + /* OR IF TAIL OF K-1ST COLUMN MATCHES HEAD OF KTH */ + if (nzbeg > nzend) + goto L1200; + + assert(k > 0 && k <= neqns); + i = rchlnk[k]; + for (jstrt = nzbeg; jstrt <= nzend; ++jstrt) { + if (nzsub[jstrt] < i) + continue; + + if (nzsub[jstrt] == i) + goto L1000; + else + goto L1200; + } + goto L1200; + + +L1000: + xnzsub[k] = jstrt; + for (j = jstrt; j <= nzend; ++j) { + if (nzsub[j] != i) + goto L1200; + + assert(i > 0 && i <= neqns); + i = rchlnk[i]; + if (i > neqns) + goto L1400; + } + nzend = jstrt - 1; + + + /* COPY THE STRUCTURE OF L(*,K) FROM RCHLNK TO THE DATA STRUCTURE (XNZSUB, NZSUB) */ +L1200: + nzbeg = nzend + 1; + nzend += knz; + + if (nzend >= *maxsub) { + flag = 1; /* Out of memory */ + break; + } + + i = k; + for (j=nzbeg; j<=nzend; j++) { + assert(i > 0 && i <= neqns); + i = rchlnk[i]; + nzsub[j] = i; + assert(i > 0 && i <= neqns); + marker[i] = k; + } + xnzsub[k] = nzbeg; + assert(k > 0 && k <= neqns); + marker[k] = k; + + /* + * UPDATE THE VECTOR MRGLNK. NOTE COLUMN L(*,K) JUST FOUND + * IS REQUIRED TO DETERMINE COLUMN L(*,J), WHERE + * L(J,K) IS THE FIRST NONZERO IN L(*,K) BELOW DIAGONAL. + */ +L1400: + if (knz > 1) { + kxsub = xnzsub[k]; + i = nzsub[kxsub]; + assert(i > 0 && i <= neqns); + assert(k > 0 && k <= neqns); + mrglnk[k] = mrglnk[i]; + mrglnk[i] = k; + } + + xlnz[k + 1] = xlnz[k] + knz; + } + + if (flag == 0) { + *maxlnz = xlnz[neqns] - 1; + *maxsub = xnzsub[neqns]; + xnzsub[neqns + 1] = xnzsub[neqns]; + } + + + marker++; + mrglnk++; + rchlnk++; + nzsub++; + xnzsub++; + xlnz++; + invp++; + perm++; + adjncy++; + xadj++; + + gk_free((void **)&rchlnk, &mrglnk, &marker, LTERM); + + return flag; + +} + diff --git a/src/metis/programs/stat.c b/src/metis/programs/stat.c new file mode 100644 index 0000000..7faf587 --- /dev/null +++ b/src/metis/programs/stat.c @@ -0,0 +1,148 @@ +/*! +\file gklib.c +\brief Functions for printing various statistics for the computed partitionings + and orderings. + +\date Started 7/25/1997 +\author George +\author Copyright 1997-2009, Regents of the University of Minnesota +\version\verbatim $Id: stat.c 10046 2011-06-01 14:13:40Z karypis $ \endverbatim +*/ + + + +#include "metisbin.h" + + +/****************************************************************************/ +/*! This function computes various information associated with a partition */ +/****************************************************************************/ +void ComputePartitionInfo(params_t *params, graph_t *graph, idx_t *where) +{ + idx_t i, ii, j, k, nvtxs, ncon, nparts, tvwgt; + idx_t *xadj, *adjncy, *vwgt, *adjwgt, *kpwgts; + real_t *tpwgts, unbalance; + idx_t pid, ndom, maxndom, minndom, tndom, *pptr, *pind, *pdom; + idx_t ncmps, nover, *cptr, *cind, *cpwgts; + + nvtxs = graph->nvtxs; + ncon = graph->ncon; + xadj = graph->xadj; + adjncy = graph->adjncy; + vwgt = graph->vwgt; + adjwgt = graph->adjwgt; + + nparts = params->nparts; + tpwgts = params->tpwgts; + + /* Compute objective-related infomration */ + printf(" - Edgecut: %"PRIDX", communication volume: %"PRIDX".\n\n", + ComputeCut(graph, where), ComputeVolume(graph, where)); + + + /* Compute constraint-related information */ + kpwgts = ismalloc(ncon*nparts, 0, "ComputePartitionInfo: kpwgts"); + + for (i=0; i 0 ? 1 : 0); + tndom += ndom; + if (pid == 0 || maxndom < ndom) + maxndom = ndom; + if (pid == 0 || minndom > ndom) + minndom = ndom; + } + + printf(" - Subdomain connectivity: max: %"PRIDX", min: %"PRIDX", avg: %.2"PRREAL"\n\n", + maxndom, minndom, 1.0*tndom/nparts); + + gk_free((void **)&pptr, &pind, &pdom, LTERM); + + + /* Compute subdomain adjacency information */ + cptr = imalloc(nvtxs+1, "ComputePartitionInfo: cptr"); + cind = imalloc(nvtxs, "ComputePartitionInfo: cind"); + cpwgts = ismalloc(nparts, 0, "ComputePartitionInfo: cpwgts"); + + ncmps = FindPartitionInducedComponents(graph, where, cptr, cind); + if (ncmps == nparts) + printf(" - Each partition is contiguous.\n"); + else { + if (IsConnected(graph, 0)) { + for (nover=0, i=0; i - - -/************************************************************************* -* This function is the entry point of refinement -**************************************************************************/ -void Refine2Way(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, idxtype *tpwgts, float ubfactor) -{ - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); - - /* Compute the parameters of the coarsest graph */ - Compute2WayPartitionParams(ctrl, graph); - - for (;;) { - ASSERT(CheckBnd(graph)); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->RefTmr)); - switch (ctrl->RType) { - case 1: - Balance2Way(ctrl, graph, tpwgts, ubfactor); - FM_2WayEdgeRefine(ctrl, graph, tpwgts, 8); - break; - default: - errexit("Unknown refinement type: %d\n", ctrl->RType); - } - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); - - if (graph == orggraph) - break; - - graph = graph->finer; - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); - Project2WayPartition(ctrl, graph); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); -} - - -/************************************************************************* -* This function allocates memory for 2-way edge refinement -**************************************************************************/ -void Allocate2WayPartitionMemory(CtrlType *ctrl, GraphType *graph) -{ - idxtype nvtxs; - - nvtxs = graph->nvtxs; - - graph->pwgts = idxmalloc(2, "Allocate2WayPartitionMemory: pwgts"); - graph->where = idxmalloc(nvtxs, "Allocate2WayPartitionMemory: where"); - graph->id = idxmalloc(nvtxs, "Allocate2WayPartitionMemory: id"); - graph->ed = idxmalloc(nvtxs, "Allocate2WayPartitionMemory: ed"); - graph->bndptr = idxmalloc(nvtxs, "Allocate2WayPartitionMemory: bndptr"); - graph->bndind = idxmalloc(nvtxs, "Allocate2WayPartitionMemory: bndind"); - -} - - -/************************************************************************* -* This function computes the initial id/ed -**************************************************************************/ -void Compute2WayPartitionParams(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, j, k, l, nvtxs, nbnd, mincut; - idxtype *xadj, *vwgt, *adjncy, *adjwgt, *pwgts; - idxtype *id, *ed, *where; - idxtype *bndptr, *bndind; - idxtype me, other; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - pwgts = idxset(2, 0, graph->pwgts); - id = idxset(nvtxs, 0, graph->id); - ed = idxset(nvtxs, 0, graph->ed); - bndptr = idxset(nvtxs, -1, graph->bndptr); - bndind = graph->bndind; - - - /*------------------------------------------------------------ - / Compute now the id/ed degrees - /------------------------------------------------------------*/ - nbnd = mincut = 0; - for (i=0; i= 0 && where[i] <= 1); - me = where[i]; - pwgts[me] += vwgt[i]; - - for (j=xadj[i]; j 0 || xadj[i] == xadj[i+1]) { - mincut += ed[i]; - bndptr[i] = nbnd; - bndind[nbnd++] = i; - } - } - - graph->mincut = mincut/2; - graph->nbnd = nbnd; - - ASSERT(pwgts[0]+pwgts[1] == idxsum(nvtxs, vwgt)); -} - - - -/************************************************************************* -* This function projects a partition, and at the same time computes the -* parameters for refinement. -**************************************************************************/ -void Project2WayPartition(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, j, k, nvtxs, nbnd, me; - idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum; - idxtype *cmap, *where, *id, *ed, *bndptr, *bndind; - idxtype *cwhere, *cid, *ced, *cbndptr; - GraphType *cgraph; - - cgraph = graph->coarser; - cwhere = cgraph->where; - cid = cgraph->id; - ced = cgraph->ed; - cbndptr = cgraph->bndptr; - - nvtxs = graph->nvtxs; - cmap = graph->cmap; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - adjwgtsum = graph->adjwgtsum; - - Allocate2WayPartitionMemory(ctrl, graph); - - where = graph->where; - id = idxset(nvtxs, 0, graph->id); - ed = idxset(nvtxs, 0, graph->ed); - bndptr = idxset(nvtxs, -1, graph->bndptr); - bndind = graph->bndind; - - - /* Go through and project partition and compute id/ed for the nodes */ - for (i=0; i 0 || xadj[i] == xadj[i+1]) { - bndptr[i] = nbnd; - bndind[nbnd++] = i; - } - } - } - } - - graph->mincut = cgraph->mincut; - graph->nbnd = nbnd; - idxcopy(2, cgraph->pwgts, graph->pwgts); - - FreeGraph(graph->coarser, 1); - graph->coarser = NULL; - -} - diff --git a/src/metis/rename.h b/src/metis/rename.h deleted file mode 100644 index ff32b7a..0000000 --- a/src/metis/rename.h +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * rename.h - * - * This file contains header files - * - * Started 10/2/97 - * George - * - * $Id: rename.h,v 1.1 2002/08/10 04:34:09 karypis Exp $ - * - */ - -/*#define _RENAME_H_*/ - -#ifndef _RENAME_H_ -#define _RENAME_H_ - -/* balance.c */ -#define Balance2Way libmetis__Balance2Way -#define Bnd2WayBalance libmetis__Bnd2WayBalance -#define General2WayBalance libmetis__General2WayBalance - - -/* bucketsort.c */ -#define BucketSortKeysInc libmetis__BucketSortKeysInc - - -/* ccgraph.c */ -#define CreateCoarseGraph libmetis__CreateCoarseGraph -#define CreateCoarseGraphNoMask libmetis__CreateCoarseGraphNoMask -#define CreateCoarseGraph_NVW libmetis__CreateCoarseGraph_NVW -#define SetUpCoarseGraph libmetis__SetUpCoarseGraph -#define ReAdjustMemory libmetis__ReAdjustMemory - - -/* coarsen.c */ -#define Coarsen2Way libmetis__Coarsen2Way - - -/* compress.c */ -#define CompressGraph libmetis__CompressGraph -#define PruneGraph libmetis__PruneGraph - - -/* debug.c */ -#define ComputeCut libmetis__ComputeCut -#define CheckBnd libmetis__CheckBnd -#define CheckBnd2 libmetis__CheckBnd2 -#define CheckNodeBnd libmetis__CheckNodeBnd -#define CheckRInfo libmetis__CheckRInfo -#define CheckNodePartitionParams libmetis__CheckNodePartitionParams -#define IsSeparable libmetis__IsSeparable - - -/* estmem.c */ -#define EstimateCFraction libmetis__EstimateCFraction -#define ComputeCoarseGraphSize libmetis__ComputeCoarseGraphSize - - -/* fm.c */ -#define FM_2WayEdgeRefine libmetis__FM_2WayEdgeRefine - - -/* fortran.c */ -#define Change2CNumbering libmetis__Change2CNumbering -#define Change2FNumbering libmetis__Change2FNumbering -#define Change2FNumbering2 libmetis__Change2FNumbering2 -#define Change2FNumberingOrder libmetis__Change2FNumberingOrder -#define ChangeMesh2CNumbering libmetis__ChangeMesh2CNumbering -#define ChangeMesh2FNumbering libmetis__ChangeMesh2FNumbering -#define ChangeMesh2FNumbering2 libmetis__ChangeMesh2FNumbering2 - - -/* graph.c */ -#define SetUpGraph libmetis__SetUpGraph -#define SetUpGraph2 libmetis__SetUpGraph2 -#define VolSetUpGraph libmetis__VolSetUpGraph -#define RandomizeGraph libmetis__RandomizeGraph -#define IsConnectedSubdomain libmetis__IsConnectedSubdomain -#define IsConnected libmetis__IsConnected -#define IsConnected2 libmetis__IsConnected2 -#define FindComponents libmetis__FindComponents - - -/* initpart.c */ -#define Init2WayPartition libmetis__Init2WayPartition -#define InitSeparator libmetis__InitSeparator -#define GrowBisection libmetis__GrowBisection -#define GrowBisectionNode libmetis__GrowBisectionNode -#define RandomBisection libmetis__RandomBisection - - -/* kmetis.c */ -#define MlevelKWayPartitioning libmetis__MlevelKWayPartitioning - - -/* kvmetis.c */ -#define MlevelVolKWayPartitioning libmetis__MlevelVolKWayPartitioning - - -/* kwayfm.c */ -#define Random_KWayEdgeRefine libmetis__Random_KWayEdgeRefine -#define Greedy_KWayEdgeRefine libmetis__Greedy_KWayEdgeRefine -#define Greedy_KWayEdgeBalance libmetis__Greedy_KWayEdgeBalance - - -/* kwayrefine.c */ -#define RefineKWay libmetis__RefineKWay -#define AllocateKWayPartitionMemory libmetis__AllocateKWayPartitionMemory -#define ComputeKWayPartitionParams libmetis__ComputeKWayPartitionParams -#define ProjectKWayPartition libmetis__ProjectKWayPartition -#define IsBalanced libmetis__IsBalanced -#define ComputeKWayBoundary libmetis__ComputeKWayBoundary -#define ComputeKWayBalanceBoundary libmetis__ComputeKWayBalanceBoundary - - -/* kwayvolfm.c */ -#define Random_KWayVolRefine libmetis__Random_KWayVolRefine -#define Random_KWayVolRefineMConn libmetis__Random_KWayVolRefineMConn -#define Greedy_KWayVolBalance libmetis__Greedy_KWayVolBalance -#define Greedy_KWayVolBalanceMConn libmetis__Greedy_KWayVolBalanceMConn -#define KWayVolUpdate libmetis__KWayVolUpdate -#define ComputeKWayVolume libmetis__ComputeKWayVolume -#define ComputeVolume libmetis__ComputeVolume -#define CheckVolKWayPartitionParams libmetis__CheckVolKWayPartitionParams -#define ComputeVolSubDomainGraph libmetis__ComputeVolSubDomainGraph -#define EliminateVolSubDomainEdges libmetis__EliminateVolSubDomainEdges - - -/* kwayvolrefine.c */ -#define RefineVolKWay libmetis__RefineVolKWay -#define AllocateVolKWayPartitionMemory libmetis__AllocateVolKWayPartitionMemory -#define ComputeVolKWayPartitionParams libmetis__ComputeVolKWayPartitionParams -#define ComputeKWayVolGains libmetis__ComputeKWayVolGains -#define ProjectVolKWayPartition libmetis__ProjectVolKWayPartition -#define ComputeVolKWayBoundary libmetis__ComputeVolKWayBoundary -#define ComputeVolKWayBalanceBoundary libmetis__ComputeVolKWayBalanceBoundary - - -/* match.c */ -#define Match_RM libmetis__Match_RM -#define Match_RM_NVW libmetis__Match_RM_NVW -#define Match_HEM libmetis__Match_HEM -#define Match_SHEM libmetis__Match_SHEM - - -/* mbalance.c */ -#define MocBalance2Way libmetis__MocBalance2Way -#define MocGeneral2WayBalance libmetis__MocGeneral2WayBalance - - -/* mbalance2.c */ -#define MocBalance2Way2 libmetis__MocBalance2Way2 -#define MocGeneral2WayBalance2 libmetis__MocGeneral2WayBalance2 -#define SelectQueue3 libmetis__SelectQueue3 - - -/* mcoarsen.c */ -#define MCCoarsen2Way libmetis__MCCoarsen2Way - - -/* memory.c */ -#define AllocateWorkSpace libmetis__AllocateWorkSpace -#define FreeWorkSpace libmetis__FreeWorkSpace -#define WspaceAvail libmetis__WspaceAvail -#define idxwspacemalloc libmetis__idxwspacemalloc -#define idxwspacefree libmetis__idxwspacefree -#define fwspacemalloc libmetis__fwspacemalloc -#define CreateGraph libmetis__CreateGraph -#define InitGraph libmetis__InitGraph -#define FreeRData libmetis__FreeRData -#define FreeGraph libmetis__FreeGraph - - -/* mesh.c */ -#define TRIDUALMETIS libmetis__TRIDUALMETIS -#define TETDUALMETIS libmetis__TETDUALMETIS -#define HEXDUALMETIS libmetis__HEXDUALMETIS -#define TRINODALMETIS libmetis__TRINODALMETIS -#define TETNODALMETIS libmetis__TETNODALMETIS -#define HEXNODALMETIS libmetis__HEXNODALMETIS - - -/* mfm.c */ -#define MocFM_2WayEdgeRefine libmetis__MocFM_2WayEdgeRefine -#define SelectQueue libmetis__SelectQueue -#define BetterBalance libmetis__BetterBalance -#define Compute2WayHLoadImbalance libmetis__Compute2WayHLoadImbalance -#define Compute2WayHLoadImbalanceVec libmetis__Compute2WayHLoadImbalanceVec - - -/* mfm2.c */ -#define MocFM_2WayEdgeRefine2 libmetis__MocFM_2WayEdgeRefine2 -#define SelectQueue2 libmetis__SelectQueue2 -#define IsBetter2wayBalance libmetis__IsBetter2wayBalance - - -/* mincover.c */ -#define MinCover libmetis__MinCover -#define MinCover_Augment libmetis__MinCover_Augment -#define MinCover_Decompose libmetis__MinCover_Decompose -#define MinCover_ColDFS libmetis__MinCover_ColDFS -#define MinCover_RowDFS libmetis__MinCover_RowDFS - - -/* minitpart.c */ -#define MocInit2WayPartition libmetis__MocInit2WayPartition -#define MocGrowBisection libmetis__MocGrowBisection -#define MocRandomBisection libmetis__MocRandomBisection -#define MocInit2WayBalance libmetis__MocInit2WayBalance -#define SelectQueueoneWay libmetis__SelectQueueoneWay - - -/* minitpart2.c */ -#define MocInit2WayPartition2 libmetis__MocInit2WayPartition2 -#define MocGrowBisection2 libmetis__MocGrowBisection2 -#define MocGrowBisectionNew2 libmetis__MocGrowBisectionNew2 -#define MocInit2WayBalance2 libmetis__MocInit2WayBalance2 -#define SelectQueueOneWay2 libmetis__SelectQueueOneWay2 - - -/* mkmetis.c */ -#define MCMlevelKWayPartitioning libmetis__MCMlevelKWayPartitioning - - -/* mkwayfmh.c */ -#define MCRandom_KWayEdgeRefineHorizontal libmetis__MCRandom_KWayEdgeRefineHorizontal -#define MCGreedy_KWayEdgeBalanceHorizontal libmetis__MCGreedy_KWayEdgeBalanceHorizontal -#define AreAllHVwgtsBelow libmetis__AreAllHVwgtsBelow -#define AreAllHVwgtsAbove libmetis__AreAllHVwgtsAbove -#define ComputeHKWayLoadImbalance libmetis__ComputeHKWayLoadImbalance -#define MocIsHBalanced libmetis__MocIsHBalanced -#define IsHBalanceBetterFT libmetis__IsHBalanceBetterFT -#define IsHBalanceBetterTT libmetis__IsHBalanceBetterTT - - -/* mkwayrefine.c */ -#define MocRefineKWayHorizontal libmetis__MocRefineKWayHorizontal -#define MocAllocateKWayPartitionMemory libmetis__MocAllocateKWayPartitionMemory -#define MocComputeKWayPartitionParams libmetis__MocComputeKWayPartitionParams -#define MocProjectKWayPartition libmetis__MocProjectKWayPartition -#define MocComputeKWayBalanceBoundary libmetis__MocComputeKWayBalanceBoundary - - -/* mmatch.c */ -#define MCMatch_RM libmetis__MCMatch_RM -#define MCMatch_HEM libmetis__MCMatch_HEM -#define MCMatch_SHEM libmetis__MCMatch_SHEM -#define MCMatch_SHEBM libmetis__MCMatch_SHEBM -#define MCMatch_SBHEM libmetis__MCMatch_SBHEM -#define BetterVBalance libmetis__BetterVBalance -#define AreAllVwgtsBelowFast libmetis__AreAllVwgtsBelowFast - - -/* mmd.c */ -#define genmmd libmetis__genmmd -#define mmdelm libmetis__mmdelm -#define mmdint libmetis__mmdint -#define mmdnum libmetis__mmdnum -#define mmdupd libmetis__mmdupd - - -/* mpmetis.c */ -#define MCMlevelRecursiveBisection libmetis__MCMlevelRecursiveBisection -#define MCHMlevelRecursiveBisection libmetis__MCHMlevelRecursiveBisection -#define MCMlevelEdgeBisection libmetis__MCMlevelEdgeBisection -#define MCHMlevelEdgeBisection libmetis__MCHMlevelEdgeBisection - - -/* mrefine.c */ -#define MocRefine2Way libmetis__MocRefine2Way -#define MocAllocate2WayPartitionMemory libmetis__MocAllocate2WayPartitionMemory -#define MocCompute2WayPartitionParams libmetis__MocCompute2WayPartitionParams -#define MocProject2WayPartition libmetis__MocProject2WayPartition - - -/* mrefine2.c */ -#define MocRefine2Way2 libmetis__MocRefine2Way2 - - -/* mutil.c */ -#define AreAllVwgtsBelow libmetis__AreAllVwgtsBelow -#define AreAnyVwgtsBelow libmetis__AreAnyVwgtsBelow -#define AreAllVwgtsAbove libmetis__AreAllVwgtsAbove -#define ComputeLoadImbalance libmetis__ComputeLoadImbalance -#define AreAllBelow libmetis__AreAllBelow - - -/* myqsort.c */ -#define iidxsort libmetis__iidxsort -#define ikeysort libmetis__ikeysort -#define ikeyvalsort libmetis__ikeyvalsort -#define idkeysort libmetis__idkeysort - - -/* ometis.c */ -#define MlevelNestedDissection libmetis__MlevelNestedDissection -#define MlevelNestedDissectionCC libmetis__MlevelNestedDissectionCC -#define MlevelNodeBisectionMultiple libmetis__MlevelNodeBisectionMultiple -#define MlevelNodeBisection libmetis__MlevelNodeBisection -#define SplitGraphOrder libmetis__SplitGraphOrder -#define MMDOrder libmetis__MMDOrder -#define SplitGraphOrderCC libmetis__SplitGraphOrderCC - - -/* parmetis.c */ -#define MlevelNestedDissectionP libmetis__MlevelNestedDissectionP - - -/* pmetis.c */ -#define MlevelRecursiveBisection libmetis__MlevelRecursiveBisection -#define MlevelEdgeBisection libmetis__MlevelEdgeBisection -#define SplitGraphPart libmetis__SplitGraphPart -#define SetUpSplitGraph libmetis__SetUpSplitGraph - - -/* pqueue.c */ -#define PQueueInit libmetis__PQueueInit -#define PQueueReset libmetis__PQueueReset -#define PQueueFree libmetis__PQueueFree -#define PQueueInsert libmetis__PQueueInsert -#define PQueueDelete libmetis__PQueueDelete -#define PQueueUpdate libmetis__PQueueUpdate -#define PQueueUpdateUp libmetis__PQueueUpdateUp -#define PQueueGetMax libmetis__PQueueGetMax -#define PQueueSeeMax libmetis__PQueueSeeMax -#define CheckHeap libmetis__CheckHeap - - -/* refine.c */ -#define Refine2Way libmetis__Refine2Way -#define Allocate2WayPartitionMemory libmetis__Allocate2WayPartitionMemory -#define Compute2WayPartitionParams libmetis__Compute2WayPartitionParams -#define Project2WayPartition libmetis__Project2WayPartition - - -/* separator.c */ -#define ConstructSeparator libmetis__ConstructSeparator -#define ConstructMinCoverSeparator0 libmetis__ConstructMinCoverSeparator0 -#define ConstructMinCoverSeparator libmetis__ConstructMinCoverSeparator - - -/* sfm.c */ -#define FM_2WayNodeRefine libmetis__FM_2WayNodeRefine -#define FM_2WayNodeRefineEqWgt libmetis__FM_2WayNodeRefineEqWgt -#define FM_2WayNodeRefine_OneSided libmetis__FM_2WayNodeRefine_OneSided -#define FM_2WayNodeBalance libmetis__FM_2WayNodeBalance -#define ComputeMaxNodeGain libmetis__ComputeMaxNodeGain - - -/* srefine.c */ -#define Refine2WayNode libmetis__Refine2WayNode -#define Allocate2WayNodePartitionMemory libmetis__Allocate2WayNodePartitionMemory -#define Compute2WayNodePartitionParams libmetis__Compute2WayNodePartitionParams -#define Project2WayNodePartition libmetis__Project2WayNodePartition - - -/* stat.c */ -#define ComputePartitionInfo libmetis__ComputePartitionInfo -#define ComputePartitionBalance libmetis__ComputePartitionBalance -#define ComputeElementBalance libmetis__ComputeElementBalance - - -/* subdomains.c */ -#define Random_KWayEdgeRefineMConn libmetis__Random_KWayEdgeRefineMConn -#define Greedy_KWayEdgeBalanceMConn libmetis__Greedy_KWayEdgeBalanceMConn -#define PrintSubDomainGraph libmetis__PrintSubDomainGraph -#define ComputeSubDomainGraph libmetis__ComputeSubDomainGraph -#define EliminateSubDomainEdges libmetis__EliminateSubDomainEdges -#define MoveGroupMConn libmetis__MoveGroupMConn -#define EliminateComponents libmetis__EliminateComponents -#define MoveGroup libmetis__MoveGroup - - -/* timing.c */ -#define InitTimers libmetis__InitTimers -#define PrintTimers libmetis__PrintTimers - - -/* util.c */ -#define idxmalloc libmetis__idxmalloc -#define idxsmalloc libmetis__idxsmalloc -#define idxset libmetis__idxset -#define idxargmax libmetis__idxargmax -#define idxargmin libmetis__idxargmin -#define idxsum libmetis__idxsum -#define idxaxpy libmetis__idxaxpy -#define idxargmax_strd libmetis__idxargmax_strd -#define famax2 libmetis__famax2 -#define RandomPermute libmetis__RandomPermute -#define InitRandom libmetis__InitRandom - - -#endif - - diff --git a/src/metis/rkmetis.c b/src/metis/rkmetis.c deleted file mode 100644 index bb043bc..0000000 --- a/src/metis/rkmetis.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * kmetis.c - * - * This file contains the top level routines for the multilevel k-way partitioning - * algorithm KMETIS. - * - * Started 7/28/97 - * George - * - * $Id: rkmetis.c,v 1.3 2003/04/01 05:09:37 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function is the entry point for KMETIS -**************************************************************************/ -void METIS_RefineGraphKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i; - float *tpwgts; - - tpwgts = gk_fmalloc(*nparts, "KMETIS: tpwgts"); - for (i=0; i<*nparts; i++) - tpwgts[i] = 1.0/(1.0*(*nparts)); - - METIS_WRefineGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, - tpwgts, options, edgecut, part); - - gk_free((void **)&tpwgts, LTERM); -} - - -/************************************************************************* -* This function is the entry point for KWMETIS -**************************************************************************/ -void METIS_WRefineGraphKway(idxtype *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, - idxtype *adjwgt, idxtype *wgtflag, idxtype *numflag, idxtype *nparts, - float *tpwgts, idxtype *options, idxtype *edgecut, idxtype *part) -{ - idxtype i, j; - GraphType graph; - CtrlType ctrl; - - if (*numflag == 1) - Change2CNumbering(*nvtxs, xadj, adjncy); - - SetUpGraph(&graph, OP_KMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, *wgtflag); - - if (options[0] == 0) { /* Use the default parameters */ - ctrl.CType = KMETIS_CTYPE; - ctrl.IType = KMETIS_ITYPE; - ctrl.RType = KMETIS_RTYPE; - ctrl.dbglvl = DBG_REFINE; - ctrl.dbglvl = KMETIS_DBGLVL; - } - else { - ctrl.CType = options[OPTION_CTYPE]; - ctrl.IType = options[OPTION_ITYPE]; - ctrl.RType = options[OPTION_RTYPE]; - ctrl.dbglvl = options[OPTION_DBGLVL]; - } - ctrl.optype = OP_KMETIS; - ctrl.CoarsenTo = amax((*nvtxs)/(40*gk_log2(*nparts)), 20*(*nparts)); - ctrl.maxvwgt = 0; /* GK-MOD: Ensure that no coarsening will take place */ - - InitRandom(-1); - - AllocateWorkSpace(&ctrl, &graph, *nparts); - - IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl)); - IFSET(ctrl.dbglvl, DBG_TIME, gk_startcputimer(ctrl.TotalTmr)); - - *edgecut = MlevelKWayRefinement(&ctrl, &graph, *nparts, part, tpwgts, 1.03); - - IFSET(ctrl.dbglvl, DBG_TIME, gk_stopcputimer(ctrl.TotalTmr)); - IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl)); - - FreeWorkSpace(&ctrl, &graph); - - if (*numflag == 1) - Change2FNumbering(*nvtxs, xadj, adjncy, part); -} - - -/************************************************************************* -* This function takes a graph and produces a bisection of it -**************************************************************************/ -idxtype MlevelKWayRefinement(CtrlType *ctrl, GraphType *graph, idxtype nparts, idxtype *part, - float *tpwgts, float ubfactor) -{ - idxtype i, j, nvtxs, tvwgt, tpwgts2[2]; - GraphType *cgraph; - idxtype wgtflag=3, numflag=0, options[10], edgecut; - - cgraph = Coarsen2Way(ctrl, graph); - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->InitPartTmr)); - AllocateKWayPartitionMemory(ctrl, cgraph, nparts); - - if (cgraph->nvtxs != graph->nvtxs) - errexit("GK-MOD Failed: %d %d\n", cgraph->nvtxs, graph->nvtxs); - - for (i=0; invtxs; i++) - cgraph->where[graph->cmap[i]] = part[i]; - - RefineKWayRefinement(ctrl, graph, cgraph, nparts, tpwgts, ubfactor); - - idxcopy(graph->nvtxs, graph->where, part); - - FreeGraph(graph, 0); - - return graph->mincut; - -} - diff --git a/src/metis/separator.c b/src/metis/separator.c deleted file mode 100644 index 1995aa2..0000000 --- a/src/metis/separator.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * separator.c - * - * This file contains code for separator extraction - * - * Started 8/1/97 - * George - * - * $Id: separator.c,v 1.2 2002/08/10 06:29:34 karypis Exp $ - * - */ - -#include - -/************************************************************************* -* This function takes a bisection and constructs a minimum weight vertex -* separator out of it. It uses the node-based separator refinement for it. -**************************************************************************/ -void ConstructSeparator(CtrlType *ctrl, GraphType *graph, float ubfactor) -{ - idxtype i, j, k, nvtxs, nbnd; - idxtype *xadj, *where, *bndind; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - nbnd = graph->nbnd; - bndind = graph->bndind; - - where = idxcopy(nvtxs, graph->where, idxwspacemalloc(ctrl, nvtxs)); - - /* Put the nodes in the boundary into the separator */ - for (i=0; i 0) /* Ignore islands */ - where[j] = 2; - } - - FreeRData(graph); - - Allocate2WayNodePartitionMemory(ctrl, graph); - idxcopy(nvtxs, where, graph->where); - idxwspacefree(ctrl, nvtxs); - - ASSERT(IsSeparable(graph)); - - Compute2WayNodePartitionParams(ctrl, graph); - - ASSERT(CheckNodePartitionParams(graph)); - - FM_2WayNodeRefine(ctrl, graph, ubfactor, 8); - - ASSERT(IsSeparable(graph)); -} - - - -/************************************************************************* -* This function takes a bisection and constructs a minimum weight vertex -* separator out of it. It uses an unweighted minimum-cover algorithm -* followed by node-based separator refinement. -**************************************************************************/ -void ConstructMinCoverSeparator0(CtrlType *ctrl, GraphType *graph, float ubfactor) -{ - idxtype i, ii, j, jj, k, l, nvtxs, nbnd, bnvtxs[3], bnedges[2], csize; - idxtype *xadj, *adjncy, *bxadj, *badjncy; - idxtype *where, *bndind, *bndptr, *vmap, *ivmap, *cover; - - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - - nbnd = graph->nbnd; - bndind = graph->bndind; - bndptr = graph->bndptr; - where = graph->where; - - vmap = idxwspacemalloc(ctrl, nvtxs); - ivmap = idxwspacemalloc(ctrl, nbnd); - cover = idxwspacemalloc(ctrl, nbnd); - - if (nbnd > 0) { - /* Go through the boundary and determine the sizes of the bipartite graph */ - bnvtxs[0] = bnvtxs[1] = bnedges[0] = bnedges[1] = 0; - for (i=0; i 0) { - bnvtxs[k]++; - bnedges[k] += xadj[j+1]-xadj[j]; - } - } - - bnvtxs[2] = bnvtxs[0]+bnvtxs[1]; - bnvtxs[1] = bnvtxs[0]; - bnvtxs[0] = 0; - - bxadj = idxmalloc(bnvtxs[2]+1, "ConstructMinCoverSeparator: bxadj"); - badjncy = idxmalloc(bnedges[0]+bnedges[1]+1, "ConstructMinCoverSeparator: badjncy"); - - /* Construct the ivmap and vmap */ - ASSERT(idxset(nvtxs, -1, vmap) == vmap); - for (i=0; i 0) { - vmap[j] = bnvtxs[k]; - ivmap[bnvtxs[k]++] = j; - } - } - - /* OK, go through and put the vertices of each part starting from 0 */ - bnvtxs[1] = bnvtxs[0]; - bnvtxs[0] = 0; - bxadj[0] = l = 0; - for (k=0; k<2; k++) { - for (ii=0; iibndptr[jj])); - badjncy[l++] = vmap[jj]; - } - } - bxadj[++bnvtxs[k]] = l; - } - } - } - - ASSERT(l <= bnedges[0]+bnedges[1]); - - MinCover(bxadj, badjncy, bnvtxs[0], bnvtxs[1], cover, &csize); - - IFSET(ctrl->dbglvl, DBG_SEPINFO, - mprintf("Nvtxs: %6D, [%5D %5D], Cut: %6D, SS: [%6D %6D], Cover: %6D\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, bnvtxs[0], bnvtxs[1]-bnvtxs[0], csize)); - - for (i=0; idbglvl, DBG_SEPINFO, - mprintf("Nvtxs: %6D, [%5D %5D], Cut: %6D, SS: [%6D %6D], Cover: %6D\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, 0, 0, 0)); - } - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, graph->nbnd); - idxwspacefree(ctrl, graph->nbnd); - graph->nbnd = nbnd; - - - ASSERT(IsSeparable(graph)); -} - - - -/************************************************************************* -* This function takes a bisection and constructs a minimum weight vertex -* separator out of it. It uses an unweighted minimum-cover algorithm -* followed by node-based separator refinement. -**************************************************************************/ -void ConstructMinCoverSeparator(CtrlType *ctrl, GraphType *graph, float ubfactor) -{ - idxtype i, ii, j, jj, k, l, nvtxs, nbnd, bnvtxs[3], bnedges[2], csize; - idxtype *xadj, *adjncy, *bxadj, *badjncy; - idxtype *where, *bndind, *bndptr, *vmap, *ivmap, *cover; - - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - - nbnd = graph->nbnd; - bndind = graph->bndind; - bndptr = graph->bndptr; - where = graph->where; - - vmap = idxwspacemalloc(ctrl, nvtxs); - ivmap = idxwspacemalloc(ctrl, nbnd); - cover = idxwspacemalloc(ctrl, nbnd); - - if (nbnd > 0) { - /* Go through the boundary and determine the sizes of the bipartite graph */ - bnvtxs[0] = bnvtxs[1] = bnedges[0] = bnedges[1] = 0; - for (i=0; i 0) { - bnvtxs[k]++; - bnedges[k] += xadj[j+1]-xadj[j]; - } - } - - bnvtxs[2] = bnvtxs[0]+bnvtxs[1]; - bnvtxs[1] = bnvtxs[0]; - bnvtxs[0] = 0; - - bxadj = idxmalloc(bnvtxs[2]+1, "ConstructMinCoverSeparator: bxadj"); - badjncy = idxmalloc(bnedges[0]+bnedges[1]+1, "ConstructMinCoverSeparator: badjncy"); - - /* Construct the ivmap and vmap */ - ASSERT(idxset(nvtxs, -1, vmap) == vmap); - for (i=0; i 0) { - vmap[j] = bnvtxs[k]; - ivmap[bnvtxs[k]++] = j; - } - } - - /* OK, go through and put the vertices of each part starting from 0 */ - bnvtxs[1] = bnvtxs[0]; - bnvtxs[0] = 0; - bxadj[0] = l = 0; - for (k=0; k<2; k++) { - for (ii=0; iibndptr[jj])); - badjncy[l++] = vmap[jj]; - } - } - bxadj[++bnvtxs[k]] = l; - } - } - } - - ASSERT(l <= bnedges[0]+bnedges[1]); - - MinCover(bxadj, badjncy, bnvtxs[0], bnvtxs[1], cover, &csize); - - IFSET(ctrl->dbglvl, DBG_SEPINFO, - mprintf("Nvtxs: %6D, [%5D %5D], Cut: %6D, SS: [%6D %6D], Cover: %6D\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, bnvtxs[0], bnvtxs[1]-bnvtxs[0], csize)); - - for (i=0; idbglvl, DBG_SEPINFO, - mprintf("Nvtxs: %6D, [%5D %5D], Cut: %6D, SS: [%6D %6D], Cover: %6D\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, 0, 0, 0)); - } - - /* Prepare to refine the vertex separator */ - idxcopy(nvtxs, graph->where, vmap); - - FreeRData(graph); - - Allocate2WayNodePartitionMemory(ctrl, graph); - idxcopy(nvtxs, vmap, graph->where); - idxwspacefree(ctrl, nvtxs+2*graph->nbnd); - - Compute2WayNodePartitionParams(ctrl, graph); - - ASSERT(CheckNodePartitionParams(graph)); - - FM_2WayNodeRefine_OneSided(ctrl, graph, ubfactor, 6); - - ASSERT(IsSeparable(graph)); -} - diff --git a/src/metis/sfm.c b/src/metis/sfm.c deleted file mode 100644 index e089196..0000000 --- a/src/metis/sfm.c +++ /dev/null @@ -1,1069 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * sfm.c - * - * This file contains code that implementes an FM-based separator refinement - * - * Started 8/1/97 - * George - * - * $Id: sfm.c,v 1.3 2003/07/31 06:11:13 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function performs a node-based FM refinement -**************************************************************************/ -void FM_2WayNodeRefine(CtrlType *ctrl, GraphType *graph, float ubfactor, idxtype npasses) -{ - idxtype i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind; - idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; - idxtype *mptr, *mind, *moved, *swaps, *perm; - PQueueType parts[2]; - NRInfoType *rinfo; - idxtype higain, oldgain, mincut, initcut, mincutorder; - idxtype pass, to, other, limit; - idxtype badmaxpwgt, mindiff, newdiff; - idxtype u[2], g[2]; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - - bndind = graph->bndind; - bndptr = graph->bndptr; - where = graph->where; - pwgts = graph->pwgts; - rinfo = graph->nrinfo; - - - i = ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt); - PQueueInit(ctrl, &parts[0], nvtxs, i); - PQueueInit(ctrl, &parts[1], nvtxs, i); - - moved = idxwspacemalloc(ctrl, nvtxs); - swaps = idxwspacemalloc(ctrl, nvtxs); - mptr = idxwspacemalloc(ctrl, nvtxs+1); - mind = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D] Nv-Nb[%6D %6D]. ISep: %6D\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); - - badmaxpwgt = (int)(ubfactor*(pwgts[0]+pwgts[1]+pwgts[2])/2); - - for (pass=0; passmincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (ii=0; iioflags&OFLAG_COMPRESS ? amin(5*nbnd, 400) : amin(2*nbnd, 300)); - - /****************************************************** - * Get into the FM loop - *******************************************************/ - mptr[0] = nmind = 0; - mindiff = idxtype_abs(pwgts[0]-pwgts[1]); - to = (pwgts[0] < pwgts[1] ? 0 : 1); - for (nswaps=0; nswaps g[1] ? 0 : (g[0] < g[1] ? 1 : pass%2)); - /* to = (g[0] > g[1] ? 0 : (g[0] < g[1] ? 1 : (pwgts[0] < pwgts[1] ? 0 : 1))); */ - - if (pwgts[to]+vwgt[u[to]] > badmaxpwgt) - to = (to+1)%2; - } - else if (u[0] == -1 && u[1] == -1) { - break; - } - else if (u[0] != -1 && pwgts[0]+vwgt[u[0]] <= badmaxpwgt) { - to = 0; - } - else if (u[1] != -1 && pwgts[1]+vwgt[u[1]] <= badmaxpwgt) { - to = 1; - } - else - break; - - other = (to+1)%2; - - higain = PQueueGetMax(&parts[to]); - if (moved[higain] == -1) /* Delete if it was in the separator originally */ - PQueueDelete(&parts[other], higain, vwgt[higain]-rinfo[higain].edegrees[to]); - - ASSERT(bndptr[higain] != -1); - - pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]); - - newdiff = idxtype_abs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other])); - if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) { - mincut = pwgts[2]; - mincutorder = nswaps; - mindiff = newdiff; - } - else { - if (nswaps - mincutorder > limit) { - pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]); - break; /* No further improvement, break out */ - } - } - - BNDDelete(nbnd, bndind, bndptr, higain); - pwgts[to] += vwgt[higain]; - where[higain] = to; - moved[higain] = nswaps; - swaps[nswaps] = higain; - - - /********************************************************** - * Update the degrees of the affected nodes - ***********************************************************/ - for (j=xadj[higain]; jdbglvl, DBG_MOVEINFO, - mprintf("Moved %6D to %3D, Gain: %5D [%5D] [%4D %4D] \t[%5D %5D %5D]\n", higain, to, g[to], g[other], vwgt[u[to]], vwgt[u[other]], pwgts[0], pwgts[1], pwgts[2])); - - } - - - /**************************************************************** - * Roll back computation - *****************************************************************/ - for (nswaps--; nswaps>mincutorder; nswaps--) { - higain = swaps[nswaps]; - - ASSERT(CheckNodePartitionParams(graph)); - - to = where[higain]; - other = (to+1)%2; - INC_DEC(pwgts[2], pwgts[to], vwgt[higain]); - where[higain] = 2; - BNDInsert(nbnd, bndind, bndptr, higain); - - edegrees = rinfo[higain].edegrees; - edegrees[0] = edegrees[1] = 0; - for (j=xadj[higain]; jdbglvl, DBG_REFINE, - mprintf("\tMinimum sep: %6D at %5D, PWGTS: [%6D %6D], NBND: %6D\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd)); - - graph->mincut = mincut; - graph->nbnd = nbnd; - - if (mincutorder == -1 || mincut >= initcut) - break; - } - - PQueueFree(ctrl, &parts[0]); - PQueueFree(ctrl, &parts[1]); - - idxwspacefree(ctrl, nvtxs+1); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - -/************************************************************************* -* This function performs a node-based FM refinement -**************************************************************************/ -void FM_2WayNodeRefine2(CtrlType *ctrl, GraphType *graph, float ubfactor, idxtype npasses) -{ - idxtype i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind; - idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; - idxtype *mptr, *mind, *moved, *swaps, *perm; - PQueueType parts[2]; - NRInfoType *rinfo; - idxtype higain, oldgain, mincut, initcut, mincutorder; - idxtype pass, to, other, limit; - idxtype badmaxpwgt, mindiff, newdiff; - idxtype u[2], g[2]; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - - bndind = graph->bndind; - bndptr = graph->bndptr; - where = graph->where; - pwgts = graph->pwgts; - rinfo = graph->nrinfo; - - - i = ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt); - PQueueInit(ctrl, &parts[0], nvtxs, i); - PQueueInit(ctrl, &parts[1], nvtxs, i); - - moved = idxwspacemalloc(ctrl, nvtxs); - swaps = idxwspacemalloc(ctrl, nvtxs); - mptr = idxwspacemalloc(ctrl, nvtxs+1); - mind = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D] Nv-Nb[%6D %6D]. ISep: %6D\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); - - badmaxpwgt = (int)(ubfactor*(pwgts[0]+pwgts[1]+pwgts[2])/2); - - for (pass=0; passmincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (ii=0; iioflags&OFLAG_COMPRESS ? amin(5*nbnd, 400) : amin(2*nbnd, 300)); - - /****************************************************** - * Get into the FM loop - *******************************************************/ - mptr[0] = nmind = 0; - mindiff = idxtype_abs(pwgts[0]-pwgts[1]); - to = (pwgts[0] < pwgts[1] ? 0 : 1); - for (nswaps=0; nswaps g[1] ? 0 : (g[0] < g[1] ? 1 : pass%2)); - /* to = (g[0] > g[1] ? 0 : (g[0] < g[1] ? 1 : (pwgts[0] < pwgts[1] ? 0 : 1))); */ - - if (pwgts[to]+vwgt[u[to]] > badmaxpwgt) - to = (to+1)%2; - } - else if (u[0] == -1 && u[1] == -1) { - break; - } - else if (u[0] != -1 && pwgts[0]+vwgt[u[0]] <= badmaxpwgt) { - to = 0; - } - else if (u[1] != -1 && pwgts[1]+vwgt[u[1]] <= badmaxpwgt) { - to = 1; - } - else - break; - - other = (to+1)%2; - - higain = PQueueGetMax(&parts[to]); - if (moved[higain] == -1) /* Delete if it was in the separator originally */ - PQueueDelete(&parts[other], higain, vwgt[higain]-rinfo[higain].edegrees[to]); - - ASSERT(bndptr[higain] != -1); - - pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]); - - newdiff = idxtype_abs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other])); - if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) { - mincut = pwgts[2]; - mincutorder = nswaps; - mindiff = newdiff; - } - else { - if (nswaps - mincutorder > limit) { - pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]); - break; /* No further improvement, break out */ - } - } - - BNDDelete(nbnd, bndind, bndptr, higain); - pwgts[to] += vwgt[higain]; - where[higain] = to; - moved[higain] = nswaps; - swaps[nswaps] = higain; - - - /********************************************************** - * Update the degrees of the affected nodes - ***********************************************************/ - for (j=xadj[higain]; jdbglvl, DBG_MOVEINFO, - mprintf("Moved %6D to %3D, Gain: %5D [%5D] [%4D %4D] \t[%5D %5D %5D]\n", higain, to, g[to], g[other], vwgt[u[to]], vwgt[u[other]], pwgts[0], pwgts[1], pwgts[2])); - - } - - - /**************************************************************** - * Roll back computation - *****************************************************************/ - for (nswaps--; nswaps>mincutorder; nswaps--) { - higain = swaps[nswaps]; - - ASSERT(CheckNodePartitionParams(graph)); - - to = where[higain]; - other = (to+1)%2; - INC_DEC(pwgts[2], pwgts[to], vwgt[higain]); - where[higain] = 2; - BNDInsert(nbnd, bndind, bndptr, higain); - - edegrees = rinfo[higain].edegrees; - edegrees[0] = edegrees[1] = 0; - for (j=xadj[higain]; jdbglvl, DBG_REFINE, - mprintf("\tMinimum sep: %6D at %5D, PWGTS: [%6D %6D], NBND: %6D\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd)); - - graph->mincut = mincut; - graph->nbnd = nbnd; - - if (mincutorder == -1 || mincut >= initcut) - break; - } - - PQueueFree(ctrl, &parts[0]); - PQueueFree(ctrl, &parts[1]); - - idxwspacefree(ctrl, nvtxs+1); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - -/************************************************************************* -* This function performs a node-based FM refinement -**************************************************************************/ -void FM_2WayNodeRefineEqWgt(CtrlType *ctrl, GraphType *graph, idxtype npasses) -{ - idxtype i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind; - idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; - idxtype *mptr, *mind, *moved, *swaps, *perm; - PQueueType parts[2]; - NRInfoType *rinfo; - idxtype higain, oldgain, mincut, initcut, mincutorder; - idxtype pass, to, other, limit; - idxtype mindiff, newdiff; - idxtype u[2], g[2]; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - - bndind = graph->bndind; - bndptr = graph->bndptr; - where = graph->where; - pwgts = graph->pwgts; - rinfo = graph->nrinfo; - - - i = ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt); - PQueueInit(ctrl, &parts[0], nvtxs, i); - PQueueInit(ctrl, &parts[1], nvtxs, i); - - moved = idxwspacemalloc(ctrl, nvtxs); - swaps = idxwspacemalloc(ctrl, nvtxs); - mptr = idxwspacemalloc(ctrl, nvtxs+1); - mind = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D] Nv-Nb[%6D %6D]. ISep: %6D\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); - - for (pass=0; passmincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (ii=0; iioflags&OFLAG_COMPRESS ? amin(5*nbnd, 400) : amin(2*nbnd, 300)); - - /****************************************************** - * Get into the FM loop - *******************************************************/ - mptr[0] = nmind = 0; - mindiff = idxtype_abs(pwgts[0]-pwgts[1]); - to = (pwgts[0] < pwgts[1] ? 0 : 1); - for (nswaps=0; nswaps g[1] ? 0 : (g[0] < g[1] ? 1 : pass%2)); - } - } - other = (to+1)%2; - - if ((higain = PQueueGetMax(&parts[to])) == -1) - break; - - if (moved[higain] == -1) /* Delete if it was in the separator originally */ - PQueueDelete(&parts[other], higain, vwgt[higain]-rinfo[higain].edegrees[to]); - - ASSERT(bndptr[higain] != -1); - - pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]); - - newdiff = idxtype_abs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other])); - if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) { - mincut = pwgts[2]; - mincutorder = nswaps; - mindiff = newdiff; - } - else { - if (nswaps - mincutorder > limit) { - pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]); - break; /* No further improvement, break out */ - } - } - - BNDDelete(nbnd, bndind, bndptr, higain); - pwgts[to] += vwgt[higain]; - where[higain] = to; - moved[higain] = nswaps; - swaps[nswaps] = higain; - - - /********************************************************** - * Update the degrees of the affected nodes - ***********************************************************/ - for (j=xadj[higain]; jdbglvl, DBG_MOVEINFO, - mprintf("Moved %6D to %3D, Gain: %5D [%5D] [%4D %4D] \t[%5D %5D %5D]\n", higain, to, g[to], g[other], vwgt[u[to]], vwgt[u[other]], pwgts[0], pwgts[1], pwgts[2])); - - } - - - /**************************************************************** - * Roll back computation - *****************************************************************/ - for (nswaps--; nswaps>mincutorder; nswaps--) { - higain = swaps[nswaps]; - - ASSERT(CheckNodePartitionParams(graph)); - - to = where[higain]; - other = (to+1)%2; - INC_DEC(pwgts[2], pwgts[to], vwgt[higain]); - where[higain] = 2; - BNDInsert(nbnd, bndind, bndptr, higain); - - edegrees = rinfo[higain].edegrees; - edegrees[0] = edegrees[1] = 0; - for (j=xadj[higain]; jdbglvl, DBG_REFINE, - mprintf("\tMinimum sep: %6D at %5D, PWGTS: [%6D %6D], NBND: %6D\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd)); - - graph->mincut = mincut; - graph->nbnd = nbnd; - - if (mincutorder == -1 || mincut >= initcut) - break; - } - - PQueueFree(ctrl, &parts[0]); - PQueueFree(ctrl, &parts[1]); - - idxwspacefree(ctrl, nvtxs+1); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - -/************************************************************************* -* This function performs a node-based FM refinement. This is the -* one-way version -**************************************************************************/ -void FM_2WayNodeRefine_OneSided(CtrlType *ctrl, GraphType *graph, float ubfactor, idxtype npasses) -{ - idxtype i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind; - idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; - idxtype *mptr, *mind, *swaps, *perm; - PQueueType parts; - NRInfoType *rinfo; - idxtype higain, oldgain, mincut, initcut, mincutorder; - idxtype pass, to, other, limit; - idxtype badmaxpwgt, mindiff, newdiff; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - - bndind = graph->bndind; - bndptr = graph->bndptr; - where = graph->where; - pwgts = graph->pwgts; - rinfo = graph->nrinfo; - - PQueueInit(ctrl, &parts, nvtxs, ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt)); - - perm = idxwspacemalloc(ctrl, nvtxs); - swaps = idxwspacemalloc(ctrl, nvtxs); - mptr = idxwspacemalloc(ctrl, nvtxs+1); - mind = idxwspacemalloc(ctrl, nvtxs); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions-N1: [%6D %6D] Nv-Nb[%6D %6D]. ISep: %6D\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); - - badmaxpwgt = (int)(ubfactor*(pwgts[0]+pwgts[1]+pwgts[2])/2); - - to = (pwgts[0] < pwgts[1] ? 1 : 0); - for (pass=0; passmincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (ii=0; iioflags&OFLAG_COMPRESS ? amin(5*nbnd, 400) : amin(2*nbnd, 300)); - - /****************************************************** - * Get into the FM loop - *******************************************************/ - mptr[0] = nmind = 0; - mindiff = idxtype_abs(pwgts[0]-pwgts[1]); - for (nswaps=0; nswaps badmaxpwgt) - break; /* No point going any further. Balance will be bad */ - - pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]); - - newdiff = idxtype_abs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other])); - if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) { - mincut = pwgts[2]; - mincutorder = nswaps; - mindiff = newdiff; - } - else { - if (nswaps - mincutorder > limit) { - pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]); - break; /* No further improvement, break out */ - } - } - - BNDDelete(nbnd, bndind, bndptr, higain); - pwgts[to] += vwgt[higain]; - where[higain] = to; - swaps[nswaps] = higain; - - - /********************************************************** - * Update the degrees of the affected nodes - ***********************************************************/ - for (j=xadj[higain]; jdbglvl, DBG_MOVEINFO, - mprintf("Moved %6D to %3D, Gain: %5D [%5D] \t[%5D %5D %5D] [%3D %2D]\n", - higain, to, (vwgt[higain]-rinfo[higain].edegrees[other]), vwgt[higain], pwgts[0], pwgts[1], pwgts[2], nswaps, limit)); - - } - - - /**************************************************************** - * Roll back computation - *****************************************************************/ - for (nswaps--; nswaps>mincutorder; nswaps--) { - higain = swaps[nswaps]; - - ASSERT(CheckNodePartitionParams(graph)); - ASSERT(where[higain] == to); - - INC_DEC(pwgts[2], pwgts[to], vwgt[higain]); - where[higain] = 2; - BNDInsert(nbnd, bndind, bndptr, higain); - - edegrees = rinfo[higain].edegrees; - edegrees[0] = edegrees[1] = 0; - for (j=xadj[higain]; jdbglvl, DBG_REFINE, - mprintf("\tMinimum sep: %6D at %5D, PWGTS: [%6D %6D], NBND: %6D\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd)); - - graph->mincut = mincut; - graph->nbnd = nbnd; - - if (pass%2 == 1 && (mincutorder == -1 || mincut >= initcut)) - break; - } - - PQueueFree(ctrl, &parts); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs+1); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function performs a node-based FM refinement -**************************************************************************/ -void FM_2WayNodeBalance(CtrlType *ctrl, GraphType *graph, float ubfactor) -{ - idxtype i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps; - idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr; - idxtype *perm, *moved; - PQueueType parts; - NRInfoType *rinfo; - idxtype higain, oldgain; - idxtype pass, to, other; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - - bndind = graph->bndind; - bndptr = graph->bndptr; - where = graph->where; - pwgts = graph->pwgts; - rinfo = graph->nrinfo; - - if (idxtype_abs(pwgts[0]-pwgts[1]) < (int)((ubfactor-1.0)*(pwgts[0]+pwgts[1]))) - return; - if (idxtype_abs(pwgts[0]-pwgts[1]) < 3*idxsum(nvtxs, vwgt, 1)/nvtxs) - return; - - to = (pwgts[0] < pwgts[1] ? 0 : 1); - other = (to+1)%2; - - PQueueInit(ctrl, &parts, nvtxs, ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt)); - - perm = idxwspacemalloc(ctrl, nvtxs); - moved = idxset(nvtxs, -1, idxwspacemalloc(ctrl, nvtxs)); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D] Nv-Nb[%6D %6D]. ISep: %6D [B]\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut)); - - nbnd = graph->nbnd; - RandomPermute(nbnd, perm, 1); - for (ii=0; iidbglvl, DBG_MOVEINFO, - mprintf("Moved %6D to %3D, Gain: %3D, \t[%5D %5D %5D]\n", higain, to, vwgt[higain]-rinfo[higain].edegrees[other], pwgts[0], pwgts[1], pwgts[2])); - - - /********************************************************** - * Update the degrees of the affected nodes - ***********************************************************/ - for (j=xadj[higain]; j pwgts[other]) - break; - } - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\tBalanced sep: %6D at %4D, PWGTS: [%6D %6D], NBND: %6D\n", pwgts[2], nswaps, pwgts[0], pwgts[1], nbnd)); - - graph->mincut = pwgts[2]; - graph->nbnd = nbnd; - - - PQueueFree(ctrl, &parts); - - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - -/************************************************************************* -* This function computes the maximum possible gain for a vertex -**************************************************************************/ -idxtype ComputeMaxNodeGain(idxtype nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt) -{ - idxtype i, j, k, max; - - max = 0; - for (j=xadj[0]; j - - -/************************************************************************* -* This function is the entry point of the separator refinement -**************************************************************************/ -void Refine2WayNode(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, float ubfactor) -{ - - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->UncoarsenTmr)); - - for (;;) { - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->RefTmr)); - if (ctrl->RType != 15) - FM_2WayNodeBalance(ctrl, graph, ubfactor); - - switch (ctrl->RType) { - case 1: - FM_2WayNodeRefine(ctrl, graph, ubfactor, 8); - break; - case 2: - FM_2WayNodeRefine_OneSided(ctrl, graph, ubfactor, 8); - break; - case 3: - FM_2WayNodeRefine(ctrl, graph, ubfactor, 8); - FM_2WayNodeRefine_OneSided(ctrl, graph, ubfactor, 8); - break; - case 4: - FM_2WayNodeRefine_OneSided(ctrl, graph, ubfactor, 8); - FM_2WayNodeRefine(ctrl, graph, ubfactor, 8); - break; - case 5: - FM_2WayNodeRefineEqWgt(ctrl, graph, 8); - break; - } - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->RefTmr)); - - if (graph == orggraph) - break; - - graph = graph->finer; - IFSET(ctrl->dbglvl, DBG_TIME, gk_startcputimer(ctrl->ProjectTmr)); - Project2WayNodePartition(ctrl, graph); - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->ProjectTmr)); - } - - IFSET(ctrl->dbglvl, DBG_TIME, gk_stopcputimer(ctrl->UncoarsenTmr)); -} - - -/************************************************************************* -* This function allocates memory for 2-way edge refinement -**************************************************************************/ -void Allocate2WayNodePartitionMemory(CtrlType *ctrl, GraphType *graph) -{ - idxtype nvtxs; - - nvtxs = graph->nvtxs; - - graph->pwgts = idxmalloc(3, "Allocate2WayNodePartitionMemory: pwgts"); - graph->where = idxmalloc(nvtxs, "Allocate2WayNodePartitionMemory: where"); - graph->bndptr = idxmalloc(nvtxs, "Allocate2WayNodePartitionMemory: bndptr"); - graph->bndind = idxmalloc(nvtxs, "Allocate2WayNodePartitionMemory: bndind"); - graph->nrinfo = (NRInfoType *)gk_malloc(nvtxs*sizeof(NRInfoType), "Allocate2WayNodePartitionMemory: nrinfo"); -} - - - -/************************************************************************* -* This function computes the initial id/ed -**************************************************************************/ -void Compute2WayNodePartitionParams(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, j, k, l, nvtxs, nbnd; - idxtype *xadj, *adjncy, *adjwgt, *vwgt; - idxtype *where, *pwgts, *bndind, *bndptr, *edegrees; - NRInfoType *rinfo; - idxtype me, other; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - vwgt = graph->vwgt; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - rinfo = graph->nrinfo; - pwgts = idxset(3, 0, graph->pwgts); - bndind = graph->bndind; - bndptr = idxset(nvtxs, -1, graph->bndptr); - - - /*------------------------------------------------------------ - / Compute now the separator external degrees - /------------------------------------------------------------*/ - nbnd = 0; - for (i=0; i=0 && me <= 2); - - if (me == 2) { /* If it is on the separator do some computations */ - BNDInsert(nbnd, bndind, bndptr, i); - - edegrees = rinfo[i].edegrees; - edegrees[0] = edegrees[1] = 0; - - for (j=xadj[i]; jmincut = pwgts[2]; - graph->nbnd = nbnd; -} - - -/************************************************************************* -* This function computes the initial id/ed -**************************************************************************/ -void Project2WayNodePartition(CtrlType *ctrl, GraphType *graph) -{ - idxtype i, j, nvtxs; - idxtype *cmap, *where, *cwhere; - GraphType *cgraph; - - cgraph = graph->coarser; - cwhere = cgraph->where; - - nvtxs = graph->nvtxs; - cmap = graph->cmap; - - Allocate2WayNodePartitionMemory(ctrl, graph); - where = graph->where; - - /* Project the partition */ - for (i=0; i= 0 && where[i] <= 2, ("%d %d %d %d\n", i, cmap[i], where[i], cwhere[cmap[i]])); - } - - FreeGraph(graph->coarser, 1); - graph->coarser = NULL; - - Compute2WayNodePartitionParams(ctrl, graph); -} diff --git a/src/metis/stat.c b/src/metis/stat.c deleted file mode 100644 index 71e720f..0000000 --- a/src/metis/stat.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * stat.c - * - * This file computes various statistics - * - * Started 7/25/97 - * George - * - * $Id: stat.c,v 1.2 2002/08/10 06:29:34 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function computes cuts and balance information -**************************************************************************/ -void ComputePartitionInfo(GraphType *graph, idxtype nparts, idxtype *where) -{ - idxtype i, j, k, nvtxs, ncon, mustfree=0; - idxtype *xadj, *adjncy, *vwgt, *adjwgt, *kpwgts, *tmpptr; - idxtype *padjncy, *padjwgt, *padjcut; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - adjwgt = graph->adjwgt; - - if (vwgt == NULL) { - vwgt = graph->vwgt = idxsmalloc(nvtxs, 1, "vwgt"); - mustfree = 1; - } - if (adjwgt == NULL) { - adjwgt = graph->adjwgt = idxsmalloc(xadj[nvtxs], 1, "adjwgt"); - mustfree += 2; - } - - mprintf("%D-way Cut: %5D, Vol: %5D, ", nparts, ComputeCut(graph, where), ComputeVolume(graph, where)); - - /* Compute balance information */ - kpwgts = idxsmalloc(ncon*nparts, 0, "ComputePartitionInfo: kpwgts"); - - for (i=0; iwhere; - graph->where = where; - for (i=0; iwhere = tmpptr; - - if (mustfree == 1 || mustfree == 3) { - gk_free((void **)&vwgt, LTERM); - graph->vwgt = NULL; - } - if (mustfree == 2 || mustfree == 3) { - gk_free((void **)&adjwgt, LTERM); - graph->adjwgt = NULL; - } - - gk_free((void **)&kpwgts, &padjncy, &padjwgt, &padjcut, LTERM); -} - - -/************************************************************************* -* This function computes cuts and balance information -**************************************************************************/ -void ComputePartitionInfoBipartite(GraphType *graph, idxtype nparts, idxtype *where) -{ - idxtype i, j, k, nvtxs, ncon, mustfree=0; - idxtype *xadj, *adjncy, *vwgt, *vsize, *adjwgt, *kpwgts, *tmpptr; - idxtype *padjncy, *padjwgt, *padjcut; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - vsize = graph->vsize; - adjwgt = graph->adjwgt; - - if (vwgt == NULL) { - vwgt = graph->vwgt = idxsmalloc(nvtxs, 1, "vwgt"); - mustfree = 1; - } - if (adjwgt == NULL) { - adjwgt = graph->adjwgt = idxsmalloc(xadj[nvtxs], 1, "adjwgt"); - mustfree += 2; - } - - mprintf("%D-way Cut: %5D, Vol: %5D, ", nparts, ComputeCut(graph, where), ComputeVolume(graph, where)); - - /* Compute balance information */ - kpwgts = idxsmalloc(ncon*nparts, 0, "ComputePartitionInfo: kpwgts"); - - for (i=0; ivwgt = NULL; - } - if (mustfree == 2 || mustfree == 3) { - gk_free((void **)&adjwgt, LTERM); - graph->adjwgt = NULL; - } - - gk_free((void **)&kpwgts, &padjncy, &padjwgt, &padjcut, LTERM); -} - - - -/************************************************************************* -* This function computes the balance of the partitioning -**************************************************************************/ -void ComputePartitionBalance(GraphType *graph, idxtype nparts, idxtype *where, float *ubvec) -{ - idxtype i, j, nvtxs, ncon; - idxtype *kpwgts, *vwgt; - float balance; - - nvtxs = graph->nvtxs; - ncon = graph->ncon; - vwgt = graph->vwgt; - - kpwgts = idxsmalloc(nparts, 0, "ComputePartitionInfo: kpwgts"); - - if (vwgt == NULL) { - for (i=0; invtxs; i++) - kpwgts[where[i]] += vwgt[i*ncon+j]; - - ubvec[j] = 1.0*nparts*kpwgts[idxargmax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts, 1)); - } - } - - gk_free((void **)&kpwgts, LTERM); - -} - - -/************************************************************************* -* This function computes the balance of the element partitioning -**************************************************************************/ -float ComputeElementBalance(idxtype ne, idxtype nparts, idxtype *where) -{ - idxtype i; - idxtype *kpwgts; - float balance; - - kpwgts = idxsmalloc(nparts, 0, "ComputeElementBalance: kpwgts"); - - for (i=0; invtxs; - ncon = graph->ncon; - nvwgt = graph->nvwgt; - - kpwgts = gk_fmalloc(nparts, "ComputePartitionInfo: kpwgts"); - - for (j=0; jnvtxs; i++) - kpwgts[where[i]] += nvwgt[i*ncon+j]; - - ubvec[j] = (float)nparts*kpwgts[gk_fargmax(nparts, kpwgts)]/gk_fsum(nparts, kpwgts, 1); - } - - gk_free((void **)&kpwgts, LTERM); - -} - diff --git a/src/metis/streamio.c b/src/metis/streamio.c deleted file mode 100644 index 6cdc9d1..0000000 --- a/src/metis/streamio.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2007, Regents of the University of Minnesota - * - * streamio.c - * - * This function contains various functions that deal with extending the - * functionality of prinf and mscanf family of routines to understand - * additional format conversions. - * - * At this point the following conversions are support: - * %D for idxtype (which either 32 or 64 bits) - * - * Started 4/6/07 - * George - * - * $Id: util.c,v 1.4 2003/04/13 04:45:12 karypis Exp $ - */ - -#include - - -#define D_PATTERN "((^%|[^%]%)[0-\\s\\+]*[0-9]*)D" -#define D_SCAN_REPLACEMENT "$1" SCNIDX -#define D_PRINT_REPLACEMENT "$1" PRIIDX - - - -/************************************************************************* -* Custom mprintf() routine -**************************************************************************/ -int mprintf(char *format,...) -{ - char *new_format; - int rc; - va_list argp; - - gk_strstr_replace(format, D_PATTERN, D_PRINT_REPLACEMENT, "g", &new_format); - - /*mprintf("new_format: %s\n", new_format);*/ - - va_start(argp, format); - rc = vprintf((char *)new_format, argp); - va_end(argp); - - gk_free((void **)&new_format, LTERM); - - return rc; -} - - -/************************************************************************* -* Custom msprintf() routine -**************************************************************************/ -int msprintf(char *str, char *format,...) -{ - char *new_format; - int rc; - va_list argp; - - gk_strstr_replace(format, D_PATTERN, D_PRINT_REPLACEMENT, "g", &new_format); - - /*mprintf("new_format: %s\n", new_format);*/ - - va_start(argp, format); - rc = vsprintf(str, (char *)new_format, argp); - va_end(argp); - - gk_free((void **)&new_format, LTERM); - - return rc; -} - -/************************************************************************* -* Custom mfprintf() routine -**************************************************************************/ -int mfprintf(FILE *stream, char *format,...) -{ - char *new_format; - int rc; - va_list argp; - - gk_strstr_replace(format, D_PATTERN, D_PRINT_REPLACEMENT, "g", &new_format); - - /*mprintf("new_format: %s\n", new_format);*/ - - va_start(argp, format); - rc = vfprintf(stream, (char *)new_format, argp); - va_end(argp); - - gk_free((void **)&new_format, LTERM); - - return rc; -} - - -/************************************************************************* -* Custom mscanf() routine -**************************************************************************/ -int mscanf(char *format,...) -{ - char *new_format; - int rc; - va_list argp; - - gk_strstr_replace(format, D_PATTERN, D_SCAN_REPLACEMENT, "g", &new_format); - - /*mprintf("new_format: %s\n", new_format);*/ - - va_start(argp, format); - rc = vscanf((char *)new_format, argp); - va_end(argp); - - gk_free((void **)&new_format, LTERM); - - return rc; -} - - -/************************************************************************* -* Custom msscanf() routine -**************************************************************************/ -int msscanf(char *str, char *format,...) -{ - char *new_format; - int rc; - va_list argp; - - gk_strstr_replace(format, D_PATTERN, D_SCAN_REPLACEMENT, "g", &new_format); - - /*mprintf("new_format: %s\n", new_format);*/ - - va_start(argp, format); - rc = vsscanf(str, (char *)new_format, argp); - va_end(argp); - - gk_free((void **)&new_format, LTERM); - - return rc; -} - -/************************************************************************* -* Custom mfscanf() routine -**************************************************************************/ -int mfscanf(FILE *stream, char *format,...) -{ - char *new_format; - int rc; - va_list argp; - - gk_strstr_replace(format, D_PATTERN, D_SCAN_REPLACEMENT, "g", &new_format); - - /*mprintf("new_format: %s\n", new_format);*/ - - va_start(argp, format); - rc = vfscanf(stream, (char *)new_format, argp); - va_end(argp); - - gk_free((void **)&new_format, LTERM); - - return rc; -} - - diff --git a/src/metis/struct.h b/src/metis/struct.h deleted file mode 100644 index 471348b..0000000 --- a/src/metis/struct.h +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * struct.h - * - * This file contains data structures for ILU routines. - * - * Started 9/26/95 - * George - * - * $Id: struct.h,v 1.11 2003/04/23 02:41:59 karypis Exp $ - */ - - -#define MAXIDX ((idxtype)1<<(8*sizeof(idxtype))-2) - -/************************************************************************* -* The following data structure stores idxtype-val - double-key pairs -**************************************************************************/ -typedef struct { - idxtype dim; - double value; - idxtype nvtxs; - idxtype nsvtxs; - idxtype leafid; - idxtype partid; - - idxtype left, right; -} DTreeNodeType; - - - -/************************************************************************* -* The following data structure stores idxtype-val - double-key pairs -**************************************************************************/ -typedef struct { - idxtype nvtxs; - idxtype nnodes, nleafs; - idxtype *leafptr, *leafind, *leafwgt; /* Information as to which partitions are assigned to the leafs */ - idxtype *part; /* The actual partitioning vector */ - idxtype *leafpart; /* The leaf-based partitioning vector */ - DTreeNodeType *dtree; /* The decission tree itself */ -} ContactInfoType; - - - -/************************************************************************* -* The following data structure stores idxtype-key - double-value pairs -**************************************************************************/ -typedef struct { - double key; - idxtype val; -} DKeyValueType; - - -/************************************************************************* -* The following data structure stores key-value pair -**************************************************************************/ -struct KeyValueType { - idxtype key; - idxtype val; -}; - -typedef struct KeyValueType KeyValueType; - - -/************************************************************************* -* The following data structure will hold a node of a doubly-linked list. -**************************************************************************/ -struct ListNodeType { - idxtype id; /* The id value of the node */ - struct ListNodeType *prev, *next; /* It's a doubly-linked list */ -}; - -typedef struct ListNodeType ListNodeType; - - - -/************************************************************************* -* The following data structure is used to store the buckets for the -* refinment algorithms -**************************************************************************/ -struct PQueueType { - idxtype type; /* The type of the representation used */ - idxtype nnodes; - idxtype maxnodes; - idxtype mustfree; - - /* Linear array version of the data structures */ - idxtype pgainspan, ngainspan; /* plus and negative gain span */ - idxtype maxgain; - ListNodeType *nodes; - ListNodeType **buckets; - - /* Heap version of the data structure */ - KeyValueType *heap; - idxtype *locator; -}; - -typedef struct PQueueType PQueueType; - - -/************************************************************************* -* The following data structure stores an edge -**************************************************************************/ -struct edegreedef { - idxtype pid; - idxtype ed; -}; -typedef struct edegreedef EDegreeType; - - -/************************************************************************* -* The following data structure stores an edge for vol -**************************************************************************/ -struct vedegreedef { - idxtype pid; - idxtype ed, ned; - idxtype gv; -}; -typedef struct vedegreedef VEDegreeType; - - -/************************************************************************* -* This data structure holds various working space data -**************************************************************************/ -struct workspacedef { - idxtype *core; /* Where pairs, indices, and degrees are coming from */ - idxtype maxcore, ccore; - - EDegreeType *edegrees; - VEDegreeType *vedegrees; - idxtype cdegree; - - idxtype *auxcore; /* This points to the memory of the edegrees */ - - idxtype *pmat; /* An array of k^2 used for eliminating domain - connectivity in k-way refinement */ -}; - -typedef struct workspacedef WorkSpaceType; - - -/************************************************************************* -* The following data structure holds information on degrees for k-way -* partition -**************************************************************************/ -struct rinfodef { - idxtype id, ed; /* ID/ED of nodes */ - idxtype ndegrees; /* The number of different ext-degrees */ - EDegreeType *edegrees; /* List of edges */ -}; - -typedef struct rinfodef RInfoType; - - -/************************************************************************* -* The following data structure holds information on degrees for k-way -* vol-based partition -**************************************************************************/ -struct vrinfodef { - idxtype id, ed, nid; /* ID/ED of nodes */ - idxtype gv; /* IV/EV of nodes */ - idxtype ndegrees; /* The number of different ext-degrees */ - VEDegreeType *edegrees; /* List of edges */ -}; - -typedef struct vrinfodef VRInfoType; - - -/************************************************************************* -* The following data structure holds information on degrees for k-way -* partition -**************************************************************************/ -struct nrinfodef { - idxtype edegrees[2]; -}; - -typedef struct nrinfodef NRInfoType; - - -/************************************************************************* -* This data structure holds the input graph -**************************************************************************/ -struct graphdef { - idxtype nvtxs, nedges; /* The # of vertices and edges in the graph */ - idxtype *xadj; /* Pointers to the locally stored vertices */ - idxtype *vwgt; /* Vertex weights */ - idxtype *vsize; /* Vertex sizes for min-volume formulation */ - idxtype *adjncy; /* Array that stores the adjacency lists of nvtxs */ - idxtype *adjwgt; /* Array that stores the weights of the adjacency lists */ - - - /* These are to keep track control if the corresponding fields correspond to - application or library memory */ - int free_xadj, free_vwgt, free_vsize, free_adjncy, free_adjwgt; - - - double *coords; /* x, y, and z, Coordinates */ - - idxtype *adjwgtsum; /* The sum of the adjacency weight of each vertex */ - - idxtype *label; - - idxtype *cmap; - - /* Partition parameters */ - idxtype mincut, minvol; - idxtype *where, *pwgts; - idxtype nbnd; - idxtype *bndptr, *bndind; - - /* Bisection refinement parameters */ - idxtype *id, *ed; - - /* K-way refinement parameters */ - RInfoType *rinfo; - - /* K-way volume refinement parameters */ - VRInfoType *vrinfo; - - /* Node refinement information */ - NRInfoType *nrinfo; - - - /* Additional info needed by the MOC routines */ - idxtype ncon; /* The # of constrains */ - float *nvwgt; /* Normalized vertex weights */ - float *npwgts; /* The normalized partition weights */ - - struct graphdef *coarser, *finer; -}; - -typedef struct graphdef GraphType; - - - - -/************************************************************************* -* The following structure stores information used by Metis -**************************************************************************/ -struct controldef { - idxtype CoarsenTo; /* The # of vertices in the coarsest graph */ - idxtype dbglvl; /* Controls the debuging output of the program */ - idxtype CType; /* The type of coarsening */ - idxtype IType; /* The type of initial partitioning */ - idxtype RType; /* The type of refinement */ - idxtype maxvwgt; /* The maximum allowed weight for a vertex */ - float nmaxvwgt; /* The maximum allowed weight for a vertex for each constrain */ - idxtype optype; /* Type of operation */ - idxtype pfactor; /* .1*prunning factor */ - idxtype nseps; /* The number of separators to be found during multiple bisections */ - idxtype oflags; - - WorkSpaceType wspace; /* Work Space Informations */ - - /* Various Timers */ - double TotalTmr, InitPartTmr, MatchTmr, ContractTmr, CoarsenTmr, UncoarsenTmr, - SepTmr, RefTmr, ProjectTmr, SplitTmr, AuxTmr1, AuxTmr2, AuxTmr3, AuxTmr4, AuxTmr5, AuxTmr6; - -}; - -typedef struct controldef CtrlType; - - -/************************************************************************* -* The following data structure stores max-partition weight info for -* Vertical MOC k-way refinement -**************************************************************************/ -struct vpwgtdef { - float max[2][MAXNCON]; - idxtype imax[2][MAXNCON]; -}; - -typedef struct vpwgtdef VPInfoType; - - - - diff --git a/src/metis/subdomains.c b/src/metis/subdomains.c deleted file mode 100644 index dc1b390..0000000 --- a/src/metis/subdomains.c +++ /dev/null @@ -1,1295 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * subdomains.c - * - * This file contains functions that deal with prunning the number of - * adjacent subdomains in KMETIS - * - * Started 7/15/98 - * George - * - * $Id: subdomains.c,v 1.3 2003/07/31 06:04:52 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void Random_KWayEdgeRefineMConn(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, float ubfactor, idxtype npasses, idxtype ffactor) -{ - idxtype i, ii, iii, j, jj, k, l, pass, nvtxs, nmoves, nbnd, tvwgt, myndegrees; - idxtype from, me, to, oldcut, vwgt, gain; - idxtype maxndoms, nadd; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts; - idxtype *phtable, *pmat, *pmatptr, *ndoms; - EDegreeType *myedegrees; - RInfoType *myrinfo; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndptr = graph->bndptr; - bndind = graph->bndind; - - where = graph->where; - pwgts = graph->pwgts; - - pmat = ctrl->wspace.pmat; - phtable = idxwspacemalloc(ctrl, nparts); - ndoms = idxwspacemalloc(ctrl, nparts); - - ComputeSubDomainGraph(graph, nparts, pmat, ndoms); - - /* Setup the weight intervals of the various subdomains */ - minwgt = idxwspacemalloc(ctrl, nparts); - maxwgt = idxwspacemalloc(ctrl, nparts); - itpwgts = idxwspacemalloc(ctrl, nparts); - tvwgt = idxsum(nparts, pwgts, 1); - ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt, 1)); - - for (i=0; idbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D]-[%6D %6D], Balance: %5.3f, Nv-Nb[%6D %6D]. Cut: %6D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], minwgt[0], maxwgt[0], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd, - graph->mincut)); - - for (pass=0; passmincut); - - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - - oldcut = graph->mincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (nmoves=iii=0; iiinbnd; iii++) { - ii = perm[iii]; - if (ii >= nbnd) - continue; - i = bndind[ii]; - - myrinfo = graph->rinfo+i; - - if (myrinfo->ed >= myrinfo->id) { /* Total ED is too high */ - from = where[i]; - vwgt = graph->vwgt[i]; - - if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from]) - continue; /* This cannot be moved! */ - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - /* Determine the valid domains */ - for (j=0; j maxndoms-1) { - phtable[to] = 0; - nadd = maxndoms; - break; - } - nadd++; - } - } - if (ndoms[to]+nadd > maxndoms) - phtable[to] = 0; - if (nadd == 0) - phtable[to] = 2; - } - - /* Find the first valid move */ - j = myrinfo->id; - for (k=0; kid. Allow good nodes to move */ - if (pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain && gain >= 0) - break; - } - if (k == myndegrees) - continue; /* break out if you did not find a candidate */ - - for (j=k+1; j myedegrees[k].ed && pwgts[to]+vwgt <= maxwgt[to]) || - (myedegrees[j].ed == myedegrees[k].ed && - itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid])) - k = j; - } - - to = myedegrees[k].pid; - - j = 0; - if (myedegrees[k].ed-myrinfo->id > 0) - j = 1; - else if (myedegrees[k].ed-myrinfo->id == 0) { - if (/*(iii&7) == 0 ||*/ phtable[myedegrees[k].pid] == 2 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from]) - j = 1; - } - if (j == 0) - continue; - - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - graph->mincut -= myedegrees[k].ed-myrinfo->id; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D to %3D. Gain: %4D. Cut: %6D\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut)); - - /* Update pmat to reflect the move of 'i' */ - pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed); - pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed); - if (pmat[from*nparts+to] == 0) { - ndoms[from]--; - if (ndoms[from]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - if (pmat[to*nparts+from] == 0) { - ndoms[to]--; - if (ndoms[to]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - - /* Update where, weight, and ID/ED information of the vertex you moved */ - where[i] = to; - INC_DEC(pwgts[to], pwgts[from], vwgt); - myrinfo->ed += myrinfo->id-myedegrees[k].ed; - SWAP(myrinfo->id, myedegrees[k].ed, j); - if (myedegrees[k].ed == 0) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].pid = from; - - if (myrinfo->ed-myrinfo->id < 0) - BNDDelete(nbnd, bndind, bndptr, i); - - /* Update the degrees of adjacent vertices */ - for (j=xadj[i]; jrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - ASSERT(CheckRInfo(myrinfo)); - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1) - BNDInsert(nbnd, bndind, bndptr, ii); - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1) - BNDDelete(nbnd, bndind, bndptr, ii); - } - - /* Remove contribution from the .ed of 'from' */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ed == adjwgt[j]) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].ed -= adjwgt[j]; - break; - } - } - } - - /* Add contribution to the .ed of 'to' */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - - /* Update pmat to reflect the move of 'i' for domains other than 'from' and 'to' */ - if (me != from && me != to) { - pmat[me*nparts+from] -= adjwgt[j]; - pmat[from*nparts+me] -= adjwgt[j]; - if (pmat[me*nparts+from] == 0) { - ndoms[me]--; - if (ndoms[me]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - if (pmat[from*nparts+me] == 0) { - ndoms[from]--; - if (ndoms[from]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - - if (pmat[me*nparts+to] == 0) { - ndoms[me]++; - if (ndoms[me] > maxndoms) { - mprintf("You just increased the maxndoms: %D %D\n", ndoms[me], maxndoms); - maxndoms = ndoms[me]; - } - } - if (pmat[to*nparts+me] == 0) { - ndoms[to]++; - if (ndoms[to] > maxndoms) { - mprintf("You just increased the maxndoms: %D %D\n", ndoms[to], maxndoms); - maxndoms = ndoms[to]; - } - } - pmat[me*nparts+to] += adjwgt[j]; - pmat[to*nparts+me] += adjwgt[j]; - } - - ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]); - ASSERT(CheckRInfo(myrinfo)); - - } - nmoves++; - } - } - - graph->nbnd = nbnd; - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\t[%6D %6D], Balance: %5.3f, Nb: %6D. Nmoves: %5D, Cut: %5D, Vol: %5D, %D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, - graph->mincut, ComputeVolume(graph, where), idxsum(nparts, ndoms, 1))); - - if (graph->mincut == oldcut) - break; - } - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); -} - - - -/************************************************************************* -* This function performs k-way refinement -**************************************************************************/ -void Greedy_KWayEdgeBalanceMConn(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, float ubfactor, idxtype npasses) -{ - idxtype i, ii, iii, j, jj, k, l, pass, nvtxs, nbnd, tvwgt, myndegrees, oldgain, gain, nmoves; - idxtype from, me, to, oldcut, vwgt, maxndoms, nadd; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *moved, *itpwgts; - idxtype *phtable, *pmat, *pmatptr, *ndoms; - EDegreeType *myedegrees; - RInfoType *myrinfo; - PQueueType queue; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - bndind = graph->bndind; - bndptr = graph->bndptr; - - where = graph->where; - pwgts = graph->pwgts; - - pmat = ctrl->wspace.pmat; - phtable = idxwspacemalloc(ctrl, nparts); - ndoms = idxwspacemalloc(ctrl, nparts); - - ComputeSubDomainGraph(graph, nparts, pmat, ndoms); - - - /* Setup the weight intervals of the various subdomains */ - minwgt = idxwspacemalloc(ctrl, nparts); - maxwgt = idxwspacemalloc(ctrl, nparts); - itpwgts = idxwspacemalloc(ctrl, nparts); - tvwgt = idxsum(nparts, pwgts, 1); - ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt, 1)); - - for (i=0; iadjwgtsum[idxargmax(nvtxs, graph->adjwgtsum)]); - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("Partitions: [%6D %6D]-[%6D %6D], Balance: %5.3f, Nv-Nb[%6D %6D]. Cut: %6D [B]\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], minwgt[0], maxwgt[0], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd, - graph->mincut)); - - for (pass=0; passmincut); - - /* Check to see if things are out of balance, given the tolerance */ - for (i=0; i maxwgt[i]) - break; - } - if (i == nparts) /* Things are balanced. Return right away */ - break; - - PQueueReset(&queue); - idxset(nvtxs, -1, moved); - - oldcut = graph->mincut; - nbnd = graph->nbnd; - - RandomPermute(nbnd, perm, 1); - for (ii=0; iirinfo[i].ed - graph->rinfo[i].id); - moved[i] = 2; - } - - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - - for (nmoves=0;;) { - if ((i = PQueueGetMax(&queue)) == -1) - break; - moved[i] = 1; - - myrinfo = graph->rinfo+i; - from = where[i]; - vwgt = graph->vwgt[i]; - - if (pwgts[from]-vwgt < minwgt[from]) - continue; /* This cannot be moved! */ - - myedegrees = myrinfo->edegrees; - myndegrees = myrinfo->ndegrees; - - /* Determine the valid domains */ - for (j=0; j maxndoms-1) { - phtable[to] = 0; - nadd = maxndoms; - break; - } - nadd++; - } - } - if (ndoms[to]+nadd > maxndoms) - phtable[to] = 0; - } - - for (k=0; k minwgt[to] && myedegrees[k].ed-myrinfo->id < 0) - continue; - - /*===================================================================== - * If we got here, we can now move the vertex from 'from' to 'to' - *======================================================================*/ - graph->mincut -= myedegrees[k].ed-myrinfo->id; - - IFSET(ctrl->dbglvl, DBG_MOVEINFO, mprintf("\t\tMoving %6D to %3D. Gain: %4D. Cut: %6D\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut)); - - /* Update pmat to reflect the move of 'i' */ - pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed); - pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed); - if (pmat[from*nparts+to] == 0) { - ndoms[from]--; - if (ndoms[from]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - if (pmat[to*nparts+from] == 0) { - ndoms[to]--; - if (ndoms[to]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - - - /* Update where, weight, and ID/ED information of the vertex you moved */ - where[i] = to; - INC_DEC(pwgts[to], pwgts[from], vwgt); - myrinfo->ed += myrinfo->id-myedegrees[k].ed; - SWAP(myrinfo->id, myedegrees[k].ed, j); - if (myedegrees[k].ed == 0) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].pid = from; - - if (myrinfo->ed == 0) - BNDDelete(nbnd, bndind, bndptr, i); - - /* Update the degrees of adjacent vertices */ - for (j=xadj[i]; jrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - ASSERT(CheckRInfo(myrinfo)); - - oldgain = (myrinfo->ed-myrinfo->id); - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - - if (myrinfo->ed > 0 && bndptr[ii] == -1) - BNDInsert(nbnd, bndind, bndptr, ii); - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - - if (myrinfo->ed == 0 && bndptr[ii] != -1) - BNDDelete(nbnd, bndind, bndptr, ii); - } - - /* Remove contribution from the .ed of 'from' */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ed == adjwgt[j]) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].ed -= adjwgt[j]; - break; - } - } - } - - /* Add contribution to the .ed of 'to' */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - - /* Update pmat to reflect the move of 'i' for domains other than 'from' and 'to' */ - if (me != from && me != to) { - pmat[me*nparts+from] -= adjwgt[j]; - pmat[from*nparts+me] -= adjwgt[j]; - if (pmat[me*nparts+from] == 0) { - ndoms[me]--; - if (ndoms[me]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - if (pmat[from*nparts+me] == 0) { - ndoms[from]--; - if (ndoms[from]+1 == maxndoms) - maxndoms = ndoms[idxargmax(nparts, ndoms)]; - } - - if (pmat[me*nparts+to] == 0) { - ndoms[me]++; - if (ndoms[me] > maxndoms) { - mprintf("You just increased the maxndoms: %D %D\n", ndoms[me], maxndoms); - maxndoms = ndoms[me]; - } - } - if (pmat[to*nparts+me] == 0) { - ndoms[to]++; - if (ndoms[to] > maxndoms) { - mprintf("You just increased the maxndoms: %D %D\n", ndoms[to], maxndoms); - maxndoms = ndoms[to]; - } - } - pmat[me*nparts+to] += adjwgt[j]; - pmat[to*nparts+me] += adjwgt[j]; - } - - /* Update the queue */ - if (me == to || me == from) { - gain = myrinfo->ed-myrinfo->id; - if (moved[ii] == 2) { - if (myrinfo->ed > 0) - PQueueUpdate(&queue, ii, oldgain, gain); - else { - PQueueDelete(&queue, ii, oldgain); - moved[ii] = -1; - } - } - else if (moved[ii] == -1 && myrinfo->ed > 0) { - PQueueInsert(&queue, ii, gain); - moved[ii] = 2; - } - } - - ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]); - ASSERT(CheckRInfo(myrinfo)); - } - nmoves++; - } - - graph->nbnd = nbnd; - - IFSET(ctrl->dbglvl, DBG_REFINE, - mprintf("\t[%6D %6D], Balance: %5.3f, Nb: %6D. Nmoves: %5D, Cut: %6D, %D\n", - pwgts[idxargmin(nparts, pwgts)], pwgts[idxargmax(nparts, pwgts)], - 1.0*nparts*pwgts[idxargmax(nparts, pwgts)]/tvwgt, graph->nbnd, - nmoves, graph->mincut, idxsum(nparts, ndoms, 1))); - } - - PQueueFree(ctrl, &queue); - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); -} - - - - -/************************************************************************* -* This function computes the subdomain graph -**************************************************************************/ -void PrintSubDomainGraph(GraphType *graph, idxtype nparts, idxtype *where) -{ - idxtype i, j, k, me, nvtxs, total, max; - idxtype *xadj, *adjncy, *adjwgt, *pmat; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - pmat = idxsmalloc(nparts*nparts, 0, "ComputeSubDomainGraph: pmat"); - - for (i=0; i 0) - k++; - } - total += k; - - if (k > max) - max = k; -/* - mprintf("%2D -> %2D ", i, k); - for (j=0; j 0) - mprintf("[%2D %4D] ", j, pmat[i*nparts+j]); - } - mprintf("\n"); -*/ - } - mprintf("Total adjacent subdomains: %D, Max: %D\n", total, max); - - gk_free((void **)&pmat, LTERM); -} - - - -/************************************************************************* -* This function computes the subdomain graph -**************************************************************************/ -void ComputeSubDomainGraph(GraphType *graph, idxtype nparts, idxtype *pmat, idxtype *ndoms) -{ - idxtype i, j, k, me, nvtxs, ndegrees; - idxtype *xadj, *adjncy, *adjwgt, *where; - RInfoType *rinfo; - EDegreeType *edegrees; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - where = graph->where; - rinfo = graph->rinfo; - - idxset(nparts*nparts, 0, pmat); - - for (i=0; i 0) { - me = where[i]; - ndegrees = rinfo[i].ndegrees; - edegrees = rinfo[i].edegrees; - - k = me*nparts; - for (j=0; j 0) - ndoms[i]++; - } - } - -} - - - - - -/************************************************************************* -* This function computes the subdomain graph -**************************************************************************/ -void EliminateSubDomainEdges(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts) -{ - idxtype i, ii, j, k, me, other, nvtxs, total, max, avg, totalout, nind, ncand, ncand2, target, target2, nadd; - idxtype min, move, cpwgt, tvwgt; - idxtype *xadj, *adjncy, *vwgt, *adjwgt, *pwgts, *where, *maxpwgt, *pmat, *ndoms, *mypmat, *otherpmat, *ind; - KeyValueType *cand, *cand2; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - adjwgt = graph->adjwgt; - - where = graph->where; - pwgts = graph->pwgts; /* We agk_fsume that this is properly initialized */ - - maxpwgt = idxwspacemalloc(ctrl, nparts); - ndoms = idxwspacemalloc(ctrl, nparts); - otherpmat = idxwspacemalloc(ctrl, nparts); - ind = idxwspacemalloc(ctrl, nvtxs); - pmat = ctrl->wspace.pmat; - - cand = (KeyValueType *)gk_malloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand"); - cand2 = (KeyValueType *)gk_malloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand"); - - /* Compute the pmat matrix and ndoms */ - ComputeSubDomainGraph(graph, nparts, pmat, ndoms); - - - /* Compute the maximum allowed weight for each domain */ - tvwgt = idxsum(nparts, pwgts, 1); - for (i=0; i 0) { - cand2[ncand2].key = mypmat[i]; - cand2[ncand2++].val = i; - } - } - ikeysort(ncand2, cand2); - - move = 0; - for (min=0; min totalout/(2*ndoms[me])) - break; - - other = cand2[min].val; - - /*mprintf("\tMinOut: %D to %D\n", mypmat[other], other);*/ - - idxset(nparts, 0, otherpmat); - - /* Go and find the vertices in 'other' that are connected in 'me' */ - for (nind=0, i=0; i 0) { - cand[ncand].key = -otherpmat[i]; - cand[ncand++].val = i; - } - } - ikeysort(ncand, cand); - - /* - * Go through and the select the first domain that is common with 'me', and - * does not increase the ndoms[target] higher than my ndoms, subject to the - * maxpwgt constraint. Traversal is done from the mostly connected to the least. - */ - target = target2 = -1; - for (i=0; i 0) { - if (pwgts[k] + cpwgt > maxpwgt[k]) /* Check if balance will go off */ - continue; - - for (j=0; j 0 && ndoms[j] >= ndoms[me]-1 && pmat[nparts*j+k] == 0) - break; - } - if (j == nparts) { /* No bad second level effects */ - for (nadd=0, j=0; j 0 && pmat[nparts*k+j] == 0) - nadd++; - } - - /*mprintf("\t\tto=%D, nadd=%D, %D\n", k, nadd, ndoms[k]);*/ - if (target2 == -1 && ndoms[k]+nadd < ndoms[me]) { - target2 = k; - } - if (nadd == 0) { - target = k; - break; - } - } - } - } - if (target == -1 && target2 != -1) - target = target2; - - if (target == -1) { - /* mprintf("\t\tCould not make the move\n");*/ - continue; - } - - /*mprintf("\t\tMoving to %D\n", target);*/ - - /* Update the partition weights */ - INC_DEC(pwgts[target], pwgts[other], cpwgt); - - MoveGroupMConn(ctrl, graph, ndoms, pmat, nparts, target, nind, ind); - - move = 1; - break; - } - - if (move == 0) - break; - } - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); - - gk_free((void **)&cand, &cand2, LTERM); -} - - -/************************************************************************* -* This function moves a collection of vertices and updates their rinfo -**************************************************************************/ -void MoveGroupMConn(CtrlType *ctrl, GraphType *graph, idxtype *ndoms, idxtype *pmat, - idxtype nparts, idxtype to, idxtype nind, idxtype *ind) -{ - idxtype i, ii, iii, j, jj, k, l, nvtxs, nbnd, myndegrees; - idxtype from, me; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *bndptr, *bndind; - EDegreeType *myedegrees; - RInfoType *myrinfo; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - bndptr = graph->bndptr; - bndind = graph->bndind; - - nbnd = graph->nbnd; - - for (iii=0; iiirinfo+i; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[i+1]-xadj[i]; - myrinfo->ndegrees = 0; - } - myedegrees = myrinfo->edegrees; - - /* find the location of 'to' in myrinfo or create it if it is not there */ - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) - break; - } - if (k == myrinfo->ndegrees) { - myedegrees[k].pid = to; - myedegrees[k].ed = 0; - myrinfo->ndegrees++; - } - - graph->mincut -= myedegrees[k].ed-myrinfo->id; - - /* Update pmat to reflect the move of 'i' */ - pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed); - pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed); - if (pmat[from*nparts+to] == 0) - ndoms[from]--; - if (pmat[to*nparts+from] == 0) - ndoms[to]--; - - /* Update where, weight, and ID/ED information of the vertex you moved */ - where[i] = to; - myrinfo->ed += myrinfo->id-myedegrees[k].ed; - SWAP(myrinfo->id, myedegrees[k].ed, j); - if (myedegrees[k].ed == 0) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].pid = from; - - if (myrinfo->ed-myrinfo->id < 0 && bndptr[i] != -1) - BNDDelete(nbnd, bndind, bndptr, i); - - /* Update the degrees of adjacent vertices */ - for (j=xadj[i]; jrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - ASSERT(CheckRInfo(myrinfo)); - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1) - BNDInsert(nbnd, bndind, bndptr, ii); - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1) - BNDDelete(nbnd, bndind, bndptr, ii); - } - - /* Remove contribution from the .ed of 'from' */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ed == adjwgt[j]) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].ed -= adjwgt[j]; - break; - } - } - } - - /* Add contribution to the .ed of 'to' */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - - /* Update pmat to reflect the move of 'i' for domains other than 'from' and 'to' */ - if (me != from && me != to) { - pmat[me*nparts+from] -= adjwgt[j]; - pmat[from*nparts+me] -= adjwgt[j]; - if (pmat[me*nparts+from] == 0) - ndoms[me]--; - if (pmat[from*nparts+me] == 0) - ndoms[from]--; - - if (pmat[me*nparts+to] == 0) - ndoms[me]++; - if (pmat[to*nparts+me] == 0) - ndoms[to]++; - - pmat[me*nparts+to] += adjwgt[j]; - pmat[to*nparts+me] += adjwgt[j]; - } - - ASSERT(CheckRInfo(myrinfo)); - } - - ASSERT(CheckRInfo(graph->rinfo+i)); - } - - graph->nbnd = nbnd; - -} - - - - -/************************************************************************* -* This function finds all the connected components induced by the -* partitioning vector in wgraph->where and tries to push them around to -* remove some of them -**************************************************************************/ -void EliminateComponents(CtrlType *ctrl, GraphType *graph, idxtype nparts, float *tpwgts, float ubfactor) -{ - idxtype i, ii, j, jj, k, me, nvtxs, tvwgt, first, last, nleft, ncmps, cwgt, other, target, deltawgt; - idxtype *xadj, *adjncy, *vwgt, *adjwgt, *where, *pwgts, *maxpwgt; - idxtype *cpvec, *touched, *perm, *todo, *cind, *cptr, *npcmps; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - vwgt = graph->vwgt; - adjwgt = graph->adjwgt; - - where = graph->where; - pwgts = graph->pwgts; - - touched = idxset(nvtxs, 0, idxwspacemalloc(ctrl, nvtxs)); - cptr = idxwspacemalloc(ctrl, nvtxs+1); - cind = idxwspacemalloc(ctrl, nvtxs); - perm = idxwspacemalloc(ctrl, nvtxs); - todo = idxwspacemalloc(ctrl, nvtxs); - maxpwgt = idxwspacemalloc(ctrl, nparts); - cpvec = idxwspacemalloc(ctrl, nparts); - npcmps = idxset(nparts, 0, idxwspacemalloc(ctrl, nparts)); - - for (i=0; i 0) { - if (first == last) { /* Find another starting vertex */ - cptr[++ncmps] = first; - ASSERT(touched[todo[0]] == 0); - i = todo[0]; - cind[last++] = i; - touched[i] = 1; - me = where[i]; - npcmps[me]++; - } - - i = cind[first++]; - k = perm[i]; - j = todo[k] = todo[--nleft]; - perm[j] = k; - - for (j=xadj[i]; j nparts) { /* There are more components than processors */ - /* First determine the max allowed load imbalance */ - tvwgt = idxsum(nparts, pwgts, 1); - for (i=0; i .30*pwgts[me]) - continue; /* Skip the component if it is over 30% of the weight */ - - /* Determine the connectivity */ - idxset(nparts, 0, cpvec); - for (j=cptr[i]; j 0 && (cwgt < deltawgt || pwgts[j] + cwgt < maxpwgt[j])) { - if (target == -1 || cpvec[target] < cpvec[j]) - target = j; - } - } - - /* mprintf("\tMoving it to %D [%D]\n", target, cpvec[target]);*/ - - if (target != -1) { - /* Assign all the vertices of 'me' to 'target' and update data structures */ - INC_DEC(pwgts[target], pwgts[me], cwgt); - npcmps[me]--; - - MoveGroup(ctrl, graph, nparts, target, i, cptr, cind); - } - } - - } - - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nparts); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs); - idxwspacefree(ctrl, nvtxs+1); - -} - - -/************************************************************************* -* This function moves a collection of vertices and updates their rinfo -**************************************************************************/ -void MoveGroup(CtrlType *ctrl, GraphType *graph, idxtype nparts, idxtype to, idxtype gid, idxtype *ptr, idxtype *ind) -{ - idxtype i, ii, iii, j, jj, k, l, nvtxs, nbnd, myndegrees; - idxtype from, me; - idxtype *xadj, *adjncy, *adjwgt; - idxtype *where, *bndptr, *bndind; - EDegreeType *myedegrees; - RInfoType *myrinfo; - - nvtxs = graph->nvtxs; - xadj = graph->xadj; - adjncy = graph->adjncy; - adjwgt = graph->adjwgt; - - where = graph->where; - bndptr = graph->bndptr; - bndind = graph->bndind; - - nbnd = graph->nbnd; - - for (iii=ptr[gid]; iiirinfo+i; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[i+1]-xadj[i]; - myrinfo->ndegrees = 0; - } - myedegrees = myrinfo->edegrees; - - /* find the location of 'to' in myrinfo or create it if it is not there */ - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) - break; - } - if (k == myrinfo->ndegrees) { - myedegrees[k].pid = to; - myedegrees[k].ed = 0; - myrinfo->ndegrees++; - } - - graph->mincut -= myedegrees[k].ed-myrinfo->id; - - - /* Update where, weight, and ID/ED information of the vertex you moved */ - where[i] = to; - myrinfo->ed += myrinfo->id-myedegrees[k].ed; - SWAP(myrinfo->id, myedegrees[k].ed, j); - if (myedegrees[k].ed == 0) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].pid = from; - - if (myrinfo->ed-myrinfo->id < 0 && bndptr[i] != -1) - BNDDelete(nbnd, bndind, bndptr, i); - - /* Update the degrees of adjacent vertices */ - for (j=xadj[i]; jrinfo+ii; - if (myrinfo->edegrees == NULL) { - myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree; - ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii]; - } - myedegrees = myrinfo->edegrees; - - ASSERT(CheckRInfo(myrinfo)); - - if (me == from) { - INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1) - BNDInsert(nbnd, bndind, bndptr, ii); - } - else if (me == to) { - INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]); - - if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1) - BNDDelete(nbnd, bndind, bndptr, ii); - } - - /* Remove contribution from the .ed of 'from' */ - if (me != from) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == from) { - if (myedegrees[k].ed == adjwgt[j]) - myedegrees[k] = myedegrees[--myrinfo->ndegrees]; - else - myedegrees[k].ed -= adjwgt[j]; - break; - } - } - } - - /* Add contribution to the .ed of 'to' */ - if (me != to) { - for (k=0; kndegrees; k++) { - if (myedegrees[k].pid == to) { - myedegrees[k].ed += adjwgt[j]; - break; - } - } - if (k == myrinfo->ndegrees) { - myedegrees[myrinfo->ndegrees].pid = to; - myedegrees[myrinfo->ndegrees++].ed = adjwgt[j]; - } - } - - ASSERT(CheckRInfo(myrinfo)); - } - - ASSERT(CheckRInfo(graph->rinfo+i)); - } - - graph->nbnd = nbnd; - -} - diff --git a/src/metis/timing.c b/src/metis/timing.c deleted file mode 100644 index 2bf7ab4..0000000 --- a/src/metis/timing.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * timing.c - * - * This file contains routines that deal with timing Metis - * - * Started 7/24/97 - * George - * - * $Id: timing.c,v 1.2 2002/08/10 06:29:34 karypis Exp $ - * - */ - -#include - - -/************************************************************************* -* This function clears the timers -**************************************************************************/ -void InitTimers(CtrlType *ctrl) -{ - gk_clearcputimer(ctrl->TotalTmr); - gk_clearcputimer(ctrl->InitPartTmr); - gk_clearcputimer(ctrl->MatchTmr); - gk_clearcputimer(ctrl->ContractTmr); - gk_clearcputimer(ctrl->CoarsenTmr); - gk_clearcputimer(ctrl->UncoarsenTmr); - gk_clearcputimer(ctrl->RefTmr); - gk_clearcputimer(ctrl->ProjectTmr); - gk_clearcputimer(ctrl->SplitTmr); - gk_clearcputimer(ctrl->SepTmr); - gk_clearcputimer(ctrl->AuxTmr1); - gk_clearcputimer(ctrl->AuxTmr2); - gk_clearcputimer(ctrl->AuxTmr3); - gk_clearcputimer(ctrl->AuxTmr4); - gk_clearcputimer(ctrl->AuxTmr5); - gk_clearcputimer(ctrl->AuxTmr6); -} - - - -/************************************************************************* -* This function prints the various timers -**************************************************************************/ -void PrintTimers(CtrlType *ctrl) -{ - mprintf("\nTiming Information -------------------------------------------------"); - mprintf("\n Multilevel: \t\t %7.3f", gk_getcputimer(ctrl->TotalTmr)); - mprintf("\n Coarsening: \t\t %7.3f", gk_getcputimer(ctrl->CoarsenTmr)); - mprintf("\n Matching: \t\t\t %7.3f", gk_getcputimer(ctrl->MatchTmr)); - mprintf("\n Contract: \t\t\t %7.3f", gk_getcputimer(ctrl->ContractTmr)); - mprintf("\n Initial Partition: \t %7.3f", gk_getcputimer(ctrl->InitPartTmr)); - mprintf("\n Construct Separator: \t %7.3f", gk_getcputimer(ctrl->SepTmr)); - mprintf("\n Uncoarsening: \t\t %7.3f", gk_getcputimer(ctrl->UncoarsenTmr)); - mprintf("\n Refinement: \t\t\t %7.3f", gk_getcputimer(ctrl->RefTmr)); - mprintf("\n Projection: \t\t\t %7.3f", gk_getcputimer(ctrl->ProjectTmr)); - mprintf("\n Splitting: \t\t %7.3f", gk_getcputimer(ctrl->SplitTmr)); - mprintf("\n AUX1: \t\t %7.3f", gk_getcputimer(ctrl->AuxTmr1)); - mprintf("\n AUX2: \t\t %7.3f", gk_getcputimer(ctrl->AuxTmr2)); - mprintf("\n AUX3: \t\t %7.3f", gk_getcputimer(ctrl->AuxTmr3)); - mprintf("\n********************************************************************\n"); -} - - - diff --git a/src/metis/util.c b/src/metis/util.c deleted file mode 100644 index c16db72..0000000 --- a/src/metis/util.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 1997, Regents of the University of Minnesota - * - * util.c - * - * This function contains various utility routines - * - * Started 9/28/95 - * George - * - * $Id: util.c,v 1.4 2003/04/13 04:45:12 karypis Exp $ - */ - -#include - - - - -/************************************************************************* -* The following are utility functions defined for idxtype using GKlib's -* function generating macros -**************************************************************************/ -GK_XMALLOC(idxmalloc, idxtype) -GK_XREALLOC(idxrealloc, idxtype) -GK_XSMALLOC(idxsmalloc, idxtype, idxset) -GK_SET(idxset, idxtype) -GK_ARGMAX(idxargmax, idxtype) -GK_ARGMIN(idxargmin, idxtype) -GK_SUM(idxsum, idxtype, idxtype) -GK_AXPY(idxaxpy, idxtype) - - - - -/************************************************************************* -* These functions return the index of the maximum element in a vector -**************************************************************************/ -idxtype idxargmax_strd(size_t n, idxtype *x, idxtype incx) -{ - size_t i, max=0; - - n *= incx; - for (i=incx; i x[max] ? i : max); - - return max/incx; -} - - - - -/************************************************************************* -* These functions return the index of the almost maximum element in a vector -**************************************************************************/ -idxtype famax2(size_t n, float *x) -{ - size_t i, max1, max2; - - if (x[0] > x[1]) { - max1 = 0; - max2 = 1; - } - else { - max1 = 1; - max2 = 0; - } - - for (i=2; i x[max1]) { - max2 = max1; - max1 = i; - } - else if (x[i] > x[max2]) - max2 = i; - } - - return max2; -} - - - - - - - -/************************************************************************* -* This file randomly permutes the contents of an array. -* flag == 0, don't initialize perm -* flag == 1, set p[i] = i -**************************************************************************/ -void RandomPermute(size_t n, idxtype *p, idxtype flag) -{ - size_t i, u, v; - idxtype tmp; - - if (flag == 1) { - for (i=0; i