@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
127 lines (99 loc) • 3.76 kB
JavaScript
import { Metis } from "./metis.js";
import { metis_options } from "./metis_options.js";
/**
* How many times to retry partitioning before giving up
* @type {number}
*/
const MAX_STEPS = 50;
/**
*
* @param {Uint32Array} result
* @param {number} node_count
* @param {Uint32Array} edge_addresses
* @param {Uint32Array} adjacency_uint32
* @param {Uint32Array} edge_weights_uint32
* @param {number} patch_size
* @returns {Promise<number>}
*/
export async function metis_cluster_bs(
result,
node_count,
edge_addresses,
adjacency_uint32,
edge_weights_uint32,
patch_size
) {
const metis = Metis.INSTANCE;
let partition_count_bound_lower = Math.ceil(node_count / patch_size);
let partition_count_bound_upper = partition_count_bound_lower;
let partition_count = partition_count_bound_lower;
let counts = new Uint32Array(partition_count);
let passes = 0;
let p;
let max_count;
const metisOptions = new metis_options();
metisOptions.seed = 0xCAFE;
for (; ;) {
passes++;
if (passes > MAX_STEPS) {
throw new Error(`Maximum number of passes exceeded (${MAX_STEPS}). Current cluster count= ${partition_count}, initial=${partition_count_bound_lower}. Max elements per cluster = ${max_count}`);
}
p = await metis.partition(
node_count,
partition_count,
edge_addresses,
adjacency_uint32,
edge_weights_uint32,
metisOptions
);
max_count = 0;
for (let i = 0; i < node_count; i++) {
const part = p[i];
counts[part]++;
if (counts[part] > max_count) {
max_count = counts[part];
}
}
const overflow = max_count - patch_size;
// perform binary search for the optimal partition count
if (overflow > 0) {
// update lower bound
partition_count_bound_lower = partition_count + 1;
// part overflow
if (partition_count === partition_count_bound_upper) {
partition_count_bound_upper = Math.ceil(partition_count * 1.04);
partition_count = partition_count_bound_upper;
} else {
// try half-way mark
partition_count = Math.ceil((partition_count_bound_lower + partition_count_bound_upper) / 2);
}
} else if (partition_count > partition_count_bound_lower && (partition_count_bound_upper - partition_count_bound_lower) > 1) {
// no overflow, but we're probably overshooting
partition_count_bound_upper = partition_count;
partition_count = Math.ceil((partition_count_bound_lower + partition_count_bound_upper) / 2);
} else {
// done
break;
}
if (partition_count > counts.length) {
counts = new Uint32Array(partition_count)
} else {
counts.fill(0);
}
// console.log(`${partition_count}`);
}
// remap P values. Metis assigns unique unsigned integer values to each cluster, but they don't necessarily start at 0, to fix this we remap values
const mapping = [];
let index_counter = 0;
for (let i = 0; i < p.length; i++) {
const source_index = p[i];
let target_index = mapping[source_index];
if (target_index === undefined) {
target_index = index_counter++;
mapping[source_index] = target_index;
}
p[i] = target_index;
}
result.set(p);
return partition_count;
}