pylinkage.population package

Module contents

Population abstractions for working with collections of mechanisms.

  • Member: A single mechanism variant (dimensions + scores + trajectory).

  • Ensemble: N parameter variants of one topology (fast batch simulation).

  • Population: Heterogeneous collection of mechanisms (different topologies).

class pylinkage.population.Ensemble(linkage: Linkage, dimensions: NDArray[np.float64], initial_positions: NDArray[np.float64], scores: dict[str, NDArray[np.float64]] | None = None)

Bases: object

N parameter variants of one linkage topology.

All members share the same SolverData structure (joint_types, parent_indices, constraint_offsets, solve_order). Only constraints and initial_positions vary per member.

Supports vectorized batch simulation via numba.

Integer indexing returns a Member; slice or array indexing returns a new Ensemble (numpy convention).

Parameters:
  • linkage – Template Linkage defining the topology. Used to compile the shared SolverData; not mutated.

  • dimensions – Constraint vectors, shape (n_members, n_constraints).

  • initial_positions – Joint positions, shape (n_members, n_joints, 2).

  • scores – Named score arrays, each of shape (n_members,). Stored columnar for efficient ranking/filtering.

property dimensions: ndarray[tuple[Any, ...], dtype[float64]]

Constraint vectors, shape (n_members, n_constraints).

filter(predicate: Callable[[Member], bool]) Ensemble

Return a new Ensemble with members matching predicate.

filter_by_score(name: str = 'score', *, min_val: float = -inf, max_val: float = inf) Ensemble

Return a new Ensemble with members whose score is in range.

classmethod from_agents(linkage: Linkage, agents: list[Agent]) Ensemble

Create an Ensemble from a legacy optimization result.

Parameters:
  • linkage – The linkage that was optimized.

  • agents – Results from PSO, grid search, etc.

classmethod from_pareto_front(linkage: Linkage, front: ParetoFront) Ensemble

Create an Ensemble from a multi-objective Pareto front.

Parameters:
  • linkage – The linkage that was optimized.

  • front – Pareto front result.

property initial_positions: ndarray[tuple[Any, ...], dtype[float64]]

Initial positions, shape (n_members, n_joints, 2).

property linkage: Linkage

Template linkage defining the shared topology.

property n_constraints: int

Number of constraints per member.

property n_joints: int

Number of joints in the shared topology.

property n_members: int

Number of parameter variants.

plot_plotly(idx: int = 0, iterations: int | None = None, **kwargs: Any) object

Plot a member with plot_linkage_plotly().

Simulates the member if no trajectory is cached.

Parameters:
  • idx – Member index to visualize.

  • iterations – Simulation steps (if trajectory not yet computed).

  • **kwargs – Forwarded to plot_linkage_plotly().

Returns:

A plotly Figure object.

rank(key: str = 'score', *, ascending: bool = True) Ensemble

Return a new Ensemble sorted by key.

save_svg(path: str, idx: int = 0, iterations: int | None = None, **kwargs: Any) None

Save a member as SVG via save_linkage_svg().

Simulates the member if no trajectory is cached.

Parameters:
  • path – Output file path.

  • idx – Member index to visualize.

  • iterations – Simulation steps (if trajectory not yet computed).

  • **kwargs – Forwarded to save_linkage_svg().

property scores: dict[str, ndarray[tuple[Any, ...], dtype[float64]]]

Named score arrays, each of shape (n_members,).

show(idx: int = 0, iterations: int | None = None, **kwargs: Any) object

Animate a member with show_linkage().

Simulates the member if no trajectory is cached.

Parameters:
  • idx – Member index to visualize.

  • iterations – Simulation steps (if trajectory not yet computed).

  • **kwargs – Forwarded to show_linkage().

Returns:

The matplotlib FuncAnimation object.

simulate(iterations: int | None = None, dt: float = 1.0, *, store: bool = True) ndarray[tuple[Any, ...], dtype[float64]]

Batch-simulate all members.

Parameters:
  • iterations – Steps per member. If None, uses the linkage’s rotation period.

  • dt – Time step for crank rotation.

  • store – If True, cache trajectories so they are available on individual Members and via trajectories.

Returns:

Array of shape (n_members, iterations, n_joints, 2).

simulate_member(idx: int, iterations: int | None = None, dt: float = 1.0) ndarray[tuple[Any, ...], dtype[float64]]

Simulate a single member (lazy per-member access).

The result is cached in _trajectories if the full batch has already been computed; otherwise a one-off simulation is run and the result is returned (not cached in the batch array).

property template: SolverData

Shared solver data for the topology, compiled on first access.

Raises:

NotImplementedError – If the numba solver cannot represent the linkage. Such an Ensemble still holds its members and scores; only batch simulation is unavailable.

to_agents() list[Agent]

Convert all members back to legacy Agents.

to_pareto_front() ParetoFront

Members as a ParetoFront.

Every score column becomes one objective, in insertion order, so the front carries the objective_names given to multi_objective_optimization(). Use it for the front’s own tools: best_compromise(), filter(), hypervolume() and plot().

top(n: int, key: str = 'score', *, ascending: bool = True) Ensemble

Return the top n members by key.

property topology_key: tuple[tuple[int, ...], tuple[tuple[int, ...], ...], tuple[int, ...]]

Hashable topology identity for grouping.

Two linkages with the same topology_key are structurally identical and can be merged into one Ensemble.

property trajectories: ndarray[tuple[Any, ...], dtype[float64]] | None

Cached trajectories, shape (n_members, iterations, n_joints, 2).

class pylinkage.population.Member(dimensions: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]], initial_positions: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]], scores: dict[str, float] = <factory>, trajectory: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | None = None, metadata: dict[str, ~typing.Any] = <factory>)

Bases: object

One mechanism variant in a population.

Universal record type for a single mechanism, replacing the various result types (Agent, ParetoSolution) with a unified representation.

Variables:
  • dimensions (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]) – Flat constraint vector, shape (n_constraints,).

  • initial_positions (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]) – Joint positions, shape (n_joints, 2).

  • scores (dict[str, float]) – Named scores (e.g. {“stride”: 4.2, “smoothness”: 0.8}).

  • trajectory (numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]] | None) – Cached simulation result, shape (steps, n_joints, 2). None until simulate() is called.

  • metadata (dict[str, Any]) – Arbitrary extra data (topology name, catalog entry, etc.).

dimensions: ndarray[tuple[Any, ...], dtype[float64]]
classmethod from_agent(agent: Agent, n_joints: int) Member

Convert a legacy Agent to a Member.

Parameters:
  • agent – Optimization result agent.

  • n_joints – Number of joints in the linkage (needed to reshape initial_positions into (n_joints, 2)).

classmethod from_pareto_solution(sol: ParetoSolution, n_joints: int, objective_names: tuple[str, ...] = ()) Member

Convert a ParetoSolution to a Member.

Parameters:
  • sol – Multi-objective optimization result.

  • n_joints – Number of joints in the linkage.

  • objective_names – Names for each objective score.

initial_positions: ndarray[tuple[Any, ...], dtype[float64]]
metadata: dict[str, Any]
property score: float

Primary score (first entry, or NaN if empty).

scores: dict[str, float]
to_agent() Agent

Convert back to a legacy Agent for backwards compatibility.

to_loci() tuple[tuple[tuple[float, float], ...], ...]

Convert trajectory to the loci format used by the visualizer.

Returns:

Nested tuples loci[frame][joint] = (x, y) compatible with show_linkage() and friends.

Raises:

ValueError – If no trajectory has been computed yet.

trajectory: ndarray[tuple[Any, ...], dtype[float64]] | None
class pylinkage.population.Population(ensembles: dict[str, Ensemble] | None = None)

Bases: object

Collection of mechanisms with potentially different topologies.

Internally organized as topology_key Ensemble. Supports the same iteration/filtering/ranking interface as Ensemble but cannot batch-simulate across topologies.

Parameters:

ensembles – Pre-built ensembles keyed by a user-chosen label. If None, starts empty.

add_ensemble(label: str, ensemble: Ensemble) None

Add or replace an ensemble.

Parameters:
  • label – Human-readable label for this topology group.

  • ensemble – The Ensemble to add.

ensemble(label: str) Ensemble

Get an ensemble by its label.

Raises:

KeyError – If no ensemble with that label exists.

flatten() list[Member]

All members as a flat list, regardless of topology.

classmethod from_ensembles(ensembles: list[Ensemble]) Population

Build a Population from a list of Ensembles.

Labels are auto-generated as "topology_0", "topology_1", etc.

classmethod from_members(members: list[tuple[Linkage, Member]]) Population

Build a Population from (linkage, member) pairs.

Members are grouped by topology key into Ensembles automatically.

Parameters:

members – Pairs of (template linkage, member data).

classmethod from_topology_solutions(solutions: list[TopologySolution]) Population

Build a Population from multi-topology synthesis results.

Groups solutions by topology_id into Ensembles. Each solution’s QualityMetrics are carried as scores on the Ensemble members.

Parameters:

solutions – Results from multi_topology_synthesize() or generalized_synthesis().

property n_topologies: int

Number of distinct topologies.

rank(key: str = 'score', *, ascending: bool = True) list[Member]

All members ranked by key across topologies.

Returns a flat list because Members from different topologies cannot form a single Ensemble. If key is not found on a member but the member has exactly one score, that score is used.

simulate_all(iterations: int | None = None, dt: float = 1.0) None

Simulate all ensembles (vectorized within each topology).

Parameters:
  • iterations – Steps per member. If None, each ensemble uses its linkage’s rotation period.

  • dt – Time step for crank rotation.

top(n: int, key: str = 'score', *, ascending: bool = True) list[Member]

Top n members across all topologies.

property topologies: dict[str, Ensemble]

Topology label → Ensemble mapping (read-only view).