pylinkage.hypergraph package

Submodules

pylinkage.hypergraph.core module

Core graph elements for the hypergraph representation.

This module provides the fundamental data structures for representing planar linkages as hypergraphs: Node, Edge, and Hyperedge.

These are pure topological elements - they define structure and connectivity only. Dimensional data (positions, distances, angles) is stored separately in the Dimensions class (see pylinkage.dimensions).

class pylinkage.hypergraph.core.Edge(id: str, source: str, target: str)

Bases: object

A binary connection between two nodes (topology only).

Edges represent rigid links connecting exactly two joints. For N-way rigid bodies (N > 2), use Hyperedge instead.

Distance constraints are stored separately in a Dimensions object.

Variables:
  • id (str) – Unique identifier for this edge.

  • source (str) – ID of the source node.

  • target (str) – ID of the target node.

Example

>>> edge = Edge("AB", source="A", target="B")
connects(node_id: str) bool

Check if this edge connects to the given node.

id: str
other_node(node_id: str) str

Get the node on the other end of this edge.

Parameters:

node_id – One endpoint of this edge.

Returns:

The ID of the other endpoint.

Raises:

ValueError – If node_id is not an endpoint of this edge.

source: str
target: str
class pylinkage.hypergraph.core.Hyperedge(id: str, nodes: tuple[str, ...]=<factory>, name: str | None = None)

Bases: object

An N-way rigid body connection (topology only).

A hyperedge connects N nodes (joints) that belong to the same rigid link. This is a topological grouping - distance constraints are stored separately in a Dimensions object.

This is more expressive than multiple edges with a shared body_id because it explicitly groups all related nodes together.

Variables:
  • id (str) – Unique identifier for this hyperedge.

  • nodes (tuple[str, ...]) – Tuple of node IDs connected by this rigid body.

  • name (str | None) – Human-readable name for the rigid body.

Example

>>> # Triangle rigid body connecting A, B, C
>>> he = Hyperedge(
...     id="triangle",
...     nodes=("A", "B", "C"),
...     name="Triangle Link"
... )
classmethod from_edges(edges: list[Edge], hyperedge_id: str, name: str | None = None) Hyperedge

Create a hyperedge from a collection of edges.

Assumes all edges belong to the same rigid body.

Parameters:
  • edges – List of edges to combine.

  • hyperedge_id – ID for the new hyperedge.

  • name – Optional name for the hyperedge.

Returns:

A new Hyperedge combining all the edges.

Raises:

ValueError – If edges list is empty.

Example

>>> edges = [
...     Edge("e1", "A", "B"),
...     Edge("e2", "B", "C"),
... ]
>>> he = Hyperedge.from_edges(edges, "combined")
>>> "A" in he.nodes
True
id: str
name: str | None = None
nodes: tuple[str, ...]
to_edges(prefix: str = '') list[Edge]

Convert hyperedge to regular binary edges.

Creates one Edge for each pair of adjacent nodes in this hyperedge. For a hyperedge with N nodes, creates N-1 edges forming a chain.

Parameters:

prefix – Optional prefix for generated edge IDs.

Returns:

List of Edge objects representing pairwise connections.

Example

>>> he = Hyperedge("tri", ("A", "B", "C"))
>>> edges = he.to_edges()
>>> len(edges)
2
class pylinkage.hypergraph.core.Node(id: str, role: NodeRole = NodeRole.DRIVEN, joint_type: JointType = JointType.REVOLUTE, name: str | None = None)

Bases: object

A joint in the linkage hypergraph (topology only).

Represents the topological aspect of a joint - its identity, role, and type. Geometric data (position, angles) is stored separately in a Dimensions object.

Variables:
  • id (str) – Unique identifier for this node.

  • role (pylinkage._types.NodeRole) – The role in the mechanism (GROUND, DRIVER, or DRIVEN).

  • joint_type (pylinkage._types.JointType) – The kinematic joint type (REVOLUTE or PRISMATIC).

  • name (str | None) – Human-readable name for display.

Example

>>> node = Node("A", role=NodeRole.GROUND)
>>> node.joint_type
<JointType.REVOLUTE: 1>
id: str
joint_type: JointType = 1
name: str | None = None
role: NodeRole = 2

pylinkage.hypergraph.graph module

Hypergraph-based representation of planar linkages (topology only).

This module provides the HypergraphLinkage class, which represents linkages as hypergraphs where nodes are joints and connections can be either binary edges or N-way hyperedges (rigid bodies).

This is a pure topological representation - dimensional data (positions, distances, angles) is stored separately in a Dimensions object.

class pylinkage.hypergraph.graph.HypergraphLinkage(nodes: dict[str, ~pylinkage.hypergraph.core.Node]=<factory>, edges: dict[str, ~pylinkage.hypergraph.core.Edge]=<factory>, hyperedges: dict[str, ~pylinkage.hypergraph.core.Hyperedge]=<factory>, name: str = '', _adjacency: dict[str, list[str]]=<factory>, _hyperedge_membership: dict[str, list[str]]=<factory>)

Bases: object

Pure topological hypergraph representation of a planar linkage.

A linkage is represented as a hypergraph where: - Nodes are joints (revolute R or prismatic P) with roles - Edges are binary link connections - Hyperedges are N-way rigid body groupings

This is a topology-only representation. Dimensional data (positions, distances, angles) is stored separately in a Dimensions object.

This representation enables: - First-class rigid body representation via hyperedges - Hierarchical composition - Conversion to other representations (Assur, Mechanism) - Pure structural analysis without geometric constraints

Variables:

Example

>>> graph = HypergraphLinkage(name="Four-bar")
>>> graph.add_node(Node("A", role=NodeRole.GROUND))
>>> graph.add_node(Node("B", role=NodeRole.DRIVER))
>>> graph.add_edge(Edge("AB", source="A", target="B"))
add_edge(edge: Edge) None

Add an edge to the graph.

Parameters:

edge – The Edge to add.

Raises:

ValueError – If an edge with the same ID already exists, or if source/target nodes don’t exist.

add_hyperedge(hyperedge: Hyperedge) None

Add a hyperedge to the graph.

Parameters:

hyperedge – The Hyperedge to add.

Raises:

ValueError – If a hyperedge with the same ID already exists, or if any referenced nodes don’t exist.

add_node(node: Node) None

Add a node to the graph.

Parameters:

node – The Node to add.

Raises:

ValueError – If a node with the same ID already exists.

all_neighbors(node_id: str) list[str]

Get all nodes connected to the given node via edges or hyperedges.

Parameters:

node_id – The node to find neighbors of.

Returns:

List of all connected node IDs (deduplicated).

copy() HypergraphLinkage

Create a deep copy of the graph.

Returns:

A new HypergraphLinkage with copied nodes, edges, and hyperedges.

degree(node_id: str) int

Get the edge degree (number of edge connections) of a node.

Parameters:

node_id – The node ID.

Returns:

Number of edges connected to the node.

driven_nodes() list[Node]

Get all driven nodes.

driver_nodes() list[Node]

Get all driver/input nodes.

edges: dict[str, Edge]
get_edge_between(node1: str, node2: str) Edge | None

Get the edge connecting two nodes, if any.

Parameters:
  • node1 – First node ID.

  • node2 – Second node ID.

Returns:

The Edge connecting the nodes, or None if not connected.

get_edges_for_node(node_id: str) list[Edge]

Get all edges connected to a node.

Parameters:

node_id – The node ID.

Returns:

List of Edge objects connected to the node.

get_hyperedges_for_node(node_id: str) list[Hyperedge]

Get all hyperedges containing a node.

Parameters:

node_id – The node ID.

Returns:

List of Hyperedge objects containing the node.

ground_nodes() list[Node]

Get all ground/frame nodes.

hyperdegree(node_id: str) int

Get the hyperedge degree (number of hyperedge memberships) of a node.

Parameters:

node_id – The node ID.

Returns:

Number of hyperedges containing the node.

hyperedge_neighbors(node_id: str) list[str]

Get all nodes sharing a hyperedge with the given node.

Parameters:

node_id – The node to find hyperedge neighbors of.

Returns:

List of node IDs sharing hyperedges with the given node.

hyperedges: dict[str, Hyperedge]
name: str = ''
neighbors(node_id: str) list[str]

Get all nodes connected to the given node via edges.

Parameters:

node_id – The node to find neighbors of.

Returns:

List of node IDs connected via edges.

nodes: dict[str, Node]
remove_edge(edge_id: str) Edge

Remove an edge from the graph.

Parameters:

edge_id – ID of the edge to remove.

Returns:

The removed Edge.

Raises:

KeyError – If edge not found.

remove_hyperedge(hyperedge_id: str) Hyperedge

Remove a hyperedge from the graph.

Parameters:

hyperedge_id – ID of the hyperedge to remove.

Returns:

The removed Hyperedge.

Raises:

KeyError – If hyperedge not found.

remove_node(node_id: str) Node

Remove a node and all its connections from the graph.

Parameters:

node_id – ID of the node to remove.

Returns:

The removed Node.

Raises:

KeyError – If node not found.

to_simple_graph() HypergraphLinkage

Convert to a simple graph by expanding hyperedges to edges.

Creates a new HypergraphLinkage with no hyperedges. Every hyperedge becomes the clique of its nodes — one edge per pair — since a rigid body holds each pair of its joints at a fixed distance. Pairs already joined by an edge are left as they are.

Returns:

A new HypergraphLinkage with only nodes and edges.

pylinkage.hypergraph.hierarchy module

Hierarchical linkage composition (topology only).

This module provides classes for assembling linkages from component instances and flattening them to a single HypergraphLinkage for analysis or simulation.

class pylinkage.hypergraph.hierarchy.ComponentInstance(id: str, topology: HypergraphLinkage, ports: dict[str, str]=<factory>, name: str = '')

Bases: object

An instance of a topology with connection ports.

Represents a concrete instantiation of a HypergraphLinkage with defined ports for connecting to other instances.

Variables:
  • id (str) – Unique identifier for this instance within the hierarchy.

  • topology (pylinkage.hypergraph.graph.HypergraphLinkage) – The HypergraphLinkage defining this component’s structure.

  • ports (dict[str, str]) – Mapping from port IDs to internal node IDs.

  • name (str) – Human-readable name for this instance.

Example

>>> topology = HypergraphLinkage(name="leg")
>>> # ... add nodes and edges ...
>>> instance = ComponentInstance(
...     id="left_leg",
...     topology=topology,
...     ports={"input": "A", "output": "B"},
...     name="Left Leg"
... )
get_port_qualified_node(port_id: str) str

Get the qualified node ID for a port.

Parameters:

port_id – The port ID.

Returns:

The globally qualified node ID for the port’s internal node.

Raises:

KeyError – If port not found.

get_qualified_node_id(internal_node: str) str

Get the globally qualified node ID for an internal node.

Parameters:

internal_node – The internal node ID within the component.

Returns:

A globally unique node ID like “instance_id.internal_node”.

id: str
name: str = ''
ports: dict[str, str]
topology: HypergraphLinkage
class pylinkage.hypergraph.hierarchy.Connection(from_instance: str, from_port: str, to_instance: str, to_port: str)

Bases: object

Connection between two component ports.

Defines how two component instances are connected via their ports. During flattening, connected ports are merged into a single node.

Variables:
  • from_instance (str) – Instance ID of the source component.

  • from_port (str) – Port ID on the source component.

  • to_instance (str) – Instance ID of the target component.

  • to_port (str) – Port ID on the target component.

Example

>>> conn = Connection(
...     from_instance="leg1",
...     from_port="output",
...     to_instance="leg2",
...     to_port="input"
... )
from_instance: str
from_port: str
to_instance: str
to_port: str
class pylinkage.hypergraph.hierarchy.HierarchicalLinkage(instances: dict[str, ~pylinkage.hypergraph.hierarchy.ComponentInstance]=<factory>, connections: list[Connection] = <factory>, name: str = '')

Bases: object

Top-level container for hierarchical linkage definition.

A hierarchical linkage contains component instances and defines connections between them. It can be flattened to a HypergraphLinkage for simulation or further processing.

Variables:

Example

>>> # Create instances
>>> leg1 = ComponentInstance("leg1", leg_topology, {"input": "A", "output": "B"})
>>> leg2 = ComponentInstance("leg2", leg_topology, {"input": "A", "output": "B"})
>>>
>>> # Create hierarchical linkage
>>> linkage = HierarchicalLinkage(
...     instances={"leg1": leg1, "leg2": leg2},
...     connections=[
...         Connection("leg1", "output", "leg2", "input"),
...     ],
...     name="Two-Leg Walker"
... )
>>>
>>> # Flatten for simulation
>>> flat_graph = linkage.flatten()
add_connection(connection: Connection) None

Add a connection between ports.

Parameters:

connection – The Connection to add.

Raises:

ValueError – If the connection references invalid instances or ports.

add_instance(instance: ComponentInstance) None

Add a component instance.

Parameters:

instance – The ComponentInstance to add.

Raises:

ValueError – If an instance with the same ID already exists.

connections: list[Connection]
flatten() HypergraphLinkage

Flatten the hierarchy to a single HypergraphLinkage.

This is the key method that converts the hierarchical representation to a flat hypergraph that can be converted to other representations or used for analysis.

The flattening process: 1. Creates qualified nodes from all component instances 2. Creates qualified edges from all component instances 3. Creates qualified hyperedges from all component instances 4. Merges connected ports into single nodes

Returns:

A HypergraphLinkage with all components expanded and connected.

get_all_port_nodes() dict[str, str]

Get all exposed port nodes with their qualified IDs.

Returns:

Dictionary mapping “instance_id.port_id” to qualified node ID.

instances: dict[str, ComponentInstance]
name: str = ''

pylinkage.hypergraph.mechanism_conversion module

Direct conversion between HypergraphLinkage and Mechanism.

This module provides direct conversion between the hypergraph representation and the mechanism model, bypassing the legacy Linkage class.

This is the preferred conversion path for new code: - HypergraphLinkage is the pure topological representation - Dimensions holds the geometric data (positions, distances, angles) - Mechanism is the concrete simulation model with Links + Joints

For backward compatibility with legacy Linkage, use conversion.py instead.

pylinkage.hypergraph.mechanism_conversion.from_mechanism(mechanism: Mechanism) tuple[HypergraphLinkage, Dimensions]

Convert a Mechanism to a HypergraphLinkage and Dimensions.

This converts an existing Mechanism to the hypergraph representation for analysis, visualization, or manipulation.

Parameters:

mechanism – The Mechanism to convert.

Returns:

A tuple of (HypergraphLinkage, Dimensions).

Example

>>> mechanism = Mechanism(joints=[...], links=[...])
>>> hg, dims = from_mechanism(mechanism)
>>> # Analyze or modify the hypergraph
pylinkage.hypergraph.mechanism_conversion.to_mechanism(hypergraph: HypergraphLinkage, dimensions: Dimensions) Mechanism

Convert a HypergraphLinkage and Dimensions to a Mechanism.

This converts the hypergraph topology plus dimensional data to the mechanism model. This is the preferred conversion path for new code.

Parameters:
  • hypergraph – The HypergraphLinkage defining the topology.

  • dimensions – The Dimensions providing positions, distances, angles.

Returns:

A Mechanism instance ready for simulation.

Raises:

ValueError – If the hypergraph is underconstrained or has disconnected components that cannot be solved.

Example

>>> hg = HypergraphLinkage(name="Four-bar")
>>> hg.add_node(Node("A", role=NodeRole.GROUND))
>>> hg.add_edge(Edge("AB", "A", "B"))
>>> dims = Dimensions(
...     node_positions={"A": (0, 0), "B": (1, 0)},
...     edge_distances={"AB": 1.0},
... )
>>> mechanism = to_mechanism(hg, dims)

pylinkage.hypergraph.serialization module

Serialization for hypergraph structures (topology only).

This module provides functions to convert hypergraph objects to and from dictionaries and JSON files. This enables persistence and interoperability.

Note: This module only handles topological data. For dimensional data, use the serialization functions in pylinkage.dimensions.

pylinkage.hypergraph.serialization.component_instance_from_dict(data: dict[str, Any]) ComponentInstance

Create a ComponentInstance from a dictionary.

Parameters:

data – Dictionary representation.

Returns:

A ComponentInstance.

pylinkage.hypergraph.serialization.component_instance_to_dict(instance: ComponentInstance) dict[str, Any]

Convert a ComponentInstance to a dictionary.

Parameters:

instance – The ComponentInstance to convert.

Returns:

Dictionary representation.

pylinkage.hypergraph.serialization.connection_from_dict(data: dict[str, Any]) Connection

Create a Connection from a dictionary.

pylinkage.hypergraph.serialization.connection_to_dict(connection: Connection) dict[str, Any]

Convert a Connection to a dictionary.

pylinkage.hypergraph.serialization.edge_from_dict(data: dict[str, Any]) Edge

Create an Edge from a dictionary.

Parameters:

data – Dictionary representation.

Returns:

An Edge instance.

pylinkage.hypergraph.serialization.edge_to_dict(edge: Edge) dict[str, Any]

Convert an Edge to a dictionary (topology only).

Parameters:

edge – The Edge to convert.

Returns:

Dictionary representation of the edge.

pylinkage.hypergraph.serialization.graph_from_dict(data: dict[str, Any]) HypergraphLinkage

Create a HypergraphLinkage from a dictionary.

Parameters:

data – Dictionary representation.

Returns:

A HypergraphLinkage instance.

pylinkage.hypergraph.serialization.graph_from_json(path: str | Path) HypergraphLinkage

Load a HypergraphLinkage from a JSON file.

Parameters:

path – Path to the input file.

Returns:

A HypergraphLinkage instance.

pylinkage.hypergraph.serialization.graph_to_dict(graph: HypergraphLinkage) dict[str, Any]

Convert a HypergraphLinkage to a dictionary.

Parameters:

graph – The HypergraphLinkage to convert.

Returns:

Dictionary representation of the graph.

pylinkage.hypergraph.serialization.graph_to_json(graph: HypergraphLinkage, path: str | Path) None

Save a HypergraphLinkage to a JSON file.

Parameters:
  • graph – The HypergraphLinkage to save.

  • path – Path to the output file.

pylinkage.hypergraph.serialization.hierarchical_from_dict(data: dict[str, Any]) HierarchicalLinkage

Create a HierarchicalLinkage from a dictionary.

Parameters:

data – Dictionary representation.

Returns:

A HierarchicalLinkage instance.

pylinkage.hypergraph.serialization.hierarchical_from_json(path: str | Path) HierarchicalLinkage

Load a HierarchicalLinkage from a JSON file.

Parameters:

path – Path to the input file.

Returns:

A HierarchicalLinkage instance.

pylinkage.hypergraph.serialization.hierarchical_to_dict(linkage: HierarchicalLinkage) dict[str, Any]

Convert a HierarchicalLinkage to a dictionary.

Parameters:

linkage – The HierarchicalLinkage to convert.

Returns:

Dictionary representation.

pylinkage.hypergraph.serialization.hierarchical_to_json(linkage: HierarchicalLinkage, path: str | Path) None

Save a HierarchicalLinkage to a JSON file.

Parameters:
  • linkage – The HierarchicalLinkage to save.

  • path – Path to the output file.

pylinkage.hypergraph.serialization.hyperedge_from_dict(data: dict[str, Any]) Hyperedge

Create a Hyperedge from a dictionary.

Parameters:

data – Dictionary representation.

Returns:

A Hyperedge instance.

pylinkage.hypergraph.serialization.hyperedge_to_dict(hyperedge: Hyperedge) dict[str, Any]

Convert a Hyperedge to a dictionary (topology only).

Parameters:

hyperedge – The Hyperedge to convert.

Returns:

Dictionary representation of the hyperedge.

pylinkage.hypergraph.serialization.node_from_dict(data: dict[str, Any]) Node

Create a Node from a dictionary.

Parameters:

data – Dictionary representation.

Returns:

A Node instance.

pylinkage.hypergraph.serialization.node_to_dict(node: Node) dict[str, Any]

Convert a Node to a dictionary (topology only).

Parameters:

node – The Node to convert.

Returns:

Dictionary representation of the node.

pylinkage.hypergraph.sim_conversion module

Conversion between simulation.Linkage and HypergraphLinkage.

simulation.Linkage (a.k.a. “SimLinkage”) is the joint-free component/actuator/dyad API produced by pylinkage.synthesis and pylinkage.optimization.co_optimize. This module converts one of those objects into the topological HypergraphLinkage plus a companion Dimensions record so downstream tooling (leggedsnake.Walker, exporters) can consume synthesis output directly.

Only the component types that the upstream synthesis / catalog co-optimization actually emit are handled:

  • GroundNodeRole.GROUND

  • Crank / ArcCrankNodeRole.DRIVER with a DriverAngle

  • LinearActuatorNodeRole.DRIVER (modelled as a prismatic-joint node; the prismatic axis lives on the output node)

  • RRRDyad → two edges (one per distance constraint)

  • FixedDyad → a hyperedge over the rigid triangle plus two edges carrying the two leg distances as numeric fallbacks

  • RRPDyad → one edge for the revolute leg; the prismatic track is encoded as a hyperedge over the three line/anchor nodes

  • PPDyad → a hyperedge over the four line anchors (no numeric distances — it is fully determined by the four line anchors)

Anything else raises NotImplementedError so callers know to extend the bridge when pylinkage grows new component types.

pylinkage.hypergraph.sim_conversion.from_sim_linkage(sim_linkage: SimLinkage) tuple[HypergraphLinkage, Dimensions]

Convert a pylinkage.simulation.Linkage to a hypergraph.

Parameters:

sim_linkage – A simulation.Linkage built from the modern component/actuator/dyad API.

Returns:

A tuple (hypergraph, dimensions) suitable for feeding into pylinkage.hypergraph.to_mechanism() or into any hypergraph-native consumer.

Raises:
  • TypeError – If sim_linkage does not look like a simulation.Linkage (no .components).

  • NotImplementedError – If a component type is encountered that this bridge does not yet handle.

Module contents

Hypergraph-based representation of planar linkages (topology only).

This module provides a hierarchical hypergraph abstraction for defining and manipulating planar linkages. It serves as an abstract mathematical foundation that can be converted to other representations (Assur graphs, Mechanism) for different purposes.

This is a pure topological representation - dimensional data (positions, distances, angles) is stored separately in a Dimensions object.

Key concepts: - HypergraphLinkage: Abstract graph supporting both edges and hyperedges - ComponentInstance: Instance of a topology with connection ports - HierarchicalLinkage: Composition of component instances

Example usage:

from pylinkage.hypergraph import (
    HypergraphLinkage, Node, Edge, NodeRole,
    to_mechanism
)
from pylinkage.dimensions import Dimensions, DriverAngle

# Create topology
hg = HypergraphLinkage(name="Four-bar")
hg.add_node(Node("A", role=NodeRole.GROUND))
hg.add_node(Node("B", role=NodeRole.DRIVER))
hg.add_node(Node("C", role=NodeRole.DRIVEN))
hg.add_node(Node("D", role=NodeRole.GROUND))
hg.add_edge(Edge("AB", "A", "B"))
hg.add_edge(Edge("BC", "B", "C"))
hg.add_edge(Edge("CD", "C", "D"))

# Create dimensions
dims = Dimensions(
    node_positions={"A": (0, 0), "B": (1, 0), "C": (2, 1), "D": (3, 0)},
    driver_angles={"B": DriverAngle(0.1)},
    edge_distances={"AB": 1.0, "BC": 2.0, "CD": 2.0},
)

# Convert to mechanism for simulation
mechanism = to_mechanism(hg, dims)
class pylinkage.hypergraph.ComponentInstance(id: str, topology: HypergraphLinkage, ports: dict[str, str]=<factory>, name: str = '')

Bases: object

An instance of a topology with connection ports.

Represents a concrete instantiation of a HypergraphLinkage with defined ports for connecting to other instances.

Variables:
  • id (str) – Unique identifier for this instance within the hierarchy.

  • topology (pylinkage.hypergraph.graph.HypergraphLinkage) – The HypergraphLinkage defining this component’s structure.

  • ports (dict[str, str]) – Mapping from port IDs to internal node IDs.

  • name (str) – Human-readable name for this instance.

Example

>>> topology = HypergraphLinkage(name="leg")
>>> # ... add nodes and edges ...
>>> instance = ComponentInstance(
...     id="left_leg",
...     topology=topology,
...     ports={"input": "A", "output": "B"},
...     name="Left Leg"
... )
get_port_qualified_node(port_id: str) str

Get the qualified node ID for a port.

Parameters:

port_id – The port ID.

Returns:

The globally qualified node ID for the port’s internal node.

Raises:

KeyError – If port not found.

get_qualified_node_id(internal_node: str) str

Get the globally qualified node ID for an internal node.

Parameters:

internal_node – The internal node ID within the component.

Returns:

A globally unique node ID like “instance_id.internal_node”.

id: str
name: str = ''
ports: dict[str, str]
topology: HypergraphLinkage
class pylinkage.hypergraph.Connection(from_instance: str, from_port: str, to_instance: str, to_port: str)

Bases: object

Connection between two component ports.

Defines how two component instances are connected via their ports. During flattening, connected ports are merged into a single node.

Variables:
  • from_instance (str) – Instance ID of the source component.

  • from_port (str) – Port ID on the source component.

  • to_instance (str) – Instance ID of the target component.

  • to_port (str) – Port ID on the target component.

Example

>>> conn = Connection(
...     from_instance="leg1",
...     from_port="output",
...     to_instance="leg2",
...     to_port="input"
... )
from_instance: str
from_port: str
to_instance: str
to_port: str
class pylinkage.hypergraph.Edge(id: str, source: str, target: str)

Bases: object

A binary connection between two nodes (topology only).

Edges represent rigid links connecting exactly two joints. For N-way rigid bodies (N > 2), use Hyperedge instead.

Distance constraints are stored separately in a Dimensions object.

Variables:
  • id (str) – Unique identifier for this edge.

  • source (str) – ID of the source node.

  • target (str) – ID of the target node.

Example

>>> edge = Edge("AB", source="A", target="B")
connects(node_id: str) bool

Check if this edge connects to the given node.

id: str
other_node(node_id: str) str

Get the node on the other end of this edge.

Parameters:

node_id – One endpoint of this edge.

Returns:

The ID of the other endpoint.

Raises:

ValueError – If node_id is not an endpoint of this edge.

source: str
target: str
pylinkage.hypergraph.EdgeId

alias of str

class pylinkage.hypergraph.HierarchicalLinkage(instances: dict[str, ~pylinkage.hypergraph.hierarchy.ComponentInstance]=<factory>, connections: list[Connection] = <factory>, name: str = '')

Bases: object

Top-level container for hierarchical linkage definition.

A hierarchical linkage contains component instances and defines connections between them. It can be flattened to a HypergraphLinkage for simulation or further processing.

Variables:

Example

>>> # Create instances
>>> leg1 = ComponentInstance("leg1", leg_topology, {"input": "A", "output": "B"})
>>> leg2 = ComponentInstance("leg2", leg_topology, {"input": "A", "output": "B"})
>>>
>>> # Create hierarchical linkage
>>> linkage = HierarchicalLinkage(
...     instances={"leg1": leg1, "leg2": leg2},
...     connections=[
...         Connection("leg1", "output", "leg2", "input"),
...     ],
...     name="Two-Leg Walker"
... )
>>>
>>> # Flatten for simulation
>>> flat_graph = linkage.flatten()
add_connection(connection: Connection) None

Add a connection between ports.

Parameters:

connection – The Connection to add.

Raises:

ValueError – If the connection references invalid instances or ports.

add_instance(instance: ComponentInstance) None

Add a component instance.

Parameters:

instance – The ComponentInstance to add.

Raises:

ValueError – If an instance with the same ID already exists.

connections: list[Connection]
flatten() HypergraphLinkage

Flatten the hierarchy to a single HypergraphLinkage.

This is the key method that converts the hierarchical representation to a flat hypergraph that can be converted to other representations or used for analysis.

The flattening process: 1. Creates qualified nodes from all component instances 2. Creates qualified edges from all component instances 3. Creates qualified hyperedges from all component instances 4. Merges connected ports into single nodes

Returns:

A HypergraphLinkage with all components expanded and connected.

get_all_port_nodes() dict[str, str]

Get all exposed port nodes with their qualified IDs.

Returns:

Dictionary mapping “instance_id.port_id” to qualified node ID.

instances: dict[str, ComponentInstance]
name: str = ''
class pylinkage.hypergraph.Hyperedge(id: str, nodes: tuple[str, ...]=<factory>, name: str | None = None)

Bases: object

An N-way rigid body connection (topology only).

A hyperedge connects N nodes (joints) that belong to the same rigid link. This is a topological grouping - distance constraints are stored separately in a Dimensions object.

This is more expressive than multiple edges with a shared body_id because it explicitly groups all related nodes together.

Variables:
  • id (str) – Unique identifier for this hyperedge.

  • nodes (tuple[str, ...]) – Tuple of node IDs connected by this rigid body.

  • name (str | None) – Human-readable name for the rigid body.

Example

>>> # Triangle rigid body connecting A, B, C
>>> he = Hyperedge(
...     id="triangle",
...     nodes=("A", "B", "C"),
...     name="Triangle Link"
... )
classmethod from_edges(edges: list[Edge], hyperedge_id: str, name: str | None = None) Hyperedge

Create a hyperedge from a collection of edges.

Assumes all edges belong to the same rigid body.

Parameters:
  • edges – List of edges to combine.

  • hyperedge_id – ID for the new hyperedge.

  • name – Optional name for the hyperedge.

Returns:

A new Hyperedge combining all the edges.

Raises:

ValueError – If edges list is empty.

Example

>>> edges = [
...     Edge("e1", "A", "B"),
...     Edge("e2", "B", "C"),
... ]
>>> he = Hyperedge.from_edges(edges, "combined")
>>> "A" in he.nodes
True
id: str
name: str | None = None
nodes: tuple[str, ...]
to_edges(prefix: str = '') list[Edge]

Convert hyperedge to regular binary edges.

Creates one Edge for each pair of adjacent nodes in this hyperedge. For a hyperedge with N nodes, creates N-1 edges forming a chain.

Parameters:

prefix – Optional prefix for generated edge IDs.

Returns:

List of Edge objects representing pairwise connections.

Example

>>> he = Hyperedge("tri", ("A", "B", "C"))
>>> edges = he.to_edges()
>>> len(edges)
2
pylinkage.hypergraph.HyperedgeId

alias of str

class pylinkage.hypergraph.HypergraphLinkage(nodes: dict[str, ~pylinkage.hypergraph.core.Node]=<factory>, edges: dict[str, ~pylinkage.hypergraph.core.Edge]=<factory>, hyperedges: dict[str, ~pylinkage.hypergraph.core.Hyperedge]=<factory>, name: str = '', _adjacency: dict[str, list[str]]=<factory>, _hyperedge_membership: dict[str, list[str]]=<factory>)

Bases: object

Pure topological hypergraph representation of a planar linkage.

A linkage is represented as a hypergraph where: - Nodes are joints (revolute R or prismatic P) with roles - Edges are binary link connections - Hyperedges are N-way rigid body groupings

This is a topology-only representation. Dimensional data (positions, distances, angles) is stored separately in a Dimensions object.

This representation enables: - First-class rigid body representation via hyperedges - Hierarchical composition - Conversion to other representations (Assur, Mechanism) - Pure structural analysis without geometric constraints

Variables:

Example

>>> graph = HypergraphLinkage(name="Four-bar")
>>> graph.add_node(Node("A", role=NodeRole.GROUND))
>>> graph.add_node(Node("B", role=NodeRole.DRIVER))
>>> graph.add_edge(Edge("AB", source="A", target="B"))
add_edge(edge: Edge) None

Add an edge to the graph.

Parameters:

edge – The Edge to add.

Raises:

ValueError – If an edge with the same ID already exists, or if source/target nodes don’t exist.

add_hyperedge(hyperedge: Hyperedge) None

Add a hyperedge to the graph.

Parameters:

hyperedge – The Hyperedge to add.

Raises:

ValueError – If a hyperedge with the same ID already exists, or if any referenced nodes don’t exist.

add_node(node: Node) None

Add a node to the graph.

Parameters:

node – The Node to add.

Raises:

ValueError – If a node with the same ID already exists.

all_neighbors(node_id: str) list[str]

Get all nodes connected to the given node via edges or hyperedges.

Parameters:

node_id – The node to find neighbors of.

Returns:

List of all connected node IDs (deduplicated).

copy() HypergraphLinkage

Create a deep copy of the graph.

Returns:

A new HypergraphLinkage with copied nodes, edges, and hyperedges.

degree(node_id: str) int

Get the edge degree (number of edge connections) of a node.

Parameters:

node_id – The node ID.

Returns:

Number of edges connected to the node.

driven_nodes() list[Node]

Get all driven nodes.

driver_nodes() list[Node]

Get all driver/input nodes.

edges: dict[str, Edge]
get_edge_between(node1: str, node2: str) Edge | None

Get the edge connecting two nodes, if any.

Parameters:
  • node1 – First node ID.

  • node2 – Second node ID.

Returns:

The Edge connecting the nodes, or None if not connected.

get_edges_for_node(node_id: str) list[Edge]

Get all edges connected to a node.

Parameters:

node_id – The node ID.

Returns:

List of Edge objects connected to the node.

get_hyperedges_for_node(node_id: str) list[Hyperedge]

Get all hyperedges containing a node.

Parameters:

node_id – The node ID.

Returns:

List of Hyperedge objects containing the node.

ground_nodes() list[Node]

Get all ground/frame nodes.

hyperdegree(node_id: str) int

Get the hyperedge degree (number of hyperedge memberships) of a node.

Parameters:

node_id – The node ID.

Returns:

Number of hyperedges containing the node.

hyperedge_neighbors(node_id: str) list[str]

Get all nodes sharing a hyperedge with the given node.

Parameters:

node_id – The node to find hyperedge neighbors of.

Returns:

List of node IDs sharing hyperedges with the given node.

hyperedges: dict[str, Hyperedge]
name: str = ''
neighbors(node_id: str) list[str]

Get all nodes connected to the given node via edges.

Parameters:

node_id – The node to find neighbors of.

Returns:

List of node IDs connected via edges.

nodes: dict[str, Node]
remove_edge(edge_id: str) Edge

Remove an edge from the graph.

Parameters:

edge_id – ID of the edge to remove.

Returns:

The removed Edge.

Raises:

KeyError – If edge not found.

remove_hyperedge(hyperedge_id: str) Hyperedge

Remove a hyperedge from the graph.

Parameters:

hyperedge_id – ID of the hyperedge to remove.

Returns:

The removed Hyperedge.

Raises:

KeyError – If hyperedge not found.

remove_node(node_id: str) Node

Remove a node and all its connections from the graph.

Parameters:

node_id – ID of the node to remove.

Returns:

The removed Node.

Raises:

KeyError – If node not found.

to_simple_graph() HypergraphLinkage

Convert to a simple graph by expanding hyperedges to edges.

Creates a new HypergraphLinkage with no hyperedges. Every hyperedge becomes the clique of its nodes — one edge per pair — since a rigid body holds each pair of its joints at a fixed distance. Pairs already joined by an edge are left as they are.

Returns:

A new HypergraphLinkage with only nodes and edges.

class pylinkage.hypergraph.JointType(value)

Bases: IntEnum

Kinematic joint type.

This enum classifies joints by their allowed degrees of freedom. Values are explicit integers for serialization stability.

Variables:
  • REVOLUTE – Pin joint allowing rotation (R). 1 DOF rotation.

  • PRISMATIC – Slider joint allowing translation (P). 1 DOF translation.

  • GROUND – Fixed revolute joint on frame. Special case for mechanism module.

  • TRACKER – Observer joint (T). 0 DOF, just tracks a position.

GROUND = 3
PRISMATIC = 2
REVOLUTE = 1
TRACKER = 4
class pylinkage.hypergraph.Node(id: str, role: NodeRole = NodeRole.DRIVEN, joint_type: JointType = JointType.REVOLUTE, name: str | None = None)

Bases: object

A joint in the linkage hypergraph (topology only).

Represents the topological aspect of a joint - its identity, role, and type. Geometric data (position, angles) is stored separately in a Dimensions object.

Variables:
  • id (str) – Unique identifier for this node.

  • role (pylinkage._types.NodeRole) – The role in the mechanism (GROUND, DRIVER, or DRIVEN).

  • joint_type (pylinkage._types.JointType) – The kinematic joint type (REVOLUTE or PRISMATIC).

  • name (str | None) – Human-readable name for display.

Example

>>> node = Node("A", role=NodeRole.GROUND)
>>> node.joint_type
<JointType.REVOLUTE: 1>
id: str
joint_type: JointType = 1
name: str | None = None
role: NodeRole = 2
pylinkage.hypergraph.NodeId

alias of str

class pylinkage.hypergraph.NodeRole(value)

Bases: IntEnum

Role of a node/joint in the mechanism.

Classifies joints by their kinematic role in the linkage structure.

Variables:
  • GROUND – Fixed frame point that does not move.

  • DRIVER – Input/motor joint that provides motion (actuated).

  • DRIVEN – Position computed from constraints (passive, part of Assur groups).

DRIVEN = 2
DRIVER = 1
GROUND = 0
pylinkage.hypergraph.PortId

alias of str

pylinkage.hypergraph.from_mechanism(mechanism: Mechanism) tuple[HypergraphLinkage, Dimensions]

Convert a Mechanism to a HypergraphLinkage and Dimensions.

This converts an existing Mechanism to the hypergraph representation for analysis, visualization, or manipulation.

Parameters:

mechanism – The Mechanism to convert.

Returns:

A tuple of (HypergraphLinkage, Dimensions).

Example

>>> mechanism = Mechanism(joints=[...], links=[...])
>>> hg, dims = from_mechanism(mechanism)
>>> # Analyze or modify the hypergraph
pylinkage.hypergraph.from_sim_linkage(sim_linkage: SimLinkage) tuple[HypergraphLinkage, Dimensions]

Convert a pylinkage.simulation.Linkage to a hypergraph.

Parameters:

sim_linkage – A simulation.Linkage built from the modern component/actuator/dyad API.

Returns:

A tuple (hypergraph, dimensions) suitable for feeding into pylinkage.hypergraph.to_mechanism() or into any hypergraph-native consumer.

Raises:
  • TypeError – If sim_linkage does not look like a simulation.Linkage (no .components).

  • NotImplementedError – If a component type is encountered that this bridge does not yet handle.

pylinkage.hypergraph.graph_from_dict(data: dict[str, Any]) HypergraphLinkage

Create a HypergraphLinkage from a dictionary.

Parameters:

data – Dictionary representation.

Returns:

A HypergraphLinkage instance.

pylinkage.hypergraph.graph_from_json(path: str | Path) HypergraphLinkage

Load a HypergraphLinkage from a JSON file.

Parameters:

path – Path to the input file.

Returns:

A HypergraphLinkage instance.

pylinkage.hypergraph.graph_to_dict(graph: HypergraphLinkage) dict[str, Any]

Convert a HypergraphLinkage to a dictionary.

Parameters:

graph – The HypergraphLinkage to convert.

Returns:

Dictionary representation of the graph.

pylinkage.hypergraph.graph_to_json(graph: HypergraphLinkage, path: str | Path) None

Save a HypergraphLinkage to a JSON file.

Parameters:
  • graph – The HypergraphLinkage to save.

  • path – Path to the output file.

pylinkage.hypergraph.hierarchical_from_dict(data: dict[str, Any]) HierarchicalLinkage

Create a HierarchicalLinkage from a dictionary.

Parameters:

data – Dictionary representation.

Returns:

A HierarchicalLinkage instance.

pylinkage.hypergraph.hierarchical_from_json(path: str | Path) HierarchicalLinkage

Load a HierarchicalLinkage from a JSON file.

Parameters:

path – Path to the input file.

Returns:

A HierarchicalLinkage instance.

pylinkage.hypergraph.hierarchical_to_dict(linkage: HierarchicalLinkage) dict[str, Any]

Convert a HierarchicalLinkage to a dictionary.

Parameters:

linkage – The HierarchicalLinkage to convert.

Returns:

Dictionary representation.

pylinkage.hypergraph.hierarchical_to_json(linkage: HierarchicalLinkage, path: str | Path) None

Save a HierarchicalLinkage to a JSON file.

Parameters:
  • linkage – The HierarchicalLinkage to save.

  • path – Path to the output file.

pylinkage.hypergraph.to_mechanism(hypergraph: HypergraphLinkage, dimensions: Dimensions) Mechanism

Convert a HypergraphLinkage and Dimensions to a Mechanism.

This converts the hypergraph topology plus dimensional data to the mechanism model. This is the preferred conversion path for new code.

Parameters:
  • hypergraph – The HypergraphLinkage defining the topology.

  • dimensions – The Dimensions providing positions, distances, angles.

Returns:

A Mechanism instance ready for simulation.

Raises:

ValueError – If the hypergraph is underconstrained or has disconnected components that cannot be solved.

Example

>>> hg = HypergraphLinkage(name="Four-bar")
>>> hg.add_node(Node("A", role=NodeRole.GROUND))
>>> hg.add_edge(Edge("AB", "A", "B"))
>>> dims = Dimensions(
...     node_positions={"A": (0, 0), "B": (1, 0)},
...     edge_distances={"AB": 1.0},
... )
>>> mechanism = to_mechanism(hg, dims)