Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configurable seed for partitioning #224

Draft
wants to merge 7 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog-entries/214.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added configurable partitioning algorithm to mapping tester.
davidscn marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions changelog-entries/224.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Changed partitioning algorihm to use a fixed seed which can be passed via the `--seed` flag. This makes partitioning deterministic.
1 change: 1 addition & 0 deletions examples/mapping_tester/setup-test.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"general": {
"function": "0.78 + cos(10*(x+y+z))",
"partitioning": "topology",
"ranks": {
"A": [
2
Expand Down
7 changes: 3 additions & 4 deletions src/metisAPI.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
#include <iostream>
#include <metis.h>
#include <vector>
extern "C" void partitionMetis(idx_t cell_count, idx_t point_count, idx_t *cellptr, idx_t *celldata, idx_t nparts, idx_t *point_partition);
extern "C" void partitionMetis(idx_t cell_count, idx_t point_count, idx_t *cellptr, idx_t *celldata, idx_t nparts, idx_t seed, idx_t *point_partition);
extern "C" int typewidth();

void partitionMetis(idx_t cell_count, idx_t point_count, idx_t *cellptr, idx_t *celldata, idx_t nparts, idx_t *point_partition)
void partitionMetis(idx_t cell_count, idx_t point_count, idx_t *cellptr, idx_t *celldata, idx_t nparts, idx_t seed, idx_t *point_partition)
{
idx_t options[METIS_NOPTIONS];
METIS_SetDefaultOptions(options);
// Make it deterministic
// TODO: Pass via interface
options[METIS_OPTION_SEED] = 2025;
options[METIS_OPTION_SEED] = seed;
std::vector<idx_t> cell_partition(cell_count);
idx_t objval;
// TODO: Check return value of the function (and potentially add an assert)
Expand Down
41 changes: 28 additions & 13 deletions src/precice-aste-partition
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,11 @@ class MeshPartitioner:
def run(args) -> None:
logger = MeshPartitioner.get_logger()
mesh_name = args.in_meshname
algorithm = args.algorithm
if not algorithm:
logger.info('No algorithm given. Defaulting to "meshfree"')
algorithm = "meshfree"
mesh = MeshPartitioner.read_mesh(mesh_name)
if args.numparts > 1:
part = MeshPartitioner.partition(mesh, args.numparts, algorithm)
part = MeshPartitioner.partition(
mesh, args.numparts, args.algorithm, args.seed
)
else:
if args.directory:
# Get the absolute directory where we want to store the mesh
Expand Down Expand Up @@ -160,12 +158,22 @@ class MeshPartitioner:
"-a",
dest="algorithm",
choices=["meshfree", "topology", "uniform"],
default="meshfree",
help="""Change the algorithm used for determining a partition.
A meshfree algorithm works on arbitrary meshes without needing topological information.
A topology-based algorithm needs topology information
and is therefore useless on point clouds.
A uniform algorithm will assume a uniform 2d mesh laid out somehow in 3d and partition accordingly.""",
)
parser.add_argument(
"--seed",
"-s",
type=int,
default=2025, # arbitrary default seed
help="""Seed to pass on to partitioning algorithms which may use it to end up with deterministic behaviour.
Not all algorithms may use it though.
""",
)
parser.add_argument(
"--log",
"-l",
Expand All @@ -192,30 +200,36 @@ class MeshPartitioner:
return logging.getLogger("---[ASTE-Partition]")

@staticmethod
def partition(mesh: Mesh, numparts: int, algorithm):
def partition(mesh: Mesh, numparts: int, algorithm: str, seed: int):
"""
Partitions a mesh using METIS or kmeans. This does not call METIS directly,
but instead uses a small C++ Wrapper around shared library libmetisAPI for convenience.
This shared library must be provided if this function should be called.
"""
if algorithm == "meshfree":
return MeshPartitioner.partition_kmeans(mesh, numparts)
return MeshPartitioner.partition_kmeans(mesh, numparts, seed)
elif algorithm == "topology":
return MeshPartitioner.partition_metis(mesh, numparts)
return MeshPartitioner.partition_metis(mesh, numparts, seed)
elif algorithm == "uniform":
labels = MeshPartitioner.partition_uniform(mesh, numparts)
if labels is None:
return MeshPartitioner.partition(mesh, numparts, "meshfree")
return MeshPartitioner.partition(mesh, numparts, "meshfree", seed)
return labels

@staticmethod
def partition_kmeans(mesh: Mesh, numparts: int):
def partition_kmeans(mesh: Mesh, numparts: int, seed: int):
"""Partitions a mesh using k-means. This is a meshfree algorithm and requires scipy"""
import scipy
from scipy.cluster.vq import kmeans2

points = np.copy(mesh.points)
points = MeshPartitioner.reduce_dimension(points)
_, label = kmeans2(points, numparts)
# scipy 1.15 renamed seed to rng
major, minor = map(int, scipy.__version__.split(".")[:2])
if major == 1 and minor < 15:
_, label = kmeans2(points, numparts, rng=seed)
else:
_, label = kmeans2(points, numparts, seed=seed)
return label

@staticmethod
Expand Down Expand Up @@ -296,7 +310,7 @@ class MeshPartitioner:
return mesh[:, :-1]

@staticmethod
def partition_metis(mesh: Mesh, numparts: int):
def partition_metis(mesh: Mesh, numparts: int, seed: int):
"""
Partitions a mesh using METIS. This does not call METIS directly,
but instead uses a small C++ Wrapper libmetisAPI.so for convenience.
Expand Down Expand Up @@ -339,8 +353,9 @@ class MeshPartitioner:
partition = (idx_t * len(mesh.points))()
cell_ptr = (idx_t * len(cellPtr))(*cellPtr)
cell_data = (idx_t * len(cellData))(*cellData)
the_seed = idx_t(seed)
libmetis.partitionMetis(
cell_count, point_count, cell_ptr, cell_data, num_parts, partition
cell_count, point_count, cell_ptr, cell_data, num_parts, seed, partition
)
return np.ctypeslib.as_array(partition)

Expand Down
14 changes: 11 additions & 3 deletions tools/mapping-tester/preparemeshes.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def prepareMainMesh(meshdir, name, file, function, force=False):
)


def preparePartMesh(meshdir, name, p, force=False):
def preparePartMesh(meshdir, name, p, force, algorithm):

if p == 1:
return
Expand All @@ -91,7 +91,7 @@ def preparePartMesh(meshdir, name, p, force=False):
"--mesh",
mainMesh,
"--algorithm",
"topology",
algorithm,
"-o",
partMesh,
"--directory",
Expand All @@ -111,6 +111,14 @@ def main(argv):
print('Warning: outdir "{}" already exisits.'.format(outdir))
meshdir = os.path.join(outdir, "meshes")
function = setup["general"]["function"]
algorithm = setup["general"].get("partitioning", "meshfree")

if "partitioning" in setup["general"]:
print(f"Using partitioning algorithm {algorithm}")
else:
print(
"You haven't specified a partitioning algorihm in the general section of the setup.json. This defaults to meshfree, which uses a non-deterministic k-means clusterting. Partitionings will be non-reproducible."
)

partitions = set(
[int(rank) for pranks in setup["general"]["ranks"].values() for rank in pranks]
Expand All @@ -128,7 +136,7 @@ def main(argv):
prepareMainMesh(meshdir, name, file, function, args.force)

for p in partitions:
preparePartMesh(meshdir, name, p, args.force)
preparePartMesh(meshdir, name, p, args.force, algorithm)

return 0

Expand Down
Loading