relationalai.std.graphs.Compute
class relationalai.std.graphs.Compute
The Compute
class serves as a namespace for various graph algorithms.
This class is automatically instantiated when you create a Graph
object
and is accessible via the graph’s .compute
attribute.
It provides methods for computing basic graph statistics, centrality and similarity measures, community detection, and more.
Example
Section titled “Example”Graph algorithms are executed by calling the appropriate method from a Graph
object’s
.compute
attribute.
The following example demonstrates how to compute the PageRank of each person in a social network graph:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued 'friends' property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") alice.friends.extend([bob, carol]) carol.friends.add(daniel)
# Create an undirected graph with Person nodes and edges from people to their friends.# This graph has three edges: one from Alice to Bob, Alice to Carol, and Carol to Daniel.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
with model.query() as select: # Get all person objects. person = Person() # Compute the PageRank of each person. pagerank = graph.compute.pagerank(person) # Select the each person's name and their PageRank value. response = select(person.name, alias(pagerank, "pagerank"))
print(response.results)# Output:# name pagerank# 0 Alice 0.324562# 1 Bob 0.175438# 2 Carol 0.324562# 3 Daniel 0.175438
See the documentation for each method in the Methods section for more examples.
Methods
Section titled “Methods”Basic Statistics
Section titled “Basic Statistics”.num_nodes()
Section titled “.num_nodes()”Compute.num_nodes() -> Expression
Get the number of nodes in the graph. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Returns
Section titled “Returns”Returns an Expression that produces the number of nodes in the graph as an integer value.
Example
Section titled “Example”Use .num_nodes()
to get the number of nodes in a graph.
You access the .num_nodes()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person typemodel = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the graph and connect them with a 'friends' property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") alice.friends.add(bob) bob.friends.add(alice)
# Create an undirected graph with Person nodes and edges between friends.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Get the number of nodes in the graph.with model.query() as select: num_nodes = graph.compute.num_nodes() response = select(alias(num_nodes, "num_nodes"))
print(response.results)# Output:# num_nodes# 0 2
.num_edges()
Section titled “.num_edges()”Compute.num_edges() -> Expression
Get the number of edges in the graph. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Returns
Section titled “Returns”Returns an Expression that produces the number of edges in the graph as an integer value.
Example
Section titled “Example”Use .num_edges()
to get the number of edges in a graph.
You access the .num_edges()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person typemodel = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the graph and connect them with a 'friends' property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") alice.friends.add(bob) bob.friends.add(alice)
# Create an undirected graph with Person nodes and edges between friends.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Get the number of edges in the graph.with model.query() as select: num_edges = graph.compute.num_edges() response = select(alias(num_edges, "num_edges"))
print(response.results)# Output:# num_edges# 0 1
Degree
Section titled “Degree”.degree()
Section titled “.degree()”Compute.degree(node: Producer) -> Expression
Compute the degree of a node. For an undirected graph, the degree of a node is the number of neighbors it has in the graph. In a directed graph, the degree of a node is the sum of its indegree and outdegree. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Returns
Section titled “Returns”Returns an Expression object that produces the degree of the node as an integer value.
Example
Section titled “Example”Use .degree()
to compute the degree of a node in a graph.
You access the .degree()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person typemodel = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the graph and connect them with a 'friends' property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") alice.friends.add(bob) bob.friends.add(alice)
# Create an undirected graph with Person nodes and edges between friends.# This graph has one edge between Alice and Bob.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Get the number of nodes in the graph.with model.query() as select: # Get all Person objects. person = Person() # Compute the degree of each person. degree = graph.compute.degree(person) # Select the name of each person and their degree. response = select(person.name, degree)
print(response.results)# Output:# name v# 0 Alice 1# 1 Bob 1
In directed graphs, the degree of a node is the sum of its indegree and outdegree:
# Create a directed graph with Person nodes and edges between friends.# Note that graphs are directed by default.# This graph has two edges. One from Alice to Bob and one from Bob to Alice.directed_graph = Graph(model)directed_graph.Node.extend(Person)directed_graph.Edge.extend(Person.friends)
# Get the degree of each person in the graph.with model.query() as select: person = Person() degree = directed_graph.compute.degree(person) response = select(person.name, degree)
print(response.results)# Output:# name v# 0 Alice 2# 1 Bob 2
.indegree()
Section titled “.indegree()”Compute.indegree(node: Producer) -> Expression
Compute the indegree of a node.
In a directed graph, the indegree of a node is the number of edges that point to the node.
For an undirected graph, .indegree()
is an alias of .degree()
.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Returns
Section titled “Returns”Returns an Expression object that produces the indegree of the node as an integer value.
Example
Section titled “Example”Use .indegree()
to compute the indegree of a node in a graph.
You access the .indegree()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person typemodel = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the graph and connect them with a 'friends' property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") alice.friends.add(bob) bob.friends.add(alice)
# Create a directed graph with Person nodes and edges between friends.# Note that graphs are directed by default.# This graphs has two edges: one from Alice to Bob and one from Bob to Alice.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Get the number of nodes in the graph.with model.query() as select: # Get all Person objects. person = Person() # Compute the indegree of each person. indegree = graph.compute.indegree(person) # Select the name of each person and their indegree. response = select(person.name, indegree)
print(response.results)# Output:# name v# 0 Alice 1# 1 Bob 1
For an undirected graph, .indegree()
is same as .degree()
.
.outdegree()
Section titled “.outdegree()”Compute.outdegree(node: Producer) -> Expression
Compute the outdegree of a node in the graph.
In a directed graph, the outdegree of a node is the number of edges that point away from the node.
For an undirected graph, .outdegree()
is an alias of .degree()
.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Returns
Section titled “Returns”Returns an Expression object that produces the outdegree of the node as an integer value.
Example
Section titled “Example”Use .outdegree()
to compute the outdegree of a node in a graph.
You access the .outdegree()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person typemodel = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the graph and connect them with a 'friends' property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") alice.friends.add(bob) bob.friends.add(alice)
# Create a directed graph with Person nodes and edges between friends.# Note that graphs are directed by default.# This graphs has two edges: one from Alice to Bob and one from Bob to Alice.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Get the number of nodes in the graph.with model.query() as select: # Get all Person objects. person = Person() # Compute the outdegree of each person. outdegree = graph.compute.outdegree(person) # Select the name of each person and their outdegree. response = select(person.name, outdegree)
print(response.results)# Output:# name v# 0 Alice 1# 1 Bob 1
In undirected graphs, .outdegree()
is same as .degree()
.
Centrality Measures
Section titled “Centrality Measures”.betweenness_centrality()
Section titled “.betweenness_centrality()”Compute.betweenness_centrality(node: Producer) -> Expression
Computes the betweenness centrality of a node. Betweenness centrality measures the importance of a node by counting how often it appears on the shortest paths between pairs of nodes in a graph. Nodes with high betweenness centrality may play critical roles in the flow of information or resources through a network. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Algorithm Details
Section titled “Algorithm Details”Calculating betweenness centrality requires determining all shortest paths between
each pair of nodes within a graph, which makes it computationally intensive to compute
exactly for large networks. betweenness_centrality()
gives an approximation using the
Brandes-Pich algorithm,
which samples nodes uniformly at random and performs single-source shortest-path computations
from those nodes.
This implementation nominally samples 100 nodes uniformly at random,
yielding time complexity of 100 * O(|V|+|E|)
where |V|
and |E|
are the number of
nodes and edges respectively. If the graph has fewer than 100 nodes, the implementation
reduces to the exact Brandes algorithm,
with time complexity O(|V|(|V|+|E|))
for unweighted graphs.
Returns
Section titled “Returns”Returns an Expression object that produces the betweenness centrality of the node as a floating-point value.
Example
Section titled “Example”Use .betweenness_centrality()
to compute the betweenness centrality of a node in a graph.
You access the .betweenness_centrality()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.follows.extend([bob, carol]) bob.follows.add(alice) carol.follows.add(alice)
# Create a directed graph with Person nodes and edge from people to the people they follow.# Note that graphs are directed by default.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Compute the betweenness centrality of each person in the graph.with model.query() as select: # Get all person objects. person = Person() # Compute the betweenness centrality of each person. centrality = graph.compute.betweenness_centrality(person) # Select the each person's name and their betweenness centrality. response = select(person.name, alias(centrality, "betweenness_centrality"))
print(response.results)# Output:# name betweenness_centrality# 0 Alice 2.0# 1 Bob 0.0# 2 Carol 0.0
If one of the objects produced by the node
producer is not a node in the graph, no exception is raised.
Instead, that object is filtered from the rule or query:
# Add a Company type to the model.Company = model.Type("Company")
# Add some companies to the model.with model.rule(): apple = Company.add(name="Apple") google = Company.add(name="Google")
# Get the betweenness centrality of each person and company in the graph.with model.query() as select: # Get all person and company objects. obj = (Person | Company)() # Compute the betweenness centrality of each object. # Objects that are not nodes in the graph are filtered out. centrality = graph.compute.betweenness_centrality(obj) # Select the each object's name and their betweenness centrality. response = select(obj.name, alias(centrality, "betweenness_centrality"))
# Only rows for people are returned, since companies are not nodes in the graph.print(response.results)# Output:# name betweenness_centrality# 0 Alice 2.0# 1 Bob 0.0# 2 Carol 0.0
.degree_centrality()
Section titled “.degree_centrality()”Compute.degree_centrality(node: Producer) -> Expression
This algorithm computes the degree centrality of a node. For unweighted graphs, it measures the importance of a node based on its degree. Nodes with high degree centrality are well-connected in the graph.
For weighted graphs, the weighted degree centrality of a node is the sum of the weights of the edges incident to the node divided by one less than the number of nodes in the graph.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Returns
Section titled “Returns”Returns an Expression object that produces the degree centrality of the node as a floating-point value, calculated by the following formula:
degree centrality = degree of the node / (total number of nodes - 1)
This value is at most 1.0
for simple graphs with no self loops.
In graphs with self loops, a node’s degree centrality can exceed 1.0
.
Example (Unweighted Graphs)
Section titled “Example (Unweighted Graphs)”Use .degree_centrality()
to compute the degree centrality of a node in a graph.
You access the .degree_centrality()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.follows.extend([bob, carol]) bob.follows.add(alice) carol.follows.add(alice)
# Create a directed graph with Person nodes and edge from people to the people they follow.# Note that graphs are directed by default.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Compute the degree centrality of each person in the graph.with model.query() as select: # Get all person objects. person = Person() # Compute the degree centrality of each person. centrality = graph.compute.degree_centrality(person) # Select the each person's name and their degree centrality. response = select(person.name, alias(centrality, "degree_centrality"))
print(response.results)# Output:# name degree_centrality# 0 Alice 2.0# 1 Bob 1.0# 2 Carol 1.0
If one of the objects produced by the node
producer is not a node in the graph, no exception is raised.
Instead, that object is filtered from the rule or query:
# Add a Company type to the model.Company = model.Type("Company")
# Add some companies to the model.with model.rule(): apple = Company.add(name="Apple") google = Company.add(name="Google")
# Get the degree centrality of each person and company in the graph.with model.query() as select: # Get all person and company objects. obj = (Person | Company)() # Compute the degree centrality of each object. # Objects that are not nodes in the graph are filtered out. centrality = graph.compute.degree_centrality(obj) # Select the each object's name and their degree centrality. response = select(obj.name, alias(centrality, "degree_centrality"))
# Only rows for people are returned, since companies are not nodes in the graph.print(response.results)# Output:# name degree_centrality# 0 Alice 2.0# 1 Bob 1.0# 2 Carol 1.0
Example (Weighted Graphs)
Section titled “Example (Weighted Graphs)”Use .degree_centrality()
to compute the weighted degree centrality of a node in a graph.
You access the .degree_centrality()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with Person and Friendship types.model = rai.Model("socialNetwork")Person = model.Type("Person")Friendship = model.Type("Friendship")
# Add some people to the model and connect them with friendships.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") Friendship.add(person1=alice, person2=bob, strength=100) Friendship.add(person1=bob, person2=carol, strength=10)
# Create an weighted, undirected graph with Person nodes and edges between friends.# This graph has two edges: one from Alice and Bob and one from Bob and Carol.# The edges are weighted by the strength of each friendship.graph = Graph(model, undirected=True, weighted=True)graph.Node.extend(Person)with model.rule(): friendship = Friendship() graph.Edge.add(friendship.person1, friendship.person2, weight=friendship.strength)
# Compute the weighted degree centrality of each person in the graph.with model.query() as select: person = Person() centrality = graph.compute.degree_centrality(person) response = select(person.name, alias(centrality, "degree_centrality"))
print(response.results)# Output:# name degree_centrality# 0 Alice 50.0# 1 Bob 55.0# 2 Carol 5.0
.eigenvector_centrality()
Section titled “.eigenvector_centrality()”Compute.eigenvector_centrality(node: Producer) -> Expression
Computes the eigenvector centrality of a node in an undirected graph. Eigenvector centrality measures a node’s significance in a graph, taking into account not only its direct connections, but also the centrality of those connected nodes. Specifically, it assigns to each node a centrality score that is proportional to the sum of the centrality scores of its neighboring nodes. Thus, nodes with high eigenvector centrality are important because they are connected to other important nodes. Must be called in a rule or query context.
Directed graphs are not currently supported.
See .pagerank()
for a similar measure that works with directed graphs.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | No | Currently not supported. |
Undirected | Yes | See Returns for convergence criteria. |
Weighted | Yes | Weights must be non-negative. |
Unweighted | Yes | Edge weights default to 1.0 . |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Returns
Section titled “Returns”Returns an Expression object that produces the eigenvector centrality of the node as a floating-point value.
We use the power iteration method, which converges when there is a unique eigenvalue with the largest absolute magnitude. An important example where this condition does not hold is in the case of bipartite graphs. Specifically, in a bipartite graph, the adjacency spectrum has a unique property: its eigenvalues are symmetric around zero, meaning for every positive eigenvalue, there is a corresponding negative eigenvalue of the same magnitude.
Example
Section titled “Example”Use .eigenvector_centrality()
to compute the eigenvector centrality of a node in an undirected graph.
Directed graphs are currently not supported.
You access the .eigenvector_centrality()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued 'friends' property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") alice.friends.extend([bob, carol]) carol.friends.add(daniel)
# Create an undirected graph with Person nodes and edges from people to their friends.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
with model.query() as select: # Get all person objects. person = Person() # Compute the eigenvector centrality of each person. centrality = graph.compute.eigenvector_centrality(person) # Select the each person's name and their eigenvector centrality. response = select(person.name, alias(centrality, "eigenvector_centrality"))
print(response.results)# Output:# name eigenvector_centrality# 0 Alice 0.601501# 1 Bob 0.371748# 2 Carol 0.601501# 3 Daniel 0.371748
If one of the objects produced by the node
producer is not a node in the graph, no exception is raised.
Instead, that object is filtered from the rule or query:
# Add a Company type to the model.Company = model.Type("Company")
# Add some companies to the model.with model.rule(): apple = Company.add(name="Apple") google = Company.add(name="Google")
with model.query() as select: # Get all person and company objects. obj = (Person | Company)() # Compute the eigenvector centrality of each object. # Objects that are not nodes in the graph are filtered out. centrality = graph.compute.eigenvector_centrality(obj) # Select the each object's name and their eigenvector centrality. response = select(obj.name, alias(centrality, "eigenvector_centrality"))
# Only rows for people are returned, since companies are not nodes in the graph.print(response.results)# Output:# name eigenvector_centrality# 0 Alice 0.601501# 1 Bob 0.371748# 2 Carol 0.601501# 3 Daniel 0.371748
.pagerank()
Section titled “.pagerank()”Compute.pagerank( node: Producer, damping_factor: float = 0.85, tolerance: float = 1e-6, max_iter: int = 20) -> Expression
Computes the PageRank centrality of a node in a graph. PageRank measures a node’s influence in a graph based on the quality and quantity of its inbound links. Nodes with high PageRank values may be considered more influential than other nodes in the network. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Only non-negative weights are supported. |
Unweighted | Yes | Edge weights default to 1.0 . |
Parameters
Section titled “Parameters”Name | Type | Description | Range |
---|---|---|---|
node | Producer | A node in the graph. | Vertex set. |
damping_factor | float | The PageRank damping factor. Must be between 0.0 and 1.0 , exclusive of 1.0 . Default is 0.85. | [0,1) . |
tolerance | float | The convergence tolerance for the PageRank algorithm. Default is 1e-6 . | Small positive number or zero. |
max_iter | int | The maximum number of iterations allowed in the PageRank algorithm. Default is 20 . | Positive integer. |
Returns
Section titled “Returns”Returns an Expression
object that produces
the PageRank centrality of the node as a floating-point value.
Example
Section titled “Example”Use .pagerank()
to compute the PageRank centrality of a node in a graph.
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued 'friends' property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") alice.friends.extend([bob, carol]) carol.friends.add(daniel)
# Create an undirected graph with Person nodes and edges from people to their friends.# This graph has three edges: one from Alice to Bob, Alice to Carol, and Carol to Daniel.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
with model.query() as select: # Get all person objects. person = Person() # Compute the PageRank of each person. pagerank = graph.compute.pagerank(person) # Select the each person's name and their PageRank value. response = select(person.name, alias(pagerank, "pagerank"))
print(response.results)# Output:# name pagerank# 0 Alice 0.324562# 1 Bob 0.175438# 2 Carol 0.324562# 3 Daniel 0.175438
If one of the objects produced by the node
producer is not a node in the graph, no exception is raised.
Instead, that object is filtered from the rule or query:
# Add a Company type to the model.Company = model.Type("Company")
# Add some companies to the model.with model.rule(): apple = Company.add(name="Apple") google = Company.add(name="Google")
with model.query() as select: # Get all person and company objects. obj = (Person | Company)() # Compute the PageRank of each object. # Objects that are not nodes in the graph are filtered out. pagerank = graph.compute.pagerank(obj) # Select the each object's name and their PageRank value. response = select(obj.name, alias(pagerank, "pagerank"))
# Only rows for people are returned, since companies are not nodes in the graph.print(response.results)# Output:# name pagerank# 0 Alice 0.324562# 1 Bob 0.175438# 2 Carol 0.324562# 3 Daniel 0.175438
Ego-Networks
Section titled “Ego-Networks”.ego_network()
Preview
Section titled “.ego_network() ”Compute.ego_network(node: Producer, hops: int = 1) -> Expression
This method returns the induced subgraph containing all nodes within k undirected hops from a specified source node (ego-node). It identifies nearby nodes based on undirected shortest path distance, effectively capturing the local structure around the ego-node. This is useful for analyzing local connectivity patterns or extracting ego-networks. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Returns
Section titled “Returns”Returns an Expression that produces the set of edges induced by the ego_network function.
Examples
Section titled “Examples”You access the .ego_network(node, hops)
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
model = rai.Model("socialNetwork")Person = model.Type("Person")
with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") claire = Person.add(name="Claire") dave = Person.add(name="Dave") eve = Person.add(name="Eve") frank = Person.add(name="Frank") gina = Person.add(name="Gina") alice.follows.add(alice) alice.follows.add(bob) bob.follows.add(claire) claire.follows.add(dave) dave.follows.add(eve) eve.follows.add(frank) frank.follows.add(gina)
graph = Graph(model, undirected=False)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
with model.query() as select: node = Person() a,b = graph.compute.ego_network(node, 1) ego_response = select(alias(node.name, "ego-node") , alias( Person(a).name, "u"), alias(Person(b).name, "v"))
print(ego_response.results)
# output# ego-node u v# 0 Alice Alice Alice# 1 Alice Alice Bob# 2 Bob Alice Alice# 3 Bob Alice Bob# 4 Bob Bob Claire# 5 Claire Bob Claire# 6 Claire Claire Dave# 7 Dave Claire Dave# 8 Dave Dave Eve# 9 Eve Dave Eve# 10 Eve Eve Frank# 11 Frank Eve Frank# 12 Frank Frank Gina# 13 Gina Frank Gina
If you’re interested in retrieving the 2-hop ego-network of a specific node (e.g., Alice), you can apply the following modification to the code above:
with model.query() as select: node = Person(name="Alice") a,b = graph.compute.ego_network(node, hops=2) ego_response = select(alias(node.name, "ego-node") , alias( Person(a).name, "u"), alias(Person(b).name, "v"))
# ego-node u v# 0 Alice Alice Alice# 1 Alice Alice Bob# 2 Alice Bob Claire
Similarity Measures
Section titled “Similarity Measures”.cosine_similarity()
Section titled “.cosine_similarity()”Compute.cosine_similarity(node1: Producer, node2: Producer) -> Expression
This algorithm measures the cosine similarity between two nodes in a graph.
For unweighted graphs, it measures the similarity between two nodes based on the inner product between respective neighborhood vectors.
Values range from 0.0
to 1.0
, inclusive, where 1.0
indicating that the nodes have identical neighborhoods and 0.0
indicating no meaningful relationship.
For weighted graphs, it measures the similarity between two nodes based on the inner product between respective neighborhood vectors,
weighted according to the edges.
Values range from -1.0
to 1.0
, inclusive, with higher value indicating greater similarity.
If all weights are 1.0 it degenerates to the unweighted case.
In both cases, pairs of nodes with a similarity of 0.0
, indicating no meaningful relationship,
are excluded from results for improved performance.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | Based on out-neighbors. |
Undirected | Yes | |
Weighted | Yes | |
Unweighted | Yes |
Parameters
Section titled “Parameters”Returns
Section titled “Returns”Returns an Expression object that produces the cosine similarity between the two nodes as a floating-point value.
Example (Unweighted Graphs)
Section titled “Example (Unweighted Graphs)”Use .cosine_similarity()
to compute the cosine similarity between two nodes in a graph.
You access the .cosine_similarity()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `friend` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.friends.add(bob) bob.friends.add(carol)
# Create an undirected graph with Person nodes and edges between friends.# Note that cosine similarity is only supported for undirected graphs.# This graph has two edges: one between Alice and Bob, and one between Bob and Carol.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
with model.query() as select: # Get pairs of people. person1, person2 = Person(), Person() # Compute the cosine similarity between each pair of people. similarity = graph.compute.cosine_similarity(person1, person2) # Select each person's name and their similarity value. response = select(person1.name, person2.name, alias(similarity, "cosine_similarity"))
print(response.results)# Output:# name name2 cosine_similarity# 0 Alice Alice 1.0# 1 Alice Carol 1.0# 2 Bob Bob 1.0# 3 Carol Alice 1.0# 4 Carol Carol 1.0
There is no row for Alice and Bob in the preceding query’s results.
That’s because Alice and Bob have a cosine similarity of 0.0
.
Pairs of nodes with zero similarity, indicating no meaningful similarity, are often excluded from analyses.
Consequently, we filter out these pairs to improve performance.
If node1
or node2
is not a node in the graph, no exception is raised.
Instead, that object is filtered from the rule or query:
# Add a Company type to the model.Company = model.Type("Company")
# Add some companies to the model.with model.rule(): apple = Company.add(name="Apple") google = Company.add(name="Google")
# Create the union of the Person and Company types.PersonOrCompany = Person | Company
with model.query() as select: # Get all person and company objects. obj1, obj2 = PersonOrCompany(), PersonOrCompany() obj1 < ob2 # Ensure pairs are unique. Compares internal object IDs. # Compute the cosine similarity between each pair of objects. # Objects that are not nodes in the graph are filtered out of the results. similarity = graph.compute.cosine_similarity(obj1, obj2) response = select(obj1.name, obj2.name, alias(similarity, "cosine_similarity"))
# Only rows for people are returned, since companies are not nodes in the graph.print(response.results)# Output:# name name2 cosine_similarity# 0 Carol Alice 1.0
Example (Weighted Graphs)
Section titled “Example (Weighted Graphs)”Use .cosine_similarity()
to compute the weighted cosine similarity between two nodes in a graph.
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with Person and Friendship types.model = rai.Model("socialNetwork")Person = model.Type("Person")Friendship = model.Type("Friendship")
# Add some people to the model and connect them with friendships.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") Friendship.add(person1=alice, person2=bob, strength=20) Friendship.add(person1=bob, person2=carol, strength=10) Friendship.add(person1=alice, person2=carol, strength=10)
# Create a weighted, undirected graph with Person nodes and edges between friends.# This graph has two edges: one between Alice and Bob, and one between Bob and Carol.# The edges are weighted by the strength of each friendship.graph = Graph(model, undirected=True, weighted=True)graph.Node.extend(Person)with model.rule(): friendship = Friendship() graph.Edge.add(friendship.person1, friendship.person2, weight=friendship.strength)
# Compute the weighted cosine similarity between each pair of people in the graph.with model.query() as select: person1, person2 = Person(), Person() similarity = graph.compute.cosine_similarity(person1, person2) response = select(person1.name, person2.name, alias(similarity, "cosine_similarity"))
print(response.results)# Output:# name name2 cosine_similarity# 0 Alice Alice 1.000000# 1 Alice Bob 0.200000# 2 Alice Carol 0.894427# 3 Bob Alice 0.200000# 4 Bob Bob 1.000000# 5 Bob Carol 0.894427# 6 Carol Alice 0.894427# 7 Carol Bob 0.894427# 8 Carol Carol 1.000000
.jaccard_similarity()
Section titled “.jaccard_similarity()”Compute.jaccard_similarity(node1: Producer, node2: Producer) -> Expression
This algorithm measures the Jaccard similarity between two nodes in a graph.
For unweighted graphs, it measures the similarity between two nodes based on how many neighbors (or out-neighbors if the graph is directed) they share.
Values range from 0.0
to 1.0
, inclusive, with 1.0
indicating that the nodes have identical neighborhoods and 0.0
indicating no meaningful relationship.
For weighted graphs, it measures the similarity between two nodes as the ratio of
the sums of the minimum and maximum edge weights connecting them.
Values range from 0.0
to 1.0
, inclusive, with higher values indicating greater similarity.
If all weights are 1.0 it degenerates to the unweighted case.
In both cases, pairs of nodes with a similarity of 0.0
, indicating no meaningful relationship,
are excluded from results for improved performance.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | Based on out-neighbors. |
Undirected | Yes | |
Weighted | Yes | |
Unweighted | Yes |
Parameters
Section titled “Parameters”Returns
Section titled “Returns”Returns an Expression object that produces the Jaccard similarity between the two nodes as a floating-point value, calculated by the following formula:
Jaccard similarity = (number of shared neighbors) / (total number of unique neighbors)
Example (Unweighted Graphs)
Section titled “Example (Unweighted Graphs)”Use .jaccard_similarity()
to compute the Jaccard similarity between two nodes in a graph.
You access the .jaccard_similarity()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `friend` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.friends.add(bob) bob.friends.add(carol)
# Create an undirected graph with Person nodes and edges between friends.# This graph has two edges: one between Alice and Bob, and one between Bob and Carol.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
with model.query() as select: # Get pairs of people. person1, person2 = Person(), Person() # Compute the Jaccard similarity between each pair of people. similarity = graph.compute.jaccard_similarity(person1, person2) # Select each person's name and their similarity value. response = select(person1.name, person2.name, alias(similarity, "jaccard_similarity"))
print(response.results)# Output:# name name2 jaccard_similarity# 0 Alice Alice 1.0# 1 Alice Carol 1.0# 2 Bob Bob 1.0# 3 Carol Alice 1.0# 4 Carol Carol 1.0
There is no row for Alice and Bob in the preceding query’s results.
That’s because Alice and Bob have a Jaccard similarity of 0.0
.
Pairs of nodes with zero similarity, indicating no meaningful similarity, are often excluded from analyses.
Consequently, we filter out these pairs to improve performance.
If node1
or node2
is not a node in the graph, no exception is raised.
Instead, that object is filtered from the rule or query:
# Add a Company type to the model.Company = model.Type("Company")
# Add some companies to the model.with model.rule(): apple = Company.add(name="Apple") google = Company.add(name="Google")
# Create the union of the Person and Company types.PersonOrCompany = Person | Company
with model.query() as select: # Get all person and company objects. obj1, obj2 = PersonOrCompany(), PersonOrCompany() obj1 < obj2 # Ensure pairs are unique. Compares internal object IDs. # Compute the Jaccard similarity between each pair of objects. # Objects that are not nodes in the graph are filtered out of the results. similarity = graph.compute.jaccard_similarity(obj1, obj2) response = select(obj1.name, obj2.name, alias(similarity, "jaccard_similarity"))
# Only rows for people are returned, since companies are not nodes in the graph.# Also note that unlike in the preceding case, only distinct pairs of people are returned. We ensure this by requiring obj1 < obj2,# where obj1 and obj2 represent the internal IDs of the individuals, and the IDs are ordered numerically.
print(response.results)# Output:# name name2 jaccard_similarity# 0 Carol Alice 1.0
Example (Weighted Graphs)
Section titled “Example (Weighted Graphs)”Use .jaccard_similarity()
to compute the weighted Jaccard similarity between two nodes in a graph.
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with Person and Friendship types.model = rai.Model("socialNetwork")Person = model.Type("Person")Friendship = model.Type("Friendship")
# Add some people to the model and connect them with friendships.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") Friendship.add(person1=alice, person2=bob, strength=20) Friendship.add(person1=bob, person2=carol, strength=10) Friendship.add(person1=alice, person2=carol, strength=10)
# Create a weighted, undirected graph with Person nodes and edges between friends.# This graph has two edges: one between Alice and Bob, and one between Bob and Carol.# The edges are weighted by the strength of each friendship.graph = Graph(model, undirected=True, weighted=True)graph.Node.extend(Person)with model.rule(): friendship = Friendship() graph.Edge.add(friendship.person1, friendship.person2, weight=friendship.strength)
# Compute the weighted Jaccard similarity between each pair of people in the graph.with model.query() as select: person1, person2 = Person(), Person() similarity = graph.compute.jaccard_similarity(person1, person2) response = select(person1.name, person2.name, alias(similarity, "jaccard_similarity"))
print(response.results)# Output:# name name2 jaccard_similarity# 0 Alice Alice 1.00# 1 Alice Bob 0.20# 2 Alice Carol 0.25# 3 Bob Alice 0.20# 4 Bob Bob 1.00# 5 Bob Carol 0.25# 6 Carol Alice 0.25# 7 Carol Bob 0.25# 8 Carol Carol 1.00
Link Prediction
Section titled “Link Prediction”.adamic_adar()
Preview
Section titled “.adamic_adar() ”Compute.adamic_adar(node1: Producer, node2: Producer) -> Expression
Compute the Adamic-Adar index between two nodes in a graph. The Adamic-Adar index quantifies node similarity based on shared neighbors. Values are non-negative. The algorithm accepts directed graphs as input but processes them as undirected by ignoring edge directionality. In link prediction analysis, a high Adamic-Adar values may indicate that two nodes are likely to form a connection if they do not already have one. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | Directed edges are treated as undirected by the algorithm. |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Returns
Section titled “Returns”Returns an Expression object that produces the Adamic-Adar index between the two nodes as a floating-point value, calculated by the following formula:
Adamic-Adar index = sum(1 / log(degree of shared neighbor))
The sum is over all shared neighbors of the two nodes.
Example
Section titled “Example”Use .adamic_adar()
to compute the Adamic-Adar index between two nodes in a graph.
You access the .adamic_adar()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `friend` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.friends.extend([bob, carol])
# Create an undirected graph with Person nodes and edges between friends.# This graph has two edges: one between Alice and Bob, and one between Bob and Carol.# Even when creating a directed graph by setting `undirected=False`,# the output remains identical to the undirected case.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Compute the Adamic-Adar index between each pair of people in the graph.with model.query() as select: person1, person2 = Person(), Person() similarity = graph.compute.adamic_adar(person1, person2) response = select(person1.name, person2.name, alias(similarity, "adamic_adar"))
print(response.results)# Output:# name name2 adamic_adar# 0 Alice Alice inf# 1 Bob Bob 1.442695# 2 Bob Carol 1.442695# 3 Carol Bob 1.442695# 4 Carol Carol 1.442695
There is no row for Alice and Bob in the preceding query’s results.
That’s because Alice and Bob have a an Adamic-Adar index of 0.0
.
Pairs of nodes with zero index, indicating no meaningful similarity, are often excluded from analyses.
Consequently, we filter out these pairs to improve performance.
If node1
or node2
is not a node in the graph, no exception is raised.
Instead, that object is filtered from the rule or query:
# Add a Company type to the model.Company = model.Type("Company")
# Add some companies to the model.with model.rule(): apple = Company.add(name="Apple") google = Company.add(name="Google")
# Create the union of the Person and Company types.PersonOrCompany = Person | Company
with model.query() as select: # Get all person and company objects. obj1, obj2 = PersonOrCompany(), PersonOrCompany() obj1 < obj2 # Ensure pairs are unique. Compares internal object IDs. # Compute the Adamic-Adar index between each pair of objects. # Objects that are not nodes in the graph are filtered. similarity = graph.compute.adamic_adar(obj1, obj2) response = select(obj1.name, obj2.name, alias(similarity, "adamic_adar"))
# Only rows for people are returned, since companies are not nodes in the graph.print(response.results)# Output:# name name2 adamic_adar# 0 Carol Bob 1.442695
.common_neighbor()
Preview
Section titled “.common_neighbor() ”Compute.common_neighbor(node1: Producer, node2: Producer) -> Expression
Find the common neighbors between two nodes in a graph. In an undirected graph, a node w is a common neighbor of nodes u and v if there are edges connecting w to both u and v. The algorithm accepts directed graphs as input but processes them as undirected by ignoring edge directionality. Namely, in a directed graph, a node w is a common neighbor of nodes u and v if there are edges between w and both u and v, regardless of the direction of those edges.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Undirected | Yes | |
Directed | Yes | Directed edges are treated as undirected by the algorithm. |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Returns
Section titled “Returns”Returns an Expression object that produces
nodes that are common neighbors of node1
and node2
.
Example
Section titled “Example”Use .common_neighbor()
to find the common neighbors between two nodes in a graph.
You access the .common_neighbor()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `friend` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.friends.extend([bob, carol])
# Create an undirected graph with Person nodes and edges between friends.# This graph has two edges: one between Alice and Bob, and one between Alice and Carol.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Find the common neighbors of Bob and Carol.with model.query() as select: neighbor = graph.compute.common_neighbor(Person(name="Bob"), Person(name="Carol")) response = select(neighbor.name)
print(response.results)# Output:# name# 0 Alice
If node1
or node2
is not a node in the graph, no exception is raised.
Instead, that pair of objects is filtered from the rule or query:
# Add a Company type to the model.Company = model.Type("Company")
# Add some companies to the model.with model.rule(): apple = Company.add(name="Apple") google = Company.add(name="Google")
# Create the union of the Person and Company types.PersonOrCompany = Person | Company
with model.query() as select: # Get all pairs of person and company objects. obj1, obj2 = PersonOrCompany(), PersonOrCompany() obj1 < obj2 # Ensure pairs are unique. neighbor = graph.compute.common_neighbor(obj1, obj2) response = select(obj1.name, obj2.name, neighbor.name)
# Only rows for people are returned, since companies are not nodes in the graph.print(response.results)# Output:# name name2 name3# 0 Carol Bob Alice
Note that pairs of nodes with no common neighbors are filtered by .common_neighbor()
.
For example, there is no row for Alice and Bob in the above output because they have no common neighbors.
Alice is neighbors with Bob and Carol, but Bob’s only neighbor is Alice.
.preferential_attachment()
Preview
Section titled “.preferential_attachment() ”Compute.preferential_attachment(node1: Producer, node2: Producer) -> Expression
Compute the preferential attachment score between two nodes in a graph. The preferential attachment score quantifies node similarity based on the product of their number of neighbors. In link prediction analysis, a high preferential attachment score may indicate that two nodes are likely to form a connection under the assumption that connections are more likely between nodes with higher degrees. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Undirected | Yes | |
Directed | Yes | Operates on the undirected version of the graph. |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Returns
Section titled “Returns”Returns an Expression object that produces the preferential attachment score between the two nodes as an integer value, calculated by the following formula:
preferential attachment = number of neighbors of node1 * number of neighbors of node2
Example
Section titled “Example”Use .preferential_attachment()
to compute the preferential attachment score between two nodes in a graph.
You access the .preferential_attachment()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `friend` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.friends.add(bob)
# Create an undirected graph with Person nodes and edges between friends.# This graph has one edge between Alice and Bob. Carol is not connected to anyone.# Even when creating a directed graph by setting `undirected=False`,# the output remains identical to the undirected case.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Compute the preferential attachment score between Alice and Bob.with model.query() as select: person1, person2 = Person(), Person() similarity = graph.compute.preferential_attachment(person1, person2) response = select(person1.name, person2.name, alias(similarity, "preferential_attachment"))
print(response.results)# Output:# name name2 preferential_attachment# 0 Alice Alice 1# 1 Alice Bob 1# 2 Alice Carol 0# 3 Bob Alice 1# 4 Bob Bob 1# 5 Bob Carol 0# 6 Carol Alice 0# 7 Carol Bob 0# 8 Carol Carol 0
While many graph algorithms produce sparse results (node pairs with a zero score are omitted), the preferential_attachment algorithm returns dense results, meaning node pairs with a zero score are still returned.
For example, consider Alice and Carol. Even though Carol has no neighbors, the preferential_attachment algorithm still calculates a score (in this case, 0) for the pair (Alice, Carol).
If node1
or node2
is not a node in the graph, no exception is raised.
Instead, that object is filtered from the rule or query:
# Add a Company type to the model.Company = model.Type("Company")
# Add some companies to the model.with model.rule(): apple = Company.add(name="Apple") google = Company.add(name="Google")
# Create the union of the Person and Company types.PersonOrCompany = Person | Company
with model.query() as select: # Get all person and company objects. obj1, obj2 = PersonOrCompany(), PersonOrCompany() obj1 < obj2 # Ensure pairs are unique. Compares internal object IDs. # Compute the preferential attachment score between each pair of objects. # Objects that are not nodes in the graph are filtered. similarity = graph.compute.preferential_attachment(obj1, obj2) response = select(obj1.name, obj2.name, alias(similarity, "preferential_attachment"))
# Only rows for people are returned, since companies are not nodes in the graph.print(response.results)# Output:# name name2 preferential_attachment# 0 Alice Bob 1# 1 Carol Alice 0# 2 Carol Bob 0
Community Detection
Section titled “Community Detection”.infomap()
Section titled “.infomap()”Compute.infomap( node: Producer max_levels: int = 1, max_sweeps: int = 20, level_tolerance: float = 0.01, sweep_tolerance: float = 0.0001, teleportation_rate: float = 0.15, visit_rate_tolerance: float = 1e-15, randomization_seed: int | None = None,) -> Expression
Assign a community label to node
using the Infomap algorithm.
Infomap leverages principles from information theory to detect communities within networks.
It employs the map equation, a powerful tool that quantifies the information needed to describe
random walks on a network. This method aims to identify the most efficient encoding of network structure,
thereby optimizing for the most compact representation of community divisions.
Nodes assigned to the same community are more likely to interact or communicate with one another
as information flows through the network.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Only positive weights are supported. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description | Range |
---|---|---|---|
node | Producer | A node in the graph. | Vertex set. |
max_levels | int | The maximum number of levels at which to optimize. Default is 1 . | Positive integer. |
max_sweeps | int | The maximum number of iterations to run at each level. Default is 20 . | Non-negative integer. |
level_tolerance | float | The minimum change in the map equation required to continue to the next level. Default is 0.01 . | Small positive number or zero. |
sweep_tolerance | float | The minimum change in the map equation required to continue to the next sweep. Default is 0.0001 . | Small positive number or zero. |
teleportation_rate | float | The non-zero probability of teleporting to a random node. Default is 0.15 . | [1e-4, 1] . |
visit_rate_tolerance | float | The minimum change in the visit rate required to continue to the next sweep. Default is 1e-15 . | Small positive number. |
randomization_seed | int or None | The seed for the algorithm’s random number generator. Default is None . | None or non-negative integer. |
Returns
Section titled “Returns”Returns an Expression object that produces
the integer community label assigned by Infomap to node
as an integer value.
Example
Section titled “Example”Use .infomap()
to assign community labels to nodes in a graph using the Infomap algorithm.
You access the .infomap()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") evelyn = Person.add(name="Evelyn") alice.follows.add(bob) carol.follows.add(daniel)
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Find the community label for a single person using the Infomap algorithm.with model.query() as select: community = graph.compute.infomap(Person(name="Alice")) response = select(alias(community, "community_label"))
print(response.results)
# Output:# community_label# 0 2
# Find the community label for each person in the graph.with model.query() as select: person = Person() community = graph.compute.infomap(person) response = select(person.name, alias(community, "community_label"))
print(response.results)# name community_label# 0 Alice 2# 1 Bob 2# 2 Carol 1# 3 Daniel 1# 4 Evelyn 3
In this example, .infomap()
finds three communities in the graph:
Alice and Bob are in one community, and Carol and Daniel are in another.
Evelyn is an isolated node and has been assigned a unique community ID.
.label_propagation()
Section titled “.label_propagation()”Compute.label_propagation( node: Producer, max_sweeps: int = 20, randomization_seed: int | None = None) -> Expression
Assign a community label to node
using the label propagation algorithm.
Label propagation is an algorithm that begins by assigning each node a unique community label
and iteratively updates the label of each node to the most common label among its neighbors
until convergence or a maximum number of iterations is reached.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Only positive weights are supported. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description | Range |
---|---|---|---|
node | Producer | A node in the graph. | Vertex set. |
max_sweeps | int | The maximum number of iterations to run the label propagation algorithm. Default is 20 . | Non-negative integer. |
randomization_seed | int or None | The seed for the algorithm’s random number generator. Default is None . | None or non-negative integer. |
Returns
Section titled “Returns”Returns an Expression object that produces
the integer community label assigned by label propagation to node
.
Example
Section titled “Example”Use .label_propagation()
to assign a community label to a node in a graph.
You access the .label_propagation()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") evelyn = Person.add(name="Evelyn") alice.follows.add(bob) carol.follows.add(daniel)
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Find the community label for a single person using label propagation.with model.query() as select: person = Person(name="Alice") community = graph.compute.label_propagation(person) response = select(alias(community, "community_label"))
print(response.results)# Output:# community_label# 0 2
# Find the community label for each person in the graph.with model.query() as select: person = Person() community = graph.compute.label_propagation(person) response = select(person.name, alias(community, "community_label"))
print(response.results)# Output:# name community_label# 0 Alice 2# 1 Bob 2# 2 Carol 1# 3 Daniel 1# 4 Evelyn 3
In this example, .label_propagation()
identifies three communities: (1) Alice and Bob, (2) Carol and Daniel,
and (3) Evelyn as an isolated community.
.louvain()
Section titled “.louvain()”Compute.louvain( node: Producer, max_levels: int = 1, max_sweeps: int = 20, level_tolerance: float = 0.01, sweep_tolerance: float = 0.0001, randomization_seed: int | None = None) -> Expression
Assign a community label to node
using the Louvain method.
The Louvain algorithm is a hierarchical community detection technique that initially assigns each node
to its own community. It iteratively merges nodes and communities to maximize modularity, a metric that
evaluates the density of edges within communities relative to edges between them. The process continues
until no further improvements in modularity can be made, or the maximum number of iterations is reached.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | No | Currently not supported. |
Undirected | Yes | |
Weighted | Yes | Only positive weights are supported. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description | Range |
---|---|---|---|
node | Producer | A node in the graph. | Vertex set. |
max_levels | int | The maximum number of levels at which to optimize. Default is 1 . | Positive integer. |
max_sweeps | int | The maximum number of iterations to run at each level. Default is 20 . | Non-negative integer. |
level_tolerance | float | The minimum change in modularity required to continue to the next level. Default is 0.01 . | Small positive number or zero. |
sweep_tolerance | float | The minimum change in modularity required to continue to the next sweep. Default is 0.0001 . | Small positive number or zero. |
randomization_seed | int or None | The seed for the algorithm’s random number generator. Default is None . | None or non-negative integer. |
Returns
Section titled “Returns”Returns an Expression object that produces
the integer community label assigned to node
by the Louvain algorithm.
Example
Section titled “Example”Use .louvain()
to assign community labels to nodes in a graph using the Louvain algorithm.
You access the .louvain()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `friends` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") evelyn = Person.add(name="Evelyn") alice.friends.add(bob) carol.friends.add(daniel)
# Create an undirected graph with Person nodes and edges between friends.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Find the community label for a single person using Louvain.with model.query() as select: community = graph.compute.louvain(Person(name="Alice")) response = select(alias(community, "community_label"))
print(response.results)# Output:# community_label# 0 2
# Find the community label for each person using Louvain.with model.query() as select: person = Person() community = graph.compute.louvain(person) response = select(person.name, alias(community, "community_label"))
print(response.results)
# name community_label# 0 Alice 2# 1 Bob 2# 2 Carol 1# 3 Daniel 1# 4 Evelyn 3
In this example, .louvain()
finds three communities in the graph:
Alice and Bob are in one community, and Carol and Daniel are in another.
Evelyn is an isolated node and has been assigned a unique community ID.
.triangle_community()
Section titled “.triangle_community()”Compute.triangle_community(node: Producer) -> Expression
Assign a community label to node
using the percolation method.
The percolation method identifies communities within a graph by detecting densely connected clusters of nodes.
It operates by initially locating all triangles within the graph—groups of three interconnected nodes.
The process involves iteratively merging these triangles that share a common edge. This merging continues until no further
triangles can be combined, effectively revealing the network’s densely connected communities.
Nodes that are not part of any triangles are excluded. This method is only applicable to undirected graphs.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | No | Not applicable. |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Returns
Section titled “Returns”Returns an Expression object that produces
the community label of node
as an integer value.
Example
Section titled “Example”Use .triangle_community()
to assign a community label to a node in a graph.
You access the .triangle_community()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `friends` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") charlie = Person.add(name="Charlie") diana = Person.add(name="Diana") alice.friends.add(bob) bob.friends.add(charlie) charlie.friends.extend([alice, diana])
# Create an undirected graph with Person nodes and edges between followers.# Note that graphs are directed by default.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Find the community label for a single person using the percolation method.with model.query() as select: community = graph.compute.triangle_community(Person(name="Alice")) response = select(alias(community, "community_label"))
print(response.results)# Output:# community_label# 0 1
# Compute the community label for each person in the graph.with model.query() as select: person = Person() community = graph.compute.triangle_community(person) response = select(person.name, alias(community, "community_label"))
print(response.results)# Output:# name community_label# 0 Alice 1# 1 Bob 1# 2 Charlie 1
The above graph has edges from Alice to Bob, Bob to Charlie, Charlie to Alice, and Charlie to Diana. Alice, Bob, and Charlie form a triangle, but Diana is not part of any triangles. The percolation method assigns Alice and Bob to the same community. Diana is filtered out because she is not part of any triangles.
Use std.aggregate.count()
to count the number of communities identified in the graph:
from relationalai.std.aggregates import count
with model.query() as select: person = Person() community = graph.compute.triangle_community(person) response = select(alias(count(community), "num_communities"))
print(response.results)# Output:# num_communities# 0 1
Clustering
Section titled “Clustering”.avg_clustering_coefficient()
Preview
Section titled “.avg_clustering_coefficient() ”Compute.avg_clustering_coefficient() -> Expression
Compute the average clustering coefficient of all nodes in an undirected graph.
The average clustering coefficient is the average of the
local clustering coefficients for each node in the graph.
Values range from 0
to 1
.
Higher average clustering coefficients indicate nodes’ increased tendency to form triangles with neighbors.
Standard definitions of clustering coefficients primarily apply to undirected graphs. Directed graphs are not supported as a valid input.
Must be called in a rule or query context.
- Directed graphs are not supported.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | No | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Returns
Section titled “Returns”Returns an Expression object that produces the average clustering coefficient of all nodes in the graph as a floating-point value.
Example
Section titled “Example”Use .avg_clustering_coefficient()
to compute the average clustering coefficient of all nodes in an undirected graph.
You access the .avg_clustering_coefficient()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `friend` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") alice.friends.extend([bob, carol]) bob.friends.extend([alice, carol, daniel])
# Create an undirected graph with Person nodes and edges between friends.# This graph has four edges: Alice, Bob, and Carol form a triangle, and Daniel is only connected to Bob.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Compute the average clustering coefficient of the graph.with model.query() as select: acc = graph.compute.avg_clustering_coefficient() response = select(alias(acc, "avg_clustering_coefficient"))
print(response.results)# Output:# avg_clustering_coefficient# 0 0.583333
.is_triangle()
Section titled “.is_triangle()”Compute.is_triangle(node1: Producer, node2: Producer, node3: Producer) -> Expression
Filters node1
, node2
, and node3
for combinations of that form a triangle.
In an undirected graph, a triangle is a set of three nodes where each node is connected to the other two nodes.
In a directed graph, a triangle is a set of three nodes where there is an edge from the first node to the second node, an edge from the second node to the third node, and an edge from the third node to the first node.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node1 | Producer | A node in the graph. |
node2 | Producer | A node in the graph. |
node3 | Producer | A node in the graph. |
Returns
Section titled “Returns”An Expression object.
Example
Section titled “Example”Use .is_triangle()
to check if three nodes form a triangle in a graph.
You access the .is_triangle()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person typemodel = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") charlie = Person.add(name="Charlie") diana = Person.add(name="Diana") alice.follows.add(bob) bob.follows.add(charlie) charlie.follows.extend([alice, diana])
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by Default.# This graph has edges from Alice to Bob, Bob to Charlie, Charlie to Alice, and Charlie to Diana.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Find all nodes that form a trianglewith model.query() as select: # Get triples of Person objects. person1, person2, person3 = Person(), Person(), Person() # Filter triples based on whether they form a triangle. graph.compute.is_triangle(person1, person2, person3) # Select the names of people that form triangles. response = select(person1.name, person2.name, person3.name)
print(response.results)# Output:# name name2 name3# 0 Alice Bob Charlie# 1 Bob Charlie Alice# 2 Charlie Alice Bob
The output has three rows, but each row is a permutation of the same three nodes.
.local_clustering_coefficient()
Section titled “.local_clustering_coefficient()”Compute.local_clustering_coefficient(node: Producer) -> Expression
Compute the local clustering coefficient of a node in an undirected graph.
The local clustering coefficient of a node represents the proportion of
pairs among the node’s neighbors that are also connected to each other.
Values range from 0
to 1
, where 0
indicates none of the node’s neighbors are connected,
and 1
indicates that the node’s neighbors are fully connected, forming a clique.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | No | Not applicable. |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Returns
Section titled “Returns”Returns an Expression object that produces
the local clustering coefficient of node
as a floating-point value, calculated by the following formula:
local clustering coefficient = 2 * num_edges / (degree * (degree - 1))
Here, num_edges
is the total number of edges between the neighbors of node
and degree
is the degree of node
.
Values range from 0
to 1
, where 0
indicates none of the node’s neighbors are connected,
and 1
indicates that the node’s neighbors are fully connected, forming a clique.
Example
Section titled “Example”Use .local_clustering_coefficient()
to compute the local clustering coefficient of a node in an undirected graph.
You access the .local_clustering_coefficient()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `friend` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") alice.friends.extend([bob, carol]) bob.friends.extend([alice, carol, daniel])
# Create an undirected graph with Person nodes and edges between friends.# This graph has four edges: Alice, Bob, and Carol form a triangle, and Daniel is only connected to Bob.graph = Graph(model, undirected=True)graph.Node.extend(Person)graph.Edge.extend(Person.friends)
# Compute the local clustering coefficient of each person.with model.query() as select: person = Person() lcc = graph.compute.local_clustering_coefficient(person) response = select(person.name, alias(lcc, "clustering_coefficient"))
print(response.results)# Output:# name clustering_coefficient# 0 Alice 1.000000# 1 Bob 0.333333# 2 Carol 1.000000# 3 Daniel 0.000000
# Compute the local clustering coefficient of a specific person.with model.query() as select: lcc = graph.compute.local_clustering_coefficient(Person(name="Alice")) response = select(alias(lcc, "clustering_coefficient"))
print(response.results)# Output:# clustering_coefficient# 0 1.0
.num_triangles()
Section titled “.num_triangles()”Compute.num_triangles(node: Producer | None = None) -> Expression
Compute the number of unique triangles in the graph.
A triangle is a set of three nodes x
, y
, and z
such that
there is an edge between x
and y
, y
and z
, and z
and x
.
If node
is not None
, the number of unique triangles that node
is part of is computed.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer or None | A node in the graph. If not None , the number of unique triangles that node is part of is computed. Otherwise, the total number of unique triangles in the graph is computed. Default is None . |
Returns
Section titled “Returns”Returns an Expression object that produces
the number of unique triangles in the graph as an integer value, if node
is None
,
or the number of unique triangles that node
is part of as an integer value, if node
is not None
.
Example
Section titled “Example”Use .num_triangles()
to compute the number of unique triangles in a graph.
You access the .num_triangles()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") charlie = Person.add(name="Charlie") diana = Person.add(name="Diana") alice.follows.add(bob) bob.follows.add(charlie) charlie.follows.extend([alice, diana])
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Compute the number of unique triangles in the graph.with model.query() as select: num_triangles = graph.compute.num_triangles() response = select(alias(num_triangles, "num_triangles"))
print(response.results)# Output:# num_triangles# 0 1
# Compute the number of unique triangles that each node is part of.with model.query() as select: person = Person() num_triangles = graph.compute.num_triangles(person) response = select(person.name, alias(num_triangles, "num_triangles"))
print(response.results)# Output:# name num_triangles# 0 Alice 1# 1 Bob 1# 2 Charlie 1# 3 Diana 0
.triangles()
Section titled “.triangles()”Compute.triangles(node: Producer | None = None) -> tuple[Expression, Expression, Expression]
Find all unique triangles in the graph.
A triangle is a set of three nodes x
, y
, and z
such that
there is an edge between x
and y
, y
and z
, and z
and x
.
If node
is not None
, the unique triangles that node
is part of are computed.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer or None | A node in the graph. If not None , the unique triangles that node is part of are computed. Otherwise, the total number of unique triangles in the graph is computed. Default is None . |
Returns
Section titled “Returns”Returns a tuple (node1, node2, node3)
of three Expression objects that produce
triples of nodes that form unique triangles in the graph.
For undirected graphs, triples are produced so that the nodes are ordered in ascending order by their internal identifiers.
In directed graphs, .triangles()
produces triples of nodes such that node1 < node2
, node1 < node3
, and node2 != node3
.
This ensures that each triangle is unique based on the ordering of nodes and edge directions.
For instance, (1, 2, 3)
and (1, 3, 2)
denote distinct directed triangles.
Example
Section titled “Example”Use .triangles()
to find all unique triangles in a graph.
You access the .triangles()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") charlie = Person.add(name="Charlie") diana = Person.add(name="Diana") alice.follows.add(bob) bob.follows.add(charlie) charlie.follows.extend([alice, diana]) diana.follows.add(bob)
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.graph = Graph(model)graph.Edge.extend(Person.follows)
# Compute the unique triangles in the graph.with model.query() as select: person1, person2, person3 = graph.compute.triangles() response = select(person1.name, person2.name, person3.name)
print(response.results)# Output:# name name2 name3# 0 Charlie Alice Bob# 1 Charlie Diana Bob
# Compute the unique triangles that include Alice.with model.query() as select: alice = Person(name="Alice") person1, person2, person3 = graph.compute.triangles(alice) response = select(person1.name, person2.name, person3.name)
print(response.results)# Output:# name name2 name3# 0 Charlie Alice Bob
Connectivity
Section titled “Connectivity”.is_connected()
Preview
Section titled “.is_connected() ”Compute.is_connected() -> Expression
Check if the graph is connected. A graph is connected if every node is reachable from every other node in the undirected version of the graph. For directed graphs, connected is the same as weakly connected. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Returns
Section titled “Returns”Returns an Expression object that filters connected graphs.
Example
Section titled “Example”Use .is_connected()
to check if a graph is connected.
You access the .is_connected()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork1000")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.follows.add(bob)
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.# This graph has one edge from Alice to Bob. Carol is not connected to anyone.graph = Graph(model, with_isolated_nodes=True)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Is the graph connected?with model.query() as select: with model.match() as connected: with graph.compute.is_connected(): connected.add(True) with model.case(): connected.add(False) response = select(alias(connected, "is_connected"))
print(response.results)# Output:# is_connected# 0 False
.is_reachable()
Section titled “.is_reachable()”Compute.is_reachable(node1: Producer, node2: Producer) -> Expression
Check if node2
is reachable from node1
in the graph.
One node is reachable from another if there is a path from the first node to the second.
Every node is reachable from itself.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Returns
Section titled “Returns”Returns an Expression that filters pairs of
nodes in which there is a path from node1
to node2
.
Example
Section titled “Example”Use .is_reachable()
to check if one node is reachable from another in a graph.
You access the .is_reachable()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.follows.add(bob) bob.follows.add(carol)
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.# This graph has edges from Alice to Bob and Bob to Carol.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Can Alice reach Carol?with model.query() as select: alice = Person(name="Alice") carol = Person(name="Carol") with model.match() as reachable: with model.case(): graph.compute.is_reachable(alice, carol) reachable.add(True) with model.case(): reachable.add(False) response = select(alias(reachable, "is_reachable"))
print(response.results)# Output:# is_reachable# 0 True
# Who can reach Alice?with model.query() as select: person = Person() graph.compute.is_reachable(person, Person(name="Alice")) response = select(person.name)
# The only person that can reach Alice is herself.print(response.results)# Output:# name# 0 Alice
To find all nodes reachable from a given node, use .reachable_from()
.
Use .distance()
to find the shortest path length between two nodes.
.reachable_from()
Section titled “.reachable_from()”Compute.reachable_from(node: Producer) -> Expression
Find all nodes reachable from node
in a graph.
One node is reachable from another if there is a path from the first node to the second.
Every node is reachable from itself.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Returns
Section titled “Returns”Returns an Expression object that produces
model objects that are reachable from node
.
Example
Section titled “Example”Use .reachable_from()
to find all nodes reachable from a node in a graph.
You access the .reachable_from()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.follows.add(bob) bob.follows.add(carol)
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.# This graph has edges from Alice to Bob and Bob to Carol.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Who is reachable from Alice?with model.query() as select: reachable = graph.compute.reachable_from(Person(name="Alice")) response = select(reachable.name)
print(response.results)# Output:# name# 0 Alice# 1 Bob# 2 Carol
In the example above, both Bob and Carol are reachable from Alice because there is a path from Alice to Bob and a path from Alice to Carol that passes through Bob. Every node is reachable from itself, so Alice is also reachable from Alice.
To check if one node is reachable from another, use .is_reachable()
.
Use .distance()
to find the shortest path length between two nodes.
.weakly_connected_component()
Section titled “.weakly_connected_component()”Compute.weakly_connected_component(node: Producer) -> Expression
Find the weakly connected component containing node
in a graph.
The weakly connected component of a node is the set of nodes that are
reachable from the node in an undirected version of the graph.
Components are identified by the node in the component with the smallest internal identifier.
In an undirected graph, the weakly connected component is the same as the connected component.
Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Directed | Yes | Operates on the undirected version of the graph. |
Undirected | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Parameters
Section titled “Parameters”Name | Type | Description |
---|---|---|
node | Producer | A node in the graph. |
Returns
Section titled “Returns”An Expression object that produces
the representative node of the weakly connected component containing node
.
Component representatives are the nodes with the smallest internal identifiers in the component.
Example
Section titled “Example”Use .weakly_connected_component()
to find the weakly connected component containing a node in a graph.
You access the .weakly_connected_component()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.follows.add(bob)
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.# This graph has one edge from Alice to Bob. Carol is not connected to anyone.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Compute the weakly connected component for each person in the graph.with model.query() as select: person = Person() component = graph.compute.weakly_connected_component(person) response = select(person.name, alias(component.name, "component_representative"))
print(response.results)# Output:# name component_representative# 0 Alice Alice# 1 Bob Alice# 2 Carol Carol
Component representatives are the objects with the smallest internal identifiers in the component. Above, Alice and Bob are in the same component, with Alice as the representative. Carol is in a component by herself because she is not connected to anyone.
Use std.aggregates.count
to count the number of weakly connected components in a graph:
from relationalai.std.aggregates import count
with model.query() as select: person = Person() component = graph.compute.weakly_connected_component(person) response = select(alias(count(component), "num_components"))
print(response.results)# Output:# num_components# 0 2
Distance
Section titled “Distance”.diameter_range()
Preview
Section titled “.diameter_range() ”Compute.diameter_range() -> tuple[Expression, Expression]
Compute lower and upper bounds for the maximum shortest-path distance between any two path-connected pairs of nodes. Edge weights are ignored in weighted graphs. If the graph is disconnected, the upper and lower bounds of the connected component with the most nodes is returned. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Undirected | Yes | |
Directed | Yes | |
Weighted | Yes | Weights are ignored. |
Unweighted | Yes |
Returns
Section titled “Returns”Returns a tuple of two Expression objects that produce the lower and upper bounds for the diameter of the graph. In the case of directed graphs, these bounds apply only to pairs of nodes where a path exists from one to the other.
The diameter range is computed by selecting a subset of highest degree nodes and, for each node, finding the length of the longest shortest path from that node to the rest of the graph. The minimum and maximum of these lengths are returned as the lower and upper bounds of the diameter, respectively.
Example
Section titled “Example”Use .diameter_range()
to compute the range of possible diameters in a graph.
You access the .diameter_range()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") alice.follows.add(bob) bob.follows.add(carol)
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.# This graph has edges from Alice to Bob and Bob to Carol.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Compute the diameter range of the graph.with model.query() as select: diam_min, diam_max = graph.compute.diameter_range() response = select(alias(diam_min, "min"), alias(diam_max, "max"))
print(response.results)# Output:# min max# 0 2 2
In cases like this where the lower and upper bounds are the same, the diameter of the graph is known exactly. This may not always be the case, especially for larger and more complex graphs.
.distance()
Section titled “.distance()”Compute.distance(node1: Producer, node2: Producer) -> Expression
This algorithm computes the shortest path length between two nodes in a graph. Must be called in a rule or query context.
Supported Graph Types
Section titled “Supported Graph Types”Graph Type | Supported | Notes |
---|---|---|
Undirected | Yes | |
Directed | Yes | |
Weighted | Yes | |
Unweighted | Yes |
Parameters
Section titled “Parameters”Returns
Section titled “Returns”Returns an Expression object that produces
the shortest path length between node1
and node2
as an integer value.
Example (Unweighted Graphs)
Section titled “Example (Unweighted Graphs)”Use .distance()
to compute the shortest path length between two nodes in a graph.
You access the .distance()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with a Person type.model = rai.Model("socialNetwork")Person = model.Type("Person")
# Add some people to the model and connect them with a multi-valued `follows` property.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") alice.follows.add(bob) bob.follows.add(carol) carol.follows.add(daniel)
# Create a directed graph with Person nodes and edges between followers.# Note that graphs are directed by default.# This graph has three edges: one from Alice to Bob, Bob to Carol, and Carol to Daniel.graph = Graph(model)graph.Node.extend(Person)graph.Edge.extend(Person.follows)
# Find the distance between Alice and Daniel.with model.query() as select: dist = graph.compute.distance(Person(name="Alice"), Person(name="Daniel")) response = select(alias(dist, "distance"))
print(response.results)# Output:# distance# 0 3
# Find all nodes at distance at most two from Alice.with model.query() as select: node = Person() dist = graph.compute.distance(Person(name="Alice"), node) dist <= 2 response = select(node.name, alias(dist, "distance"))
print(response.results)# Output:# name distance# 0 Alice 0# 1 Bob 1# 2 Carol 2
Note that the distance between a node and itself is 0
.
Example (Weighted Graphs)
Section titled “Example (Weighted Graphs)”Use .distance()
to compute the shortest path length between two nodes in a weighted graph.
You access the .distance()
method from a Graph
object’s
.compute
attribute:
import relationalai as raifrom relationalai.std import aliasfrom relationalai.std.graphs import Graph
# Create a model named "socialNetwork" with Person and Friendship types.model = rai.Model("socialNetwork")Person = model.Type("Person")Friendship = model.Type("Friendship")
# Add some people to the model and connect them with the Friendship type.with model.rule(): alice = Person.add(name="Alice") bob = Person.add(name="Bob") carol = Person.add(name="Carol") daniel = Person.add(name="Daniel") Friendship.add(person1=alice, person2=bob, strength=1.0) Friendship.add(person1=bob, person2=carol, strength=0.5) Friendship.add(person1=carol, person2=daniel, strength=0.75)
# Create a directed graph with Person nodes and edges between friends.# Note that graphs are directed by default.# This graph has three edges: one from Alice to Bob, Bob to Carol, and Carol to Daniel.# The edges are weighted by the strength of each friendship.graph = Graph(model, weighted=True)graph.Node.extend(Person)with model.rule(): friendship = Friendship() graph.Edge.add(friendship.person1, friendship.person2, weight=friendship.strength)
# Find the weighted distance between Alice and Daniel.with model.query() as select: dist = graph.compute.distance(Person(name="Alice"), Person(name="Daniel")) response = select(alias(dist, "distance"))
print(response.results)# Output:# distance# 0 2.25
# Find all nodes with weighted distance at most two from Alice.with model.query() as select: node = Person() dist = graph.compute.distance(Person(name="Alice"), node) dist <= 2.0 response = select(node.name, alias(dist, "distance"))
print(response.results)# Output:# name distance# 0 Alice 0.0# 1 Bob 1.0# 2 Carol 1.5