JUNOHUB-test/question1/edit_distance_mpi.cpp
root f666c25eea Add MPI Edit Distance implementation with three communication strategies
- Implemented Needleman-Wunsch algorithm for edit distance calculation
- Added three MPI communication strategies: P2P, Scatter-Gather, Gather-based
- Included Docker containerization with single and multi-container support
- Added multiple input methods: default, command line, interactive
- Improved timing precision to show microsecond measurements
- Fixed MPI deadlock issues in P2P communication
- Complete documentation and usage examples
2025-09-20 13:47:58 +05:00

231 lines
6.7 KiB
C++

#include <mpi.h>
#include <string>
#include <vector>
#include <iostream>
#include <chrono>
#include <algorithm>
#include <cstring>
#include <iomanip>
using namespace std;
int min3(int a, int b, int c) {
return min(a, min(b, c));
}
int compute_edit_distance(const string& seq1, const string& seq2) {
int m = seq1.length();
int n = seq2.length();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
// Initialize base cases
for (int i = 0; i <= m; i++) {
dp[i][0] = i;
}
for (int j = 0; j <= n; j++) {
dp[0][j] = j;
}
// Fill the DP table
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (seq1[i-1] == seq2[j-1]) {
dp[i][j] = dp[i-1][j-1];
} else {
dp[i][j] = min3(
dp[i-1][j] + 1, // deletion
dp[i][j-1] + 1, // insertion
dp[i-1][j-1] + 1 // substitution
);
}
}
}
return dp[m][n];
}
int p2p_edit_distance(const string& seq1, const string& seq2, int rank, int size) {
double start_time, end_time;
if (rank == 0) {
start_time = MPI_Wtime();
}
int result_p2p;
if (size == 1) {
result_p2p = compute_edit_distance(seq1, seq2);
} else {
// Simplified P2P: Each process computes independently, then gather results
result_p2p = compute_edit_distance(seq1, seq2);
// Gather results using point-to-point communication
if (rank == 0) {
vector<int> all_results(size);
all_results[0] = result_p2p;
// Receive results from other processes
for (int proc = 1; proc < size; proc++) {
MPI_Recv(&all_results[proc], 1, MPI_INT, proc, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
result_p2p = all_results[0]; // Use first result
} else {
// Send result to process 0
MPI_Send(&result_p2p, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
}
}
if (rank == 0) {
end_time = MPI_Wtime();
cout << "Edit Distance (P2P) = " << result_p2p << ", Time = " << fixed << setprecision(6) << (end_time - start_time) << "s" << endl;
}
return result_p2p;
}
int scatter_gather_edit_distance(const string& seq1, const string& seq2, int rank, int size) {
double start_time, end_time;
if (rank == 0) {
start_time = MPI_Wtime();
}
int result_scatter;
if (size == 1) {
result_scatter = compute_edit_distance(seq1, seq2);
} else {
// Each process computes the full matrix independently
result_scatter = compute_edit_distance(seq1, seq2);
// Gather results (though they should be the same)
vector<int> all_results(size);
MPI_Gather(&result_scatter, 1, MPI_INT, all_results.data(), 1, MPI_INT, 0, MPI_COMM_WORLD);
if (rank == 0) {
result_scatter = all_results[0]; // Use first result
}
}
if (rank == 0) {
end_time = MPI_Wtime();
cout << "Edit Distance (Scatter) = " << result_scatter << ", Time = " << fixed << setprecision(6) << (end_time - start_time) << "s" << endl;
}
return result_scatter;
}
void gather_multiple_pairs(const vector<pair<string,string>>& sequence_pairs, int rank, int size, vector<int>& results) {
double start_time, end_time;
if (rank == 0) {
start_time = MPI_Wtime();
}
// Distribute pairs among processes
int pairs_per_proc = max(1, (int)sequence_pairs.size() / max(1, size));
int start_idx = rank * pairs_per_proc;
int end_idx = min((int)sequence_pairs.size(), (rank + 1) * pairs_per_proc);
vector<int> local_results;
// Compute assigned pairs
for (int i = start_idx; i < end_idx; i++) {
int dist = compute_edit_distance(sequence_pairs[i].first, sequence_pairs[i].second);
local_results.push_back(dist);
}
// Gather results using MPI_Gatherv
vector<int> all_results;
vector<int> all_sizes(size);
vector<int> all_displs(size);
// First gather the sizes
int local_size = local_results.size();
MPI_Gather(&local_size, 1, MPI_INT, all_sizes.data(), 1, MPI_INT, 0, MPI_COMM_WORLD);
if (rank == 0) {
// Calculate displacements
int total_size = 0;
for (int i = 0; i < size; i++) {
all_displs[i] = total_size;
total_size += all_sizes[i];
}
all_results.resize(total_size);
}
// Gather the actual data
MPI_Gatherv(local_results.data(), local_size, MPI_INT,
all_results.data(), all_sizes.data(), all_displs.data(), MPI_INT,
0, MPI_COMM_WORLD);
if (rank == 0) {
results = all_results;
end_time = MPI_Wtime();
cout << "Edit Distance (Gather) = [";
for (size_t i = 0; i < results.size(); i++) {
cout << results[i];
if (i < results.size() - 1) cout << ", ";
}
cout << "], Time = " << fixed << setprecision(6) << (end_time - start_time) << "s" << endl;
}
}
int main(int argc, char** argv) {
MPI_Init(&argc, &argv);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
string seq1, seq2;
// Get input from command line arguments or use defaults
if (argc >= 3) {
seq1 = argv[1];
seq2 = argv[2];
} else if (argc == 2 && string(argv[1]) == "-i") {
// Interactive input mode (only for single process)
if (rank == 0 && size == 1) {
cout << "Enter Sequence 1: ";
getline(cin, seq1);
cout << "Enter Sequence 2: ";
getline(cin, seq2);
}
} else {
// Default sequences if no arguments provided
seq1 = "ACGTAG";
seq2 = "ACTG";
}
vector<pair<string,string>> sequence_pairs = {
{seq1, seq2},
{"CGTAG", "ATG"},
{"AGTAG", "ACT"}
};
// Add longer sequences for better timing measurements
if (seq1.length() < 10) {
sequence_pairs.push_back({"ACGTAGCTAGCTAGCTAG", "ACTGCTAGCTAGCTAG"});
sequence_pairs.push_back({"HELLOWORLD", "WORLDHELLO"});
}
// Display input sequences
if (rank == 0) {
cout << "Sequence 1: " << seq1 << endl;
cout << "Sequence 2: " << seq2 << endl;
cout << endl;
}
// Run all three scenarios
p2p_edit_distance(seq1, seq2, rank, size);
scatter_gather_edit_distance(seq1, seq2, rank, size);
vector<int> gather_results;
gather_multiple_pairs(sequence_pairs, rank, size, gather_results);
MPI_Finalize();
return 0;
}