Learn how to build production-ready social network features using modern C++ and graph theory
Introduction
Open any social media app today and youβll see sophisticated features powered by graph algorithms: βPeople You May Knowβ suggestions that are eerily accurate, influencer rankings that determine who gets verified, community recommendations that connect like-minded users, and viral content predictions that drive engagement.
Behind these features lies a common foundation: social graph analysis. Your users, their connections, and their interactions form a massive graph structure, and understanding this graph is the key to building compelling social features.
But hereβs the challenge: traditional social network analysis tools are either heavyweight frameworks with complexβ¦
Learn how to build production-ready social network features using modern C++ and graph theory
Introduction
Open any social media app today and youβll see sophisticated features powered by graph algorithms: βPeople You May Knowβ suggestions that are eerily accurate, influencer rankings that determine who gets verified, community recommendations that connect like-minded users, and viral content predictions that drive engagement.
Behind these features lies a common foundation: social graph analysis. Your users, their connections, and their interactions form a massive graph structure, and understanding this graph is the key to building compelling social features.
But hereβs the challenge: traditional social network analysis tools are either heavyweight frameworks with complex dependencies (hello, Boost), high-level languages that canβt handle backend scale (Pythonβs NetworkX), or specialized platforms that lock you into their ecosystem.
What if you could build production-ready social network analytics directly in C++, with a simple header-only library, and deploy it in your backend services today?
Enter CXXGraph - a modern C++ graph library that brings the power of social network analysis to your applications without the complexity.
In this comprehensive guide, weβll build a complete social network analysis system from scratch, covering everything from friend recommendations to viral content prediction. By the end, youβll have working code you can adapt to your own social platform.
Why CXXGraph for Social Network Analysis?
Before diving into code, letβs understand why CXXGraph is perfect for social network backends:
π Backend Performance
- Native C++ speed handles millions of users
- Zero-copy operations for edge traversal
- Efficient memory usage for large graphs
- No GC pauses during critical operations
π§ Production Ready
- Header-only: no compilation or linking hassles
- No external dependencies to manage
- Modern C++17 syntax for maintainable code
- Battle-tested algorithms (30+ graph operations)
π Rich Algorithm Library
- Community detection (find user clusters)
- Centrality metrics (identify influencers)
- Shortest paths (connection suggestions)
- Component analysis (network health)
πΌ Enterprise Friendly
- Integrates with existing C++ backends
- Works alongside databases and caches
- Easy to wrap in REST APIs
- Scales horizontally
Simply include one header:
#include "CXXGraph/CXXGraph.hpp"
No build system configuration, no package managers, no hassle.
What Weβre Building: A Complete Social Analytics Platform
Weβll create a production-grade social network analysis system with these features:
- β User Relationship Modeling - Friends, followers, connections
- β Friend Recommendations - βPeople You May Knowβ engine
- β Influence Detection - Identify key influencers and opinion leaders
- β Community Discovery - Find natural user clusters and groups
- β Virality Prediction - Estimate content spread and reach
- β Network Health Monitoring - Track engagement and connectivity
Hereβs what our analytics dashboard will output:
βββββββββββββββββββββββββββββββββββββββββββββββββ
β SOCIAL NETWORK ANALYTICS DASHBOARD β
βββββββββββββββββββββββββββββββββββββββββββββββββ
π€ USER PROFILE
Name: Alice Johnson
Connections: 156
Reach: 2,847 users
π€ FRIEND RECOMMENDATIONS
1. Bob Smith (12 mutual friends) - Score: 87.5
2. Carol Davis (8 mutual friends) - Score: 76.2
3. David Wilson (6 mutual friends) - Score: 64.8
β INFLUENCE METRICS
Your influence rank: Top 5%
Content reach: 3,241 users
Engagement score: 8.7/10
π₯ COMMUNITIES
Member of 3 communities:
- Tech Enthusiasts (234 members, 89% cohesion)
- Local Events (156 members, 76% cohesion)
- Book Club (45 members, 94% cohesion)
π VIRAL POTENTIAL
Latest post expected reach: 2,100 users
Spread velocity: 45 shares/hour
Virality score: 73/100
Letβs build it step by step.
Step 1: Data Modeling - Representing Users and Relationships
First, we need to model our social network entities:
#include "CXXGraph/CXXGraph.hpp"
#include <string>
#include <unordered_map>
#include <vector>
#include <memory>
#include <algorithm>
#include <cmath>
// User profile data
struct User {
std::string id;
std::string name;
std::string email;
std::string bio;
int followerCount;
int followingCount;
long long joinedTimestamp;
User(const std::string& uid, const std::string& uname)
: id(uid), name(uname), followerCount(0), followingCount(0),
joinedTimestamp(0) {}
};
// Different types of social connections
enum class RelationType {
FRIEND, // Bidirectional (Facebook-style)
FOLLOW, // Unidirectional (Twitter-style)
COLLEAGUE, // Professional connection (LinkedIn-style)
FAMILY, // Strong tie
BLOCKED // Negative relationship
};
// Relationship metadata
struct Relationship {
RelationType type;
int strength; // 1-10, based on interaction frequency
long long createdAt; // Timestamp when connection was made
int messageCount; // Number of direct messages
int sharedPosts; // Number of shared/retweeted posts
Relationship()
: type(RelationType::FRIEND), strength(5),
createdAt(0), messageCount(0), sharedPosts(0) {}
};
// Calculate relationship strength from interaction data
int calculateStrength(const Relationship& rel) {
double score = 0.0;
// Factor in different types of interactions
score += rel.messageCount * 0.5; // Messages are strong signals
score += rel.sharedPosts * 0.3; // Sharing shows engagement
// Time decay - older connections might be weaker
long long daysSinceCreation =
(std::time(nullptr) - rel.createdAt) / (24 * 3600);
double decayFactor = std::max(0.5, 1.0 - (daysSinceCreation / 365.0) * 0.1);
score *= decayFactor;
// Clamp to 1-10 range
return std::max(1, std::min(10, static_cast<int>(score)));
}
Step 2: Building the Social Graph
Now letβs create our core social network class:
class SocialNetwork {
private:
std::unordered_map<std::string, User> users_;
CXXGraph::Graph<std::string> graph_;
std::unordered_map<std::string, Relationship> relationships_;
CXXGraph::T_EdgeSet<std::string> edgeSet_;
bool graphDirty_ = false; // Track if graph needs rebuilding
public:
// Add a new user to the network
void addUser(const User& user) {
users_[user.id] = user;
std::cout << "β Added user: " << user.name << " (" << user.id << ")\n";
}
// Create a friend connection (bidirectional)
void addFriendship(const std::string& user1, const std::string& user2,
int interactionScore = 5) {
if (!validateUsers(user1, user2)) return;
CXXGraph::Node<std::string> node1(user1, user1);
CXXGraph::Node<std::string> node2(user2, user2);
// Create weighted undirected edge
auto edge = std::make_shared<CXXGraph::UndirectedWeightedEdge<std::string>>(
makeEdgeId(user1, user2),
node1, node2,
static_cast<double>(interactionScore)
);
edgeSet_.insert(edge);
// Store relationship metadata
Relationship rel;
rel.type = RelationType::FRIEND;
rel.strength = interactionScore;
rel.createdAt = std::time(nullptr);
relationships_[makeEdgeId(user1, user2)] = rel;
graphDirty_ = true;
}
// Create a follow relationship (unidirectional)
void addFollowRelation(const std::string& follower,
const std::string& following) {
if (!validateUsers(follower, following)) return;
CXXGraph::Node<std::string> followerNode(follower, follower);
CXXGraph::Node<std::string> followingNode(following, following);
// Directed edge for asymmetric relationship
auto edge = std::make_shared<CXXGraph::DirectedWeightedEdge<std::string>>(
follower + "->" + following,
followerNode, followingNode,
1.0
);
edgeSet_.insert(edge);
users_[following].followerCount++;
users_[follower].followingCount++;
Relationship rel;
rel.type = RelationType::FOLLOW;
rel.createdAt = std::time(nullptr);
relationships_[follower + "->" + following] = rel;
graphDirty_ = true;
}
// Update relationship strength based on new interactions
void recordInteraction(const std::string& user1, const std::string& user2,
bool isMessage = false, bool isShare = false) {
std::string edgeId = makeEdgeId(user1, user2);
if (relationships_.find(edgeId) != relationships_.end()) {
if (isMessage) relationships_[edgeId].messageCount++;
if (isShare) relationships_[edgeId].sharedPosts++;
// Recalculate strength
int newStrength = calculateStrength(relationships_[edgeId]);
relationships_[edgeId].strength = newStrength;
// Update edge weight in graph
updateEdgeWeight(edgeId, newStrength);
}
}
// Getters
const CXXGraph::Graph<std::string>& getGraph() {
if (graphDirty_) {
rebuildGraph();
}
return graph_;
}
const User& getUser(const std::string& userId) const {
return users_.at(userId);
}
std::vector<std::string> getAllUserIds() const {
std::vector<std::string> ids;
ids.reserve(users_.size());
for (const auto& [id, user] : users_) {
ids.push_back(id);
}
return ids;
}
size_t getUserCount() const { return users_.size(); }
size_t getConnectionCount() const { return edgeSet_.size(); }
private:
bool validateUsers(const std::string& user1, const std::string& user2) {
if (users_.find(user1) == users_.end()) {
std::cerr << "β User not found: " << user1 << "\n";
return false;
}
if (users_.find(user2) == users_.end()) {
std::cerr << "β User not found: " << user2 << "\n";
return false;
}
return true;
}
std::string makeEdgeId(const std::string& user1, const std::string& user2) {
// Ensure consistent edge ID regardless of order (for undirected)
if (user1 < user2) {
return user1 + "<->" + user2;
}
return user2 + "<->" + user1;
}
void rebuildGraph() {
graph_ = CXXGraph::Graph<std::string>(edgeSet_);
graphDirty_ = false;
}
void updateEdgeWeight(const std::string& edgeId, int newWeight) {
// Remove old edge and add updated one
// (Simplified - in production, you'd have more efficient updates)
graphDirty_ = true;
}
};
Design Notes:
- We separate graph structure (CXXGraph) from business logic (metadata)
- Relationship strength is dynamically calculated from interactions
- Graph is lazy-rebuilt only when needed (performance optimization)
- Both directed and undirected relationships are supported
Step 3: Friend Recommendations - βPeople You May Knowβ
This is the feature everyone recognizes. Letβs implement it properly:
class FriendRecommender {
public:
struct Recommendation {
std::string userId;
std::string userName;
int mutualFriends;
int commonInterests; // Could be based on tags, groups, etc.
double socialDistance; // Graph distance
double score;
std::string getReason() const {
if (mutualFriends > 10) {
return "Many mutual connections";
} else if (mutualFriends > 5) {
return std::to_string(mutualFriends) + " mutual friends";
} else if (commonInterests > 3) {
return "Similar interests";
} else {
return "You might know this person";
}
}
};
static std::vector<Recommendation> recommendFriends(
const SocialNetwork& network,
const std::string& userId,
int maxRecommendations = 10) {
const auto& graph = network.getGraph();
CXXGraph::Node<std::string> userNode(userId, userId);
// Step 1: Get user's direct connections
auto directFriends = getDirectFriends(graph, userId);
// Step 2: Find friends-of-friends
std::unordered_map<std::string, int> mutualFriendCount;
std::unordered_map<std::string, double> distances;
for (const auto& friendId : directFriends) {
auto friendsOfFriend = getDirectFriends(graph, friendId);
for (const auto& candidateId : friendsOfFriend) {
// Skip if it's the user or already a friend
if (candidateId == userId ||
isDirectFriend(directFriends, candidateId)) {
continue;
}
mutualFriendCount[candidateId]++;
// Calculate graph distance (simplified - using BFS depth)
if (distances.find(candidateId) == distances.end()) {
distances[candidateId] = 2.0; // Friends-of-friends are distance 2
}
}
}
// Step 3: Calculate comprehensive scores
std::vector<Recommendation> recommendations;
for (const auto& [candidateId, mutualCount] : mutualFriendCount) {
Recommendation rec;
rec.userId = candidateId;
try {
rec.userName = network.getUser(candidateId).name;
} catch (...) {
continue; // Skip if user data unavailable
}
rec.mutualFriends = mutualCount;
rec.socialDistance = distances[candidateId];
rec.commonInterests = 0; // Would be calculated from user data
// Scoring algorithm
rec.score = calculateRecommendationScore(
network, userId, candidateId, rec);
recommendations.push_back(rec);
}
// Step 4: Sort by score and return top N
std::sort(recommendations.begin(), recommendations.end(),
[](const Recommendation& a, const Recommendation& b) {
return a.score > b.score;
});
if (recommendations.size() > static_cast<size_t>(maxRecommendations)) {
recommendations.resize(maxRecommendations);
}
return recommendations;
}
// Advanced: Recommend based on network position
static std::vector<Recommendation> recommendBridgeConnections(
const SocialNetwork& network,
const std::string& userId) {
// Find users who would connect disparate parts of the user's network
// These are "bridge" connections that increase network diversity
const auto& graph = network.getGraph();
auto friends = getDirectFriends(graph, userId);
// Find communities among friends
std::vector<std::vector<std::string>> friendCommunities =
identifyFriendClusters(graph, friends);
std::vector<Recommendation> bridges;
// For each community, find connectors to other communities
for (size_t i = 0; i < friendCommunities.size(); ++i) {
for (size_t j = i + 1; j < friendCommunities.size(); ++j) {
auto bridgeCandidates = findBridgeUsers(
graph,
friendCommunities[i],
friendCommunities[j]
);
for (const auto& candidateId : bridgeCandidates) {
if (candidateId != userId &&
!isDirectFriend(friends, candidateId)) {
Recommendation rec;
rec.userId = candidateId;
try {
rec.userName = network.getUser(candidateId).name;
rec.score = 80.0; // Bridge connections are valuable
bridges.push_back(rec);
} catch (...) {}
}
}
}
}
return bridges;
}
private:
static std::vector<std::string> getDirectFriends(
const CXXGraph::Graph<std::string>& graph,
const std::string& userId) {
std::vector<std::string> friends;
CXXGraph::Node<std::string> userNode(userId, userId);
auto bfsResult = graph.breadth_first_search(userNode);
for (const auto& nodeId : bfsResult) {
if (nodeId != userId) {
friends.push_back(nodeId);
}
}
return friends;
}
static bool isDirectFriend(const std::vector<std::string>& friends,
const std::string& userId) {
return std::find(friends.begin(), friends.end(), userId) != friends.end();
}
static double calculateRecommendationScore(
const SocialNetwork& network,
const std::string& userId,
const std::string& candidateId,
const Recommendation& rec) {
double score = 0.0;
// Factor 1: Mutual friends (strongest signal)
score += rec.mutualFriends * 10.0;
// Factor 2: Graph distance (closer is better)
score += std::max(0.0, 20.0 - rec.socialDistance * 5.0);
// Factor 3: Similar network size (people prefer similar-sized networks)
try {
const auto& user = network.getUser(userId);
const auto& candidate = network.getUser(candidateId);
int followerDiff = std::abs(
user.followerCount - candidate.followerCount);
double similarityBonus = std::max(0.0, 10.0 - followerDiff / 100.0);
score += similarityBonus;
} catch (...) {}
// Factor 4: Common interests (if available)
score += rec.commonInterests * 5.0;
// Factor 5: Recency bonus (newer users might need more connections)
try {
const auto& candidate = network.getUser(candidateId);
long long accountAge = std::time(nullptr) - candidate.joinedTimestamp;
if (accountAge < 30 * 24 * 3600) { // Less than 30 days old
score += 5.0; // Help new users connect
}
} catch (...) {}
return score;
}
static std::vector<std::vector<std::string>> identifyFriendClusters(
const CXXGraph::Graph<std::string>& graph,
const std::vector<std::string>& friends) {
// Simplified clustering - in production, use proper community detection
std::vector<std::vector<std::string>> clusters;
// Implementation would use graph.connectedComponents() on subgraph
return clusters;
}
static std::vector<std::string> findBridgeUsers(
const CXXGraph::Graph<std::string>& graph,
const std::vector<std::string>& cluster1,
const std::vector<std::string>& cluster2) {
std::vector<std::string> bridges;
// Find users connected to both clusters
return bridges;
}
};
Algorithm Breakdown:
- Mutual Friends: Primary signal - people with many mutual connections
- Social Distance: Prefer closer connections in the graph
- Network Similarity: People with similar-sized networks often connect
- Bridge Detection: Advanced feature to diversify user networks
Step 4: Influence Detection - Finding Key Players
Every network has influencers. Letβs identify them scientifically:
class InfluenceAnalyzer {
public:
struct InfluenceMetrics {
std::string userId;
std::string userName;
// Basic metrics
int directConnections;
int totalReach; // Total users reachable
// Advanced metrics
double centralityScore; // Betweenness centrality
double pageRank; // Google's algorithm adapted for social
int bridgingScore; // Connects different communities
// Composite score
double overallInfluence;
std::string getInfluenceLevel() const {
if (overallInfluence > 90) return "Top Influencer";
if (overallInfluence > 75) return "Key Opinion Leader";
if (overallInfluence > 50) return "Active Connector";
if (overallInfluence > 25) return "Engaged User";
return "Casual User";
}
};
static std::vector<InfluenceMetrics> analyzeInfluence(
const SocialNetwork& network,
int topN = 20) {
const auto& graph = network.getGraph();
auto allUsers = network.getAllUserIds();
std::vector<InfluenceMetrics> allMetrics;
allMetrics.reserve(allUsers.size());
std::cout << "Analyzing influence for " << allUsers.size() << " users...\n";
for (const auto& userId : allUsers) {
InfluenceMetrics metrics;
metrics.userId = userId;
try {
metrics.userName = network.getUser(userId).name;
} catch (...) {
continue;
}
// Calculate metrics
CXXGraph::Node<std::string> userNode(userId, userId);
// Direct connections (immediate reach)
auto neighbors = graph.breadth_first_search(userNode);
metrics.directConnections = neighbors.size() - 1; // Exclude self
// Total reach (all users accessible)
metrics.totalReach = neighbors.size();
// Centrality (how often user is on shortest paths between others)
metrics.centralityScore = calculateBetweennessCentrality(
graph, userId, allUsers);
// PageRank (iterative importance calculation)
metrics.pageRank = calculateSimplifiedPageRank(
graph, userId, allUsers);
// Bridging score (connects different communities)
metrics.bridgingScore = calculateBridgingScore(
graph, userId);
// Overall influence (weighted combination)
metrics.overallInfluence =
metrics.directConnections * 0.2 +
metrics.totalReach * 0.15 +
metrics.centralityScore * 0.25 +
metrics.pageRank * 0.25 +
metrics.bridgingScore * 0.15;
allMetrics.push_back(metrics);
}
// Sort by influence
std::sort(allMetrics.begin(), allMetrics.end(),
[](const InfluenceMetrics& a, const InfluenceMetrics& b) {
return a.overallInfluence > b.overallInfluence;
});
if (allMetrics.size() > static_cast<size_t>(topN)) {
allMetrics.resize(topN);
}
return allMetrics;
}
// Calculate how many users can be reached within N hops
static int calculateReach(
const CXXGraph::Graph<std::string>& graph,
const std::string& userId,
int maxHops) {
std::unordered_set<std::string> reached;
std::queue<std::pair<std::string, int>> queue;
queue.push({userId, 0});
reached.insert(userId);
while (!queue.empty()) {
auto [currentId, depth] = queue.front();
queue.pop();
if (depth >= maxHops) continue;
CXXGraph::Node<std::string> node(currentId, currentId);
auto neighbors = graph.breadth_first_search(node);
for (const auto& neighborId : neighbors) {
if (reached.find(neighborId) == reached.end()) {
reached.insert(neighborId);
queue.push({neighborId, depth + 1});
}
}
}
return reached.size() - 1; // Exclude self
}
// Find users who would maximize spread (greedy algorithm)
static std::vector<std::string> findOptimalInfluencers(
const SocialNetwork& network,
int count) {
const auto& graph = network.getGraph();
auto allUsers = network.getAllUserIds();
std::vector<std::string> selected;
std::unordered_set<std::string> covered;
for (int i = 0; i < count; ++i) {
std::string bestUser;
int maxNewReach = 0;
// Greedy: pick user who reaches most uncovered users
for (const auto& userId : allUsers) {
if (std::find(selected.begin(), selected.end(), userId) != selected.end()) {
continue; // Already selected
}
CXXGraph::Node<std::string> node(userId, userId);
auto reachable = graph.breadth_first_search(node);
int newReach = 0;
for (const auto& reachedId : reachable) {
if (covered.find(reachedId) == covered.end()) {
newReach++;
}
}
if (newReach > maxNewReach) {
maxNewReach = newReach;
bestUser = userId;
}
}
if (!bestUser.empty()) {
selected.push_back(bestUser);
// Mark all reachable users as covered
CXXGraph::Node<std::string> node(bestUser, bestUser);
auto reachable = graph.breadth_first_search(node);
for (const auto& reachedId : reachable) {
covered.insert(reachedId);
}
}
}
return selected;
}
private:
static double calculateBetweennessCentrality(
const CXXGraph::Graph<std::string>& graph,
const std::string& userId,
const std::vector<std::string>& allUsers) {
// Simplified betweenness centrality
// Full calculation: count shortest paths through this node
double centrality = 0.0;
int sampleSize = std::min(50, static_cast<int>(allUsers.size()));
for (int i = 0; i < sampleSize; ++i) {
for (int j = i + 1; j < sampleSize; ++j) {
if (i >= static_cast<int>(allUsers.size()) ||
j >= static_cast<int>(allUsers.size())) break;
const auto& source = allUsers[i];
const auto& target = allUsers[j];
if (source == userId || target == userId) continue;
CXXGraph::Node<std::string> srcNode(source, source);
CXXGraph::Node<std::string> tgtNode(target, target);
auto result = graph.dijkstra(srcNode, tgtNode);
if (result.success) {
// Check if userId is on the path
for (const auto& nodeOnPath : result.path) {
if (nodeOnPath == userId) {
centrality += 1.0;
break;
}
}
}
}
}
// Normalize
if (sampleSize > 2) {
centrality = (centrality * 100.0) / (sampleSize * (sampleSize - 1) / 2);
}
return centrality;
}
static double calculateSimplifiedPageRank(
const CXXGraph::Graph<std::string>& graph,
const std::string& userId,
const std::vector<std::string>& allUsers) {
// Simplified PageRank: importance based on connections to important nodes
// Real PageRank requires iterative computation
CXXGraph::Node<std::string> userNode(userId, userId);
auto neighbors = graph.breadth_first_search(userNode);
double rank = static_cast<double>(neighbors.size());
// Bonus for being connected to well-connected users
for (const auto& neighborId : neighbors) {
if (neighborId == userId) continue;
CXXGraph::Node<std::string> neighborNode(neighborId, neighborId);
auto secondOrder = graph.breadth_first_search(neighborNode);
rank += secondOrder.size() * 0.1; // Damping factor
}
return rank;
}
static int calculateBridgingScore(
const CXXGraph::Graph<std::string>& graph,
const std::string& userId) {
// Users who connect otherwise disconnected communities
// Use articulation points or similar analysis
auto articulationPoints = graph.tarjan();
for (const auto& point : articulationPoints) {
if (point == userId) {
return 100; // This user is a critical bridge
}
}
return 0;
}
};
Key Metrics Explained:
- Betweenness Centrality: How often a user lies on shortest paths between others
- PageRank: Recursive importance (important people connected to other important people)
- Bridging Score: Connects disparate communities (articulation points)
Step 5: Community Detection - Finding Natural Groups
Social networks naturally form clusters - friend groups, interest communities, professional circles. Letβs detect them:
class CommunityDetector {
public:
struct Community {
std::string id;
std::vector<std::string> members;
double cohesion; // How tightly connected (0-1)
double isolation; // How separate from other communities
std::string theme; // Identified topic/interest
// Community characteristics
int internalEdges; // Connections within community
int externalEdges; // Connections to outside
double getModularity() const {
// Measure of community quality
int totalEdges = internalEdges + externalEdges;
if (totalEdges == 0) return 0.0;
return static_cast<double>(internalEdges) / totalEdges;
}
std::string getQualityRating() const {
if (cohesion > 0.8) return "Very Strong Community";
if (cohesion > 0.6) return "Strong Community";
if (cohesion > 0.4) return "Moderate Community";
return "Loose Association";
}
};
static std::vector<Community> detectCommunities(
const SocialNetwork& network) {
const auto& graph = network.getGraph();
std::cout << "π Detecting communities in network...\n";
// Use connected components for basic community detection
auto components = graph.connectedComponents();
std::vector<Community> communities;
int communityId = 1;
for (const auto& component : components) {
if (component.size() < 3) continue; // Skip tiny groups
Community comm;
comm.id = "community_" + std::to_string(communityId++);
comm.members = component;
// Calculate cohesion (how connected members are to each other)
comm.cohesion = calculateCohesion(graph, component);
// Calculate isolation (how separate from rest of network)
comm.isolation = calculateIsolation(graph, component,
network.getAllUserIds());
// Count internal vs external edges
auto edgeCounts = countEdges(graph, component);
comm.internalEdges = edgeCounts.first;
comm.externalEdges = edgeCounts.second;
// Identify theme (in real system, analyze user profiles/interests)
comm.theme = identifyTheme(network, component);
communities.push_back(comm);
}
// Sort by size
std::sort(communities.begin(), communities.end(),
[](const Community& a, const Community& b) {
return a.members.size() > b.members.size();
});
std::cout << "β Found " << communities.size() << " communities\n";
return communities;
}
// Find tight-knit friend groups (cliques)
static std::vector<std::vector<std::string>> findCliques(
const SocialNetwork& network,
int minSize = 3) {
const auto& graph = network.getGraph();
std::cout << "π Finding cliques (fully connected groups)...\n";
// Use Bron-Kerbosch algorithm for maximal cliques
auto allCliques = graph.bronKerbosch();
// Filter by minimum size
std::vector<std::vector<std::string>> cliques;
for (const auto& clique : allCliques) {
if (clique.size() >= static_cast<size_t>(minSize)) {
cliques.push_back(clique);
}
}
std::cout << "β Found " << cliques.size() << " cliques\n";
return cliques;
}
// Find overlapping communities (users can be in multiple)
static std::unordered_map<std::string, std::vector<std::string>>
findOverlappingCommunities(const SocialNetwork& network) {
const auto& graph = network.getGraph();
auto allUsers = network.getAllUserIds();
std::unordered_map<std::string, std::vector<std::string>> userCommunities;
// For each user, identify which communities they participate in
for (const auto& userId : allUsers) {
CXXGraph::Node<std::string> node(userId, userId);
auto neighbors = graph.breadth_first_search(node);
// Analyze neighbor clusters
auto neighborGroups = identifyNeighborClusters(graph, neighbors);
userCommunities[userId] = neighborGroups;
}
return userCommunities;
}
// Recommend communities for a user to join
static std::vector<Community> recommendCommunities(
const SocialNetwork& network,
const std::string& userId,
const std::vector<Community>& existingCommunities) {
std::vector<Community> recommendations;
const auto& graph = network.getGraph();
CXXGraph::Node<std::string> userNode(userId, userId);
auto userFriends = graph.breadth_first_search(userNode);
for (const auto& community : existingCommunities) {
// Check if user is already in community
if (std::find(community.members.begin(),
community.members.end(),
userId) != community.members.end()) {
continue;
}
// Count mutual friends in community
int friendsInCommunity = 0;
for (const auto& friendId : userFriends) {
if (std::find(community.members.begin(),
community.members.end(),
friendId) != community.members.end()) {
friendsInCommunity++;
}
}
// Recommend if user has multiple friends in community
if (friendsInCommunity >= 3) {
recommendations.push_back(community);
}
}
// Sort by number of friends in community
std::sort(recommendations.begin(), recommendations.end(),
[&userFriends](const Community& a, const Community& b) {
int aCount = 0, bCount = 0;
for (const auto& friendId : userFriends) {
if (std::find(a.members.begin(), a.members.end(),
friendId) != a.members.end()) aCount++;
if (std::find(b.members.begin(), b.members.end(),
friendId) != b.members.end()) bCount++;
}
return aCount > bCount;
});
return recommendations;
}
private:
static double calculateCohesion(
const CXXGraph::Graph<std::string>& graph,
const std::vector<std::string>& members) {
if (members.size() <= 1) return 1.0;
// Calculate density: actual edges / possible edges
int maxPossibleEdges = members.size() * (members.size() - 1) / 2;
int actualEdges = 0;
for (size_t i = 0; i < members.size(); ++i) {
for (size_t j = i + 1; j < members.size(); ++j) {
CXXGraph::Node<std::string> node1(members[i], members[i]);
CXXGraph::Node<std::string> node2(members[j], members[j]);
auto result = graph.dijkstra(node1, node2);
if (result.success && result.path.size() == 2) {
// Direct connection exists
actualEdges++;
}
}
}
return static_cast<double>(actualEdges) / maxPossibleEdges;
}
static double calculateIsolation(
const CXXGraph::Graph<std::string>& graph,
const std::vector<std::string>& members,
const std::vector<std::string>& allUsers) {
int internalConnections = 0;
int externalConnections = 0;
for (const auto& memberId : members) {
CXXGraph::Node<std::string> node(memberId, memberId);
auto neighbors = graph.breadth_first_search(node);
for (const auto& neighborId : neighbors) {
if (neighborId == memberId) continue;
bool isInternal = std::find(members.begin(), members.end(),
neighborId) != members.end();
if (isInternal) {
internalConnections++;
} else {
externalConnections++;
}
}
}
int totalConnections = internalConnections + externalConnections;
if (totalConnections == 0) return 0.0;
return static_cast<double>(internalConnections) / totalConnections;
}
static std::pair<int, int> countEdges(
const CXXGraph::Graph<std::string>& graph,
const std::vector<std::string>& members) {
int internal = 0;
int external = 0;
for (const auto& memberId : members) {
CXXGraph::Node<std::string> node(memberId, memberId);
auto neighbors = graph.breadth_first_search(node);
for (const auto& neighborId : neighbors) {
if (neighborId == memberId) continue;
bool isInternal = std::find(members.begin(), members.end(),
neighborId) != members.end();
if (isInternal) {
internal++;
} else {
external++;
}
}
}
return {internal / 2, external}; // Divide internal by 2 (counted twice)
}
static std::string identifyTheme(
const SocialNetwork& network,
const std::vector<std::string>& members) {
// In real system: analyze user profiles, shared hashtags,
// common groups, interaction patterns
if (members.size() > 100) return "Large Network";
if (members.size() > 50) return "Active Community";
if (members.size() > 20) return "Friend Circle";
return "Small Group";
}
static std::vector<std::string> identifyNeighborClusters(
const CXXGraph::Graph<std::string>& graph,
const std::vector<std::string>& neighbors) {
// Simplified - identify distinct groups among neighbors
std::vector<std::string> clusters;
// Real implementation would use sub-graph clustering
return clusters;
}
};
Step 6: Virality Prediction - Content Spread Simulation
Predict how content will spread through the network:
class ViralityPredictor {
public:
struct ViralityScore {
double expectedReach; // Number of users expected to see it
double spreadVelocity; // How fast it spreads (users/hour)
double persistence; // How long it stays relevant (hours)
double engagementRate; // Expected likes/shares ratio
double overallScore; // Combined metric (0-100)
std::string getViralityLevel() const {
if (overallScore > 80) return "π₯ Highly Viral";
if (overallScore > 60) return "β‘ Strong Potential";
if (overallScore > 40) return "π Moderate Spread";
if (overallScore > 20) return "π Limited Reach";
return "π€ Minimal Impact";
}
};
static ViralityScore predictVirality(
const SocialNetwork& network,
const std::string& originUserId,
double contentQuality = 0.5, // 0-1 scale
double shareability = 0.5) { // 0-1 scale
const auto& graph = network.getGraph();
ViralityScore score;
std::cout << "π Analyzing viral potential...\n";
// Factor 1: Origin user's influence
auto influenceMetrics = InfluenceAnalyzer::analyzeInfluence(network, 100);
double userInfluence = 0.0;
for (const auto& metric : influenceMetrics) {
if (metric.userId == originUserId) {
userInfluence = metric.overallInfluence;
break;
}
}
// Factor 2: Network reach potential
int maxReach = InfluenceAnalyzer::calculateReach(graph, originUserId, 5);
score.expectedReach = maxReach * contentQuality * shareability;
// Factor 3: Spread velocity (based on network density)
CXXGraph::Node<std::string> originNode(originUserId, originUserId);
auto immediateReach = graph.breadth_first_search(originNode);
double networkDensity = static_cast<double>(immediateReach.size()) /
network.getUserCount();
score.spreadVelocity = userInfluence * networkDensity *
shareability * 100.0;
// Factor 4: Content persistence
score.persistence = contentQuality * 48.0; // Hours content stays relevant
// Factor 5: Engagement rate estimation
score.engagementRate = (contentQuality + shareability) / 2.0;
// Calculate overall virality score
score.overallScore =
(score.expectedReach / network.getUserCount()) * 30.0 +
std::min(100.0, score.spreadVelocity) * 0.3 +
(score.persistence / 48.0) * 20.0 +
score.engagementRate * 20.0;
std::cout << "β Analysis complete\n";
return score;
}
// Simulate actual spread using infection model
static std::vector<int> simulateSpread(
const SocialNetwork& network,
const std::string& originUserId,
int timeSteps = 24, // Hours
double spreadProbability = 0.3, // Chance of sharing
double decayRate = 0.1) { // Content relevance decay
const auto& graph = network.getGraph();
std::vector<int> spreadCount(timeSteps);
std::unordered_set<std::string> exposed;
std::unordered_set<std::string> newlyExposed;
exposed.insert(originUserId);
newlyExposed.insert(originUserId);
spreadCount[0] = 1;
double currentRelevance = 1.0;
for (int t = 1; t < timeSteps; ++t) {
std::unordered_set<std::string> nextExposed;
// Decay content relevance over time
currentRelevance *= (1.0 - decayRate);
// Each newly exposed user can spread to their network
for (const auto& userId : newlyExposed) {
CXXGraph::Node<std::string> node(userId, userId);
auto neighbors = graph.breadth_first_search(node);
for (const auto& neighborId : neighbors) {
if (exposed.find(neighborId) == exposed.end()) {
// Probabilistic spread with decay
double effectiveProbability =
spreadProbability * currentRelevance;
double roll = static_cast<double>(rand()) / RAND_MAX;
if (roll < effectiveProbability) {
nextExposed.insert(neighborId);
exposed.insert(neighborId);
}
}
}
}
newlyExposed = nextExposed;
spreadCount[t] = exposed.size();
// Stop if no new spread
if (nextExposed.empty()) {
for (int remaining = t + 1; remaining < timeSteps; ++remaining) {
spreadCount[remaining] = exposed.size();
}
break;
}
}
return spreadCount;
}
// Find optimal users to target for maximum viral spread
static std::vector<std::string> findViralSeeds(
const SocialNetwork& network,
int seedCount = 5) {
std::cout << "π― Finding optimal viral seeds...\n";
// Use greedy algorithm: pick users with maximum marginal reach
return InfluenceAnalyzer::findOptimalInfluencers(network, seedCount);
}
// Predict cascade patterns
struct CascadePattern {
std::vector<std::vector<std::string>> waves; // Users exposed in each wave
int depth; // How many hops
double growthRate; // Exponential growth factor
std::string getPatternType() const {
if (growthRate > 2.0) return "Explosive Growth";
if (growthRate > 1.5) return "Viral Cascade";
if (growthRate > 1.0) return "Steady Spread";
return "Limited Diffusion";
}
};
static CascadePattern predictCascade(
const SocialNetwork& network,
const std::string& originUserId,
int maxDepth = 5) {
const auto& graph = network.getGraph();
CascadePattern pattern;
std::unordered_set<std::string> visited;
std::vector<std::string> currentWave = {originUserId};
visited.insert(originUserId);
for (int depth = 0; depth < maxDepth && !currentWave.empty(); ++depth) {
pattern.waves.push_back(currentWave);
std::vector<std::string> nextWave;
for (const auto& userId : currentWave) {
CXXGraph::Node<std::string> node(userId, userId);
auto neighbors = graph.breadth_first_search(node);
for (const auto& neighborId : neighbors) {
if (visited.find(neighborId) == visited.end()) {
nextWave.push_back(neighborId);
visited.insert(neighborId);
}
}
}
currentWave = nextWave;
}
pattern.depth = pattern.waves.size();
// Calculate growth rate
if (pattern.waves.size() >= 2) {
double totalGrowth = 0.0;
int growthSamples = 0;
for (size_t i = 1; i < pattern.waves.size(); ++i) {
if (pattern.waves[i-1].size() > 0) {
double waveGrowth = static_cast<double>(pattern.waves[i].size()) /
pattern.waves[i-1].size();
totalGrowth += waveGrowth;
growthSamples++;
}
}
pattern.growthRate = growthSamples > 0 ? totalGrowth / growthSamples : 1.0;
} else {
pattern.growthRate = 1.0;
}
return pattern;
}
};
Step 7: Complete Analytics Dashboard
Now letβs put it all together in a comprehensive dashboard:
class SocialNetworkDashboard {
public:
SocialNetworkDashboard(SocialNetwork& network) : network_(network) {}
void generateFullReport(const std::string& targetUserId) {
printHeader();
displayUserProfile(targetUserId);
displayNetworkOverview();
displayFriendRecommendations(targetUserId);
displayInfluenceMetrics(targetUserId);
displayCommunities(targetUserId);
predictContentVirality(targetUserId);
displayHealthMetrics();
printFooter();
}
private:
SocialNetwork& network_;
void printHeader() {
std::cout << "\n";
std::cout << "βββββββββββββββββββββββββββββββββββββββββββββββββ\n";
std::cout << "β SOCIAL NETWORK ANALYTICS DASHBOARD β\n";
std::cout << "β Powered by CXXGraph β\n";
std::cout << "βββββββββββββββββββββββββββββββββββββββββββββββββ\n\n";
}
void displayUserProfile(const std::string& userId) {
std::cout << "π€ USER PROFILE\n";
std::cout << " βββββββββββββββ\n";
try {
const auto& user = network_.getUser(userId);
std::cout << " Name: " << user.name << "\n";
std::cout << " ID: " << user.id << "\n";
std::cout << " Followers: " << user.followerCount << "\n";
std::cout << " Following: " << user.followingCount << "\n\n";
} catch (...) {
std::cout << " β οΈ User not found\n\n";
}
}
void displayNetworkOverview() {
std::cout << "π NETWORK OVERVIEW\n";
std::cout << " ββββββββββββββββββ\n";
std::cout << " Total Users: " << network_.getUserCount() << "\n";
std::cout << " Total Connections: " << network_.getConnectionCount() << "\n";
// Calculate network density
size_t userCount = network_.getUserCount();
size_t maxConnections = userCount * (userCount - 1) / 2;
double density = maxConnections > 0 ?
static_cast<double>(network_.getConnectionCount()) / maxConnections * 100.0 : 0.0;
std::cout << " Network Density: " << std::fixed
<< std::setprecision(2) << density << "%\n\n";
}
void displayFriendRecommendations(const std::string& userId) {
std::cout << "π€ FRIEND RECOMMENDATIONS\n";
std::cout << " βββββββββββββββββββββββ\n";
auto recommendations = FriendRecommender::recommendFriends(
network_, userId, 5);
if (recommendations.empty()) {
std::cout << " No recommendations available\n\n";
return;
}
for (size_t i = 0; i < recommendations.size(); ++i) {
const auto& rec = recommendations[i];
std::cout << " " << (i + 1) << ". " << rec.userName << "\n";
std::cout << " " << rec.getReason() << "\n";
std::cout << " Score: " << std::fixed
<< std::setprecision(1) << rec.score << "/100\n";
if (i < recommendations.size() - 1) std::cout << "\n";
}
std::cout << "\n";
}
void displayInfluenceMetrics(const std::string& userId) {
std::cout << "β YOUR INFLUENCE\n";
std::cout << " βββββββββββββββ\n";
auto allInfluencers = InfluenceAnalyzer::analyzeInfluence(network_, 100);
// Find user's rank
size_t userRank = 0;
const InfluenceAnalyzer::InfluenceMetrics* userMetrics = nullptr;
for (size_t i = 0; i < allInfluencers.size(); ++i) {
if (allInfluencers[i].userId == userId) {
userRank = i + 1;
userMetrics = &allInfluencers[i];
break;
}
}
if (userMetrics) {
std::cout << " Influence Rank: #" << userRank
<< " of " << network_.getUserCount() << "\n";
std::cout << " Level: " << userMetrics->getInfluenceLevel() << "\n";
std::cout << " Direct Connections: " << userMetrics->directConnections << "\n";
std::cout << " Total Reach: " << userMetrics->totalReach << " users\n";
std::cout << " Influence Score: " << std::fixed
<< std::setprecision(1) << userMetrics->overallInfluence << "/100\n";
} else {
std::cout << " Could not calculate influence metrics\n";
}
std::cout << "\n";
// Show top influencers
std::cout << " π Top 5 Influencers in Network:\n";
for (size_t i = 0; i < std::min(size_t(5), allInfluencers.size()); ++i) {
const auto& inf = allInfluencers[i];
std::cout << " " << (i + 1) << ". " << inf.userName
<< " (" << std::fixed << std::setprecision(1)
<< inf.overallInfluence << ")\n";
}
std::cout << "\n";
}
void displayCommunities(const std::string& userId) {
std::cout << "π₯ COMMUNITY ANALYSIS\n";
std::cout << " ββββββββββββββββββ\n";
auto communities = CommunityDetector::detectCommunities(network_);
std::cout << " Total Communities: " << communities.size() << "\n\n";
// Show user's communities
std::vector<CommunityDetector::Community> userCommunities;
for (const auto& comm : communities) {
if (std::find(comm.members.begin(), comm.members.end(), userId)
!= comm.members.end()) {
userCommunities.push_back(comm);
}
}
if (!userCommunities.empty()) {
std::cout << " Your Communities (" << userCommunities.size() << "):\n";
for (const auto& comm : userCommunities) {
std::cout << " β’ " << comm.theme << "\n";
std::cout << " Members: " << comm.members.size() << "\n";
std::cout << " Cohesion: " << std::fixed
<< std::setprecision(0) << (comm.cohesion * 100) << "%\n";
std::cout << " Quality: " << comm.getQualityRating() << "\n\n";
}
}
// Show largest communities
std::cout << " Largest Communities in Network:\n";
for (size_t i = 0; i < std::min(size_t(3), communities.size()); ++i) {
const auto& comm = communities[i];
std::cout << " " << (i + 1) << ". " << comm.theme
<< " (" << comm.members.size() << " members)\n";
}
std::cout << "\n";
}
void predictContentVirality(const std::string& userId) {
std::cout << "π VIRALITY PREDICTION\n";
std::cout << " βββββββββββββββββββ\n";
// Predict for high-quality content
auto score = ViralityPredictor::predictVirality(
network_, userId, 0.8, 0.7);
std::cout << " If you post high-quality content:\n\n";
std::cout << " Expected Reach: " << std::fixed
<< std::setprecision(0) << score.expectedReach << " users\n";
std::cout << " Spread Velocity: " << std::fixed
<< std::setprecision(1) << score.spreadVelocity << " users/hour\n";
std::cout << " Content Lifespan: " << std::fixed
<< std::setprecision(1) << score.persistence << " hours\n";
std::cout << " Engagement Rate: " << std::fixed
<< std::setprecision(0) << (score.engagementRate * 100) << "%\n\n";
std::cout << " Virality Level: " << score.getViralityLevel() << "\n";
std::cout << " Overall Score: " << std::fixed
<< std::setprecision(1) << score.overallScore << "/100\n\n";
}
void displayHealthMetrics() {
std::cout << "π₯ NETWORK HEALTH\n";
std::cout << " ββββββββββββββββββ\n";
const auto& graph = network_.getGraph();
// Check connectivity
bool connected = graph.isConnectedGraph();
std::cout << " Connectivity: "
<< (connected ? "β Fully Connected" : "β οΈ Fragmented") << "\n";
if (!connected) {
auto components = graph.connectedComponents();
std::cout << " Isolated Segments: " << components.size() << "\n";
}
// Find critical users (articulation points)
auto critical = graph.tarjan();
std::cout << " Critical Users: " << critical.size();
if (critical.size() > 0) {
std::cout << " β οΈ (Network vulnerable to these users leaving)";
}
std::cout << "\n\n";
// Health score
double healthScore = 100.0;
if (!connected) healthScore -= 30.0;
healthScore -= critical.size() * 2.0; // Penalty for critical nodes
healthScore = std::max(0.0, healthScore);
std::cout << " Overall Health Score: " << std::fixed
<< std::setprecision(1) << healthScore << "/100\n";
if (healthScore < 70.0) {
std::cout << " π‘ Recommendation: Encourage more connections\n";
std::cout << " to improve network resilience\n";
}
std::cout << "\n";
}
void printFooter() {
std::cout << "βββββββββββββββββββββββββββββββββββββββββββββββ\n";
std::cout << "Report generated by CXXGraph Social Analytics\n";
std::cout << "βββββββββββββββββββββββββββββββββββββββββββββββ\n\n";
}
};
Step 8: Real-World Production Example
Letβs build a complete working example that demonstrates everything:
#include "CXXGraph/CXXGraph.hpp"
#include <iostream>
#include <iomanip>
#include <random>
// [Include all classes above: SocialNetwork, FriendRecommender,
// InfluenceAnalyzer, CommunityDetector, ViralityPredictor,
// SocialNetworkDashboard]
// Helper function to generate realistic test data
class NetworkGenerator {
public:
static void populateTestNetwork(SocialNetwork& network, int userCount = 50) {
std::cout << "π§ Generating test network with " << userCount << " users...\n";
// Create users with realistic names
std::vector<std::string> firstNames = {
"Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace",
"Henry", "Iris", "Jack", "Kate", "Leo", "Mary", "Noah",
"Olivia", "Paul", "Quinn", "Rachel", "Steve", "Tina"
};
std::vector<std::string> lastNames = {
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia",
"Miller", "Davis", "Rodriguez", "Martinez"
};
std::vector<std::string> userIds;
// Generate users
for (int i = 0; i < userCount; ++i) {
std::string firstName = firstNames[i % firstNames.size()];
std::string lastName = lastNames[i % lastNames.size()];
std::string userId = "user_" + std::to_string(i + 1);
User user(userId, firstName + " " + lastName);
user.joinedTimestamp = std::time(nullptr) - (rand() % (365 * 24 * 3600));
network.addUser(user);
userIds.push_back(userId);
}
std::cout << "β Created " << userCount << " users\n";
// Create realistic connection patterns
std::cout << "π Generating connections...\n";
// Pattern 1: Dense clusters (friend groups)
int clusterSize = 7;
for (int cluster = 0; cluster < userCount / clusterSize; ++cluster) {
int baseIdx = cluster * clusterSize;
// Connect everyone in cluster to each other
for (int i = 0; i < clusterSize && baseIdx + i < userCount; ++i) {
for (int j = i + 1; j < clusterSize && baseIdx + j < userCount; ++j) {
int strength = 5 + (rand() % 4); // 5-8 strength
network.addFriendship(
userIds[baseIdx + i],
userIds[baseIdx + j],
strength
);
}
}
}
// Pattern 2: Inter-cluster bridges (weak ties)
for (int i = 0; i < userCount - clusterSize; i += clusterSize) {
int j = i + clusterSize;
if (j < userCount) {
network.addFriendship(userIds[i], userIds[j], 3); // Weaker connection
}
}
// Pattern 3: Random connections (acquaintances)
int randomConnections = userCount / 2;
for (int i = 0; i < randomConnections; ++i) {
int idx1 = rand() % userCount;
int idx2 = rand() % userCount;
if (idx1 != idx2) {
network.addFriendship(userIds[idx1], userIds[idx2], 2);
}
}
// Pattern 4: Influencer follows (asymmetric)
std::vector<int> influencerIndices = {0, 1, 2}; // First few users are influencers
for (int inf : influencerIndices) {
for (int follower = 3; follower < std::min(userCount, 15); ++follower) {
if (rand() % 3 == 0) { // 33% chance to follow
network.addFollowRelation(userIds[follower], userIds[inf]);
}
}
}
std::cout << "β Created " << network.getConnectionCount() << " connections\n";
std::cout << "β Network generation complete!\n\n";
}
// Simulate user interactions over time
static void simulateInteractions(SocialNetwork& network,
const std::vector<std::string>& userIds,
int interactionCount = 100) {
std::cout << "π¬ Simulating " << interactionCount << " interactions...\n";
for (int i = 0; i < interactionCount; ++i) {
int idx1 = rand() % userIds.size();
int idx2 = rand() % userIds.size();
if (idx1 != idx2) {
bool isMessage = rand() % 2 == 0;
bool isSha