CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-igraph

High performance graph data structures and algorithms for Python

Pending
Overview
Eval results
Files

data-types.mddocs/

Core Data Types

Essential data structures including matrices, layouts, and utility classes that support graph operations and analysis. These foundational types enable efficient computation and data manipulation in igraph.

Capabilities

Matrix Operations

Simple matrix data type for coordinate pairs, adjacency matrices, and linear algebra operations.

class Matrix:
    def __init__(self, data=None, nrow=0, ncol=0):
        """
        Create matrix from data or dimensions.
        
        Parameters:
        - data: list/nested list, matrix data
        - nrow: int, number of rows (if data is flat list)
        - ncol: int, number of columns (if data is flat list)
        """

    @classmethod
    def Fill(cls, value, shape):
        """
        Create matrix filled with specific value.
        
        Parameters:
        - value: fill value
        - shape: tuple (nrows, ncols)
        
        Returns:
        Matrix filled with value
        """

    @classmethod
    def Zero(cls, shape):
        """
        Create zero matrix.
        
        Parameters:
        - shape: tuple (nrows, ncols)
        
        Returns:
        Matrix filled with zeros
        """

    @classmethod
    def Identity(cls, n):
        """
        Create identity matrix.
        
        Parameters:
        - n: int, matrix size (n x n)
        
        Returns:
        Identity matrix
        """

    def __getitem__(self, key):
        """Access matrix elements by [i, j] or [i]."""

    def __setitem__(self, key, value):
        """Set matrix elements."""

    @property
    def shape(self):
        """Get matrix dimensions as (nrows, ncols)."""

    def transpose(self):
        """
        Get transposed matrix.
        
        Returns:
        Matrix, transposed version
        """

Usage Examples:

from igraph.datatypes import Matrix

# Create matrix from nested list
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
m1 = Matrix(data)
print(f"Matrix shape: {m1.shape}")

# Create from flat list with dimensions
flat_data = [1, 2, 3, 4, 5, 6]
m2 = Matrix(flat_data, nrow=2, ncol=3)

# Create special matrices
zero_matrix = Matrix.Zero((3, 3))
identity = Matrix.Identity(4)
filled = Matrix.Fill(7.5, (2, 4))

# Access elements
print(f"Element [1,2]: {m1[1, 2]}")
print(f"Row 0: {m1[0]}")

# Set elements
m1[0, 0] = 99
m1[2] = [10, 11, 12]  # Set entire row

# Matrix operations
transposed = m1.transpose()
print(f"Original shape: {m1.shape}")
print(f"Transposed shape: {transposed.shape}")

# Use with graph operations
import igraph as ig
g = ig.Graph.Ring(5)

# Convert adjacency to Matrix
adj_list = g.get_adjacency()
adj_matrix = Matrix(adj_list)
print(f"Adjacency matrix shape: {adj_matrix.shape}")

Layout Coordinates

Represents graph layout coordinates in n-dimensional space with transformation methods.

class Layout:
    def __init__(self, coords=None, dim=2):
        """
        Create layout from coordinates.
        
        Parameters:
        - coords: list of tuples/lists, vertex coordinates
        - dim: int, number of dimensions
        """

    def __len__(self):
        """Get number of vertices in layout."""

    def __getitem__(self, key):
        """Access coordinates by vertex index."""

    def __setitem__(self, key, value):
        """Set coordinates for vertex."""

    @property
    def coords(self):
        """Get all coordinates as list of tuples."""

    def copy(self):
        """
        Create independent copy of layout.
        
        Returns:
        Layout, deep copy
        """

    def scale(self, *args):
        """
        Scale layout coordinates.
        
        Parameters:
        - *args: scaling factors (single value or per-dimension)
        
        Returns:
        None (modifies in place)
        """

    def translate(self, *args):
        """
        Translate layout coordinates.
        
        Parameters:
        - *args: translation offsets per dimension
        
        Returns:
        None (modifies in place)
        """

    def rotate(self, angle, dim1=0, dim2=1):
        """
        Rotate layout in specified plane.
        
        Parameters:
        - angle: float, rotation angle in radians
        - dim1, dim2: int, dimensions defining rotation plane
        
        Returns:
        None (modifies in place)
        """

    def mirror(self, dim):
        """
        Mirror layout along specified dimension.
        
        Parameters:
        - dim: int, dimension to mirror (0=x, 1=y, etc.)
        
        Returns:
        None (modifies in place)
        """

    def fit_into(self, bbox, keep_aspect_ratio=True):
        """
        Fit layout into bounding box.
        
        Parameters:
        - bbox: BoundingBox or (left, top, right, bottom)
        - keep_aspect_ratio: bool, preserve proportions
        
        Returns:
        None (modifies in place)
        """

    def centroid(self):
        """
        Calculate layout centroid.
        
        Returns:
        tuple, center coordinates
        """

    def boundaries(self):
        """
        Get layout boundaries.
        
        Returns:
        tuple, (min_coords, max_coords) for each dimension
        """

Usage Examples:

from igraph.layout import Layout
from igraph.drawing import BoundingBox
import math

# Create layout manually
coords = [(0, 0), (1, 0), (1, 1), (0, 1), (0.5, 0.5)]
layout = Layout(coords)

# Access and modify coordinates
print(f"Vertex 0 position: {layout[0]}")
layout[0] = (0.1, 0.1)  # Move vertex slightly

# Layout transformations
layout_copy = layout.copy()

# Scale uniformly
layout_copy.scale(2.0)  # Double all coordinates

# Scale per dimension
layout_copy.scale(1.5, 0.8)  # Stretch x, compress y

# Translation
layout_copy.translate(10, 5)  # Move right 10, up 5

# Rotation (45 degrees)
layout_copy.rotate(math.pi/4)  # Rotate around origin

# Mirror horizontally
layout_copy.mirror(0)  # Flip along x-axis

# Fit into bounding box
bbox = BoundingBox(0, 0, 800, 600)
layout_copy.fit_into(bbox, keep_aspect_ratio=True)

# Layout analysis
centroid = layout.centroid()
boundaries = layout.boundaries()
print(f"Layout centroid: {centroid}")
print(f"Layout boundaries: {boundaries}")

# Create 3D layout
coords_3d = [(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]
layout_3d = Layout(coords_3d, dim=3)

# 3D rotation (around z-axis)
layout_3d.rotate(math.pi/6, dim1=0, dim2=1)  # Rotate x-y plane

Census and Analysis Results

Specialized data types for storing structural analysis results.

class DyadCensus:
    def __init__(self, mutual=0, asymmetric=0, null=0):
        """
        Dyad census results for directed graphs.
        
        Parameters:
        - mutual: int, number of mutual dyads
        - asymmetric: int, number of asymmetric dyads  
        - null: int, number of null dyads
        """

    @property
    def mutual(self):
        """Number of mutual dyads (edges in both directions)."""

    @property  
    def asymmetric(self):
        """Number of asymmetric dyads (edge in one direction)."""

    @property
    def null(self):
        """Number of null dyads (no edges between vertices)."""

class TriadCensus:
    def __init__(self, **kwargs):
        """
        Triad census results with counts for all 16 triad types.
        
        Triad types (MAN notation):
        - 003: empty triad
        - 012, 102, 021D, 021U, 021C: various asymmetric patterns
        - 111D, 111U: mixed patterns
        - 030T, 030C: transitive and cyclic triads
        - 201: partially connected
        - 120D, 120U, 120C: different orientations
        - 210: almost complete
        - 300: complete triad
        """

    def __getattribute__(self, name):
        """Access triad counts by type name (e.g., census.003)."""

Usage Examples:

import igraph as ig

# Dyad census for directed graph
g_dir = ig.Graph.Erdos_Renyi(20, 30, directed=True)
dyad_census = g_dir.dyad_census()

print(f"Mutual dyads: {dyad_census.mutual}")
print(f"Asymmetric dyads: {dyad_census.asymmetric}")  
print(f"Null dyads: {dyad_census.null}")
print(f"Total possible dyads: {dyad_census.mutual + dyad_census.asymmetric + dyad_census.null}")

# Verify total
n = g_dir.vcount()
expected_total = n * (n - 1) // 2
print(f"Expected total: {expected_total}")

# Triad census
triad_census = g_dir.triad_census()

# Access specific triad types
print(f"Empty triads (003): {triad_census.__getattribute__('003')}")
print(f"Complete triads (300): {triad_census.__getattribute__('300')}")
print(f"Transitive triads (030T): {triad_census.__getattribute__('030T')}")

# Analyze triad distribution
triad_types = ['003', '012', '102', '021D', '021U', '021C',
               '111D', '111U', '030T', '030C', '201',  
               '120D', '120U', '120C', '210', '300']

total_triads = 0
for triad_type in triad_types:
    count = triad_census.__getattribute__(triad_type)
    total_triads += count
    if count > 0:
        print(f"{triad_type}: {count}")

print(f"Total triads: {total_triads}")
expected_triads = n * (n - 1) * (n - 2) // 6
print(f"Expected total: {expected_triads}")

Unique ID Generation

Dictionary-like class for assigning unique IDs to names or objects.

class UniqueIdGenerator:
    def __init__(self, id_generator=None, initial=None):
        """
        Create unique ID generator.
        
        Parameters:
        - id_generator: function, custom ID generation function
        - initial: dict, initial name-to-ID mappings
        """

    def __getitem__(self, name):
        """
        Get ID for name, creating new ID if needed.
        
        Parameters:
        - name: object, name/key to get ID for
        
        Returns:
        int, unique ID for name
        """

    def __contains__(self, name):
        """Check if name already has assigned ID."""

    def __len__(self):
        """Get number of assigned IDs."""

    def keys(self):
        """Get all names with assigned IDs."""

    def values(self):
        """Get all assigned ID values."""

    def items(self):
        """Get (name, ID) pairs."""

    def reverse_dict(self):
        """
        Get reverse mapping from IDs to names.
        
        Returns:
        dict, ID-to-name mapping
        """

Usage Examples:

from igraph.datatypes import UniqueIdGenerator

# Create ID generator for vertex names
id_gen = UniqueIdGenerator()

# Assign IDs to names
names = ["Alice", "Bob", "Carol", "Alice", "Dave", "Bob"]
vertex_ids = []

for name in names:
    vertex_id = id_gen[name]  # Gets existing ID or creates new one
    vertex_ids.append(vertex_id)

print(f"Name to ID mapping: {list(id_gen.items())}")
print(f"Vertex IDs: {vertex_ids}")  # [0, 1, 2, 0, 3, 1]

# Check if name exists
print(f"'Alice' has ID: {'Alice' in id_gen}")
print(f"'Eve' has ID: {'Eve' in id_gen}")

# Get reverse mapping
reverse = id_gen.reverse_dict()
print(f"ID to name: {reverse}")

# Use with custom ID generation
def custom_id_gen():
    """Generate IDs starting from 100."""
    counter = 100
    while True:
        yield counter
        counter += 1

custom_gen = UniqueIdGenerator(id_generator=custom_id_gen().__next__)
custom_gen["first"] = None  # Triggers ID generation
custom_gen["second"] = None
print(f"Custom IDs: {list(custom_gen.items())}")

# Pre-populate with initial mappings
initial_map = {"root": 0, "admin": 1}
preset_gen = UniqueIdGenerator(initial=initial_map)
preset_gen["user1"]  # Gets ID 2
preset_gen["user2"]  # Gets ID 3
print(f"Preset generator: {list(preset_gen.items())}")

# Use in graph construction
def build_graph_from_edges(edge_list):
    """Build graph from string-based edge list."""
    id_gen = UniqueIdGenerator()
    numeric_edges = []
    
    for source, target in edge_list:
        source_id = id_gen[source]
        target_id = id_gen[target]
        numeric_edges.append((source_id, target_id))
    
    g = ig.Graph(edges=numeric_edges, directed=False)
    
    # Add vertex names
    reverse_map = id_gen.reverse_dict()
    g.vs["name"] = [reverse_map[i] for i in range(len(id_gen))]
    
    return g

# Example usage
string_edges = [
    ("Alice", "Bob"),
    ("Bob", "Carol"), 
    ("Carol", "Dave"),
    ("Alice", "Dave")
]

g = build_graph_from_edges(string_edges)
print(f"Graph vertices: {g.vs['name']}")
print(f"Graph edges: {g.get_edgelist()}")

Geometric Utilities

Geometric classes for visualization and spatial operations.

class BoundingBox:
    def __init__(self, *args):
        """
        Create bounding box.
        
        Parameters:
        - *args: (left, top, right, bottom) or ((left, top), (right, bottom))
        """

    @property
    def left(self):
        """Left coordinate."""

    @property
    def top(self):
        """Top coordinate."""

    @property
    def right(self):
        """Right coordinate."""

    @property
    def bottom(self):
        """Bottom coordinate."""

    @property
    def width(self):
        """Bounding box width."""

    @property
    def height(self):
        """Bounding box height."""

    def area(self):
        """Calculate area."""

    def contains(self, point):
        """Check if point is inside bounding box."""

class Point:
    def __init__(self, x=0, y=0):
        """
        Create 2D point.
        
        Parameters:
        - x, y: float, coordinates
        """

    def distance(self, other):
        """Calculate distance to another point."""

class Rectangle:
    def __init__(self, corner1, corner2=None, width=None, height=None):
        """
        Create rectangle from corners or corner + dimensions.
        
        Parameters:
        - corner1: Point or tuple, first corner
        - corner2: Point or tuple, opposite corner (OR use width/height)
        - width: float, rectangle width
        - height: float, rectangle height
        """

    def area(self):
        """Calculate rectangle area."""

    def contains(self, point):
        """Check if point is inside rectangle."""

Usage Examples:

from igraph.drawing.utils import BoundingBox, Point, Rectangle

# Bounding box operations
bbox1 = BoundingBox(0, 0, 100, 80)
bbox2 = BoundingBox((10, 5), (90, 75))  # Alternative syntax

print(f"Box dimensions: {bbox1.width} x {bbox1.height}")
print(f"Box area: {bbox1.area()}")

# Point operations
p1 = Point(25, 30)
p2 = Point(75, 60)

distance = p1.distance(p2)
print(f"Distance between points: {distance:.2f}")

# Check containment
is_inside = bbox1.contains(p1)
print(f"Point {p1.x}, {p1.y} inside bbox: {is_inside}")

# Rectangle operations
rect1 = Rectangle(Point(10, 10), Point(50, 40))
rect2 = Rectangle(Point(20, 20), width=30, height=25)

print(f"Rectangle 1 area: {rect1.area()}")
print(f"Rectangle 2 contains (30, 30): {rect2.contains(Point(30, 30))}")

# Use in layout fitting
layout = ig.Graph.Ring(8).layout_circle()
target_bbox = BoundingBox(50, 50, 750, 550)

# Fit layout into bounding box
layout.fit_into(target_bbox, keep_aspect_ratio=True)

# Verify all points are within bounds
for i, (x, y) in enumerate(layout):
    point = Point(x, y)
    if not target_bbox.contains(point):
        print(f"Point {i} outside bounds: ({x:.1f}, {y:.1f})")

Install with Tessl CLI

npx tessl i tessl/pypi-igraph

docs

analysis.md

clustering.md

community.md

data-types.md

graph-creation.md

graph-structure.md

index.md

io-formats.md

layout.md

sequences.md

visualization.md

tile.json