DSL Reference

Complete reference for the py3plex DSL (Domain-Specific Language) for querying multilayer networks.

Note

For task-oriented usage, see How to Query Multilayer Graphs with the SQL-like DSL. This page is a complete reference with all syntax and operators.

Overview

The py3plex DSL provides two interfaces:

  1. String syntax: SQL-like queries for quick exploration

  2. Builder API: Type-safe Python interface for production code

String Syntax Reference

Basic Structure

SELECT <target> [FROM <layers>] [WHERE <conditions>] [COMPUTE <metrics>] [ORDER BY <field>] [LIMIT <n>]

Targets

  • nodes — Select nodes

  • edges — Select edges (experimental)

Layer Selection

FROM layer="layer_name"
FROM layers IN ("layer1", "layer2")

Conditions

Operators:

  • = — Equal

  • > — Greater than

  • < — Less than

  • >= — Greater than or equal

  • <= — Less than or equal

  • != — Not equal

Logical operators:

  • AND — Both conditions must be true

  • OR — Either condition must be true

Examples:

WHERE degree > 5
WHERE layer="friends" AND degree > 3
WHERE degree > 5 OR betweenness_centrality > 0.1

Compute Clause

Calculate metrics for selected nodes:

COMPUTE degree
COMPUTE degree COMPUTE betweenness_centrality
COMPUTE clustering

Available metrics:

  • degree — Node degree

  • betweenness_centrality — Betweenness centrality

  • closeness_centrality — Closeness centrality

  • clustering — Clustering coefficient

  • pagerank — PageRank score

  • layer_count — Number of layers node appears in

See Algorithm Roadmap for complete metric list.

Order By

ORDER BY degree
ORDER BY -degree  # Descending (prefix with -)
ORDER BY betweenness_centrality

Limit

LIMIT 10
LIMIT 100

Complete Examples

from py3plex.dsl import execute_query

# Get high-degree nodes
result = execute_query(
    network,
    'SELECT nodes WHERE degree > 5'
)

# Get nodes from specific layer
result = execute_query(
    network,
    'SELECT nodes FROM layer="friends" '
    'WHERE degree > 3 '
    'COMPUTE betweenness_centrality '
    'ORDER BY -betweenness_centrality '
    'LIMIT 10'
)

Builder API Reference

Import

from py3plex.dsl import Q, L

Query Construction

Start a query:

Q.nodes()     # Select nodes
Q.edges()     # Select edges (experimental)

Layer Selection

Single layer:

Q.nodes().from_layers(L["friends"])

Multiple layers (union):

Q.nodes().from_layers(L["friends"] + L["work"])

# Or use the new LayerSet algebra:
Q.nodes().from_layers(L["friends | work"])

Layer intersection:

Q.nodes().from_layers(L["friends"] & L["work"])

Advanced Layer Set Algebra:

# All layers except coupling
Q.nodes().from_layers(L["* - coupling"])

# Complex expressions with set operations
Q.nodes().from_layers(L["(social | work) & ~bots"])

# Named groups for reuse
from py3plex.dsl import LayerSet
LayerSet.define_group("bio", LayerSet("ppi") | LayerSet("gene"))
Q.nodes().from_layers(LayerSet("bio"))

See also

For complete documentation on layer set algebra including all operators, string parsing, named groups, and real-world examples, see: Layer Set Algebra

Filtering

Comparison operators:

Q.nodes().where(degree__gt=5)       # Greater than
Q.nodes().where(degree__gte=5)      # Greater than or equal
Q.nodes().where(degree__lt=5)       # Less than
Q.nodes().where(degree__lte=5)      # Less than or equal
Q.nodes().where(degree__eq=5)       # Equal
Q.nodes().where(degree__ne=5)       # Not equal

Multiple conditions:

Q.nodes().where(
    degree__gt=5,
    layer_count__gte=2
)

Computing Metrics

Q.nodes().compute("degree")
Q.nodes().compute("degree", "betweenness_centrality")

Sorting

Q.nodes().order_by("degree")           # Ascending
Q.nodes().order_by("-degree")          # Descending

Limiting

Q.nodes().limit(10)

Execution

result = Q.nodes().execute(network)

Chaining

All methods can be chained:

result = (
    Q.nodes()
     .from_layers(L["friends"])
     .where(degree__gt=5)
     .compute("betweenness_centrality")
     .order_by("-betweenness_centrality")
     .limit(10)
     .execute(network)
)

Temporal Queries

Filter by Time Point

# String syntax
result = execute_query(
    network,
    'SELECT nodes AT "2024-01-15T10:00:00"'
)

# Builder API
result = (
    Q.nodes()
     .at("2024-01-15T10:00:00")
     .execute(network)
)

Filter by Time Range

# String syntax
result = execute_query(
    network,
    'SELECT nodes DURING "2024-01-01" TO "2024-01-31"'
)

# Builder API
result = (
    Q.nodes()
     .during("2024-01-01", "2024-01-31")
     .execute(network)
)

Temporal Edge Attributes

Edges can have temporal attributes:

  • t — Point in time (ISO 8601 timestamp)

  • t_start and t_end — Time range

See Working with Networks for creating temporal networks.

Grouping and Coverage Queries

Per-Layer Grouping

Group results by layer and apply per-group operations:

# Group by layer
result = (
    Q.nodes()
     .from_layers(L["*"])
     .compute("degree")
     .per_layer()              # Sugar for .group_by("layer")
        .top_k(5, "degree")    # Top 5 per layer
     .end_grouping()
     .execute(network)
)

Top-K Per Group

Select top-k items per group (requires prior grouping):

# Top 10 highest-degree nodes per layer
result = (
    Q.nodes()
     .from_layers(L["*"])
     .compute("degree", "betweenness_centrality")
     .per_layer()
        .top_k(10, "degree")
     .end_grouping()
     .execute(network)
)

Coverage Filtering

Filter based on presence across groups:

Mode: “all” — Keep items appearing in ALL groups (intersection)

# Nodes that are top-5 hubs in ALL layers
multi_hubs = (
    Q.nodes()
     .from_layers(L["*"])
     .compute("betweenness_centrality")
     .per_layer()
        .top_k(5, "betweenness_centrality")
     .end_grouping()
     .coverage(mode="all")
     .execute(network)
)

Mode: “any” — Keep items appearing in AT LEAST ONE group (union)

# Nodes that are top-5 in any layer
any_hubs = (
    Q.nodes()
     .from_layers(L["*"])
     .compute("degree")
     .per_layer()
        .top_k(5, "degree")
     .end_grouping()
     .coverage(mode="any")
     .execute(network)
)

Mode: “at_least” — Keep items appearing in at least K groups

# Nodes in top-10 of at least 2 layers
two_layer_hubs = (
    Q.nodes()
     .from_layers(L["*"])
     .compute("degree")
     .per_layer()
        .top_k(10, "degree")
     .end_grouping()
     .coverage(mode="at_least", k=2)
     .execute(network)
)

Mode: “exact” — Keep items appearing in exactly K groups

# Layer specialists: top-5 in exactly 1 layer
specialists = (
    Q.nodes()
     .from_layers(L["*"])
     .compute("betweenness_centrality")
     .per_layer()
        .top_k(5, "betweenness_centrality")
     .end_grouping()
     .coverage(mode="exact", k=1)
     .execute(network)
)

Wildcard Layer Selection

Use L["*"] to select all layers:

# All layers
Q.nodes().from_layers(L["*"])

# All layers except "bots"
Q.nodes().from_layers(L["*"] - L["bots"])

# Layer algebra still works
Q.nodes().from_layers((L["*"] - L["spam"]) & L["verified"])

General Grouping

Group by arbitrary attributes (not just layer):

# Group by multiple attributes
result = (
    Q.nodes()
     .compute("degree", "community")
     .group_by("layer", "community")
        .top_k(3, "degree")
     .end_grouping()
     .execute(network)
)

Limitations

  • Coverage filtering is currently supported only for node queries

  • Edge queries with coverage will raise a clear DslExecutionError

  • Grouping requires computed attributes or inherent node properties (like layer)

Working with Results

Result Object

Query results are dictionaries mapping nodes to their computed attributes:

result = Q.nodes().compute("degree").execute(network)

# Access individual node
node = ('Alice', 'friends')
degree = result[node]['degree']

# Iterate
for node, data in result.items():
    print(f"{node}: {data}")

Convert to Pandas

df = result.to_pandas()
print(df.head())

Extensibility

Custom Operators

Register custom DSL operators:

from py3plex.dsl import register_operator

@register_operator('my_metric')
def my_custom_metric(context, node):
    """Compute custom metric for a node."""
    # Your implementation
    return value

See Architecture and Design for plugin development.

Performance Considerations

  1. Compute metrics once: Don’t recompute in multiple queries

  2. Filter early: Use WHERE before COMPUTE

  3. Limit results: Use LIMIT for large networks

  4. Layer-specific: Query single layers when possible

Next Steps