This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Pylinkage is a Python library for building and optimizing planar linkages using Particle Swarm Optimization (PSO). It provides tools to define kinematic linkages, simulate their motion, optimize their geometry against objective functions, and visualize the results.
uv sync # Install all dependencies (including dev)
uv sync --no-dev # Install only production dependenciesuv run task test # Run all tests
uv run task test-cov # Run with coverage
uv run pytest tests/joints/ # Run specific test directory
uv run pytest -k "test_buildable" # Run tests matching patternuv run task lint # Lint code
uv run task lint-fix # Lint and auto-fix
uv run task format # Format code
uv run task typecheck # Type checkuv build # Build wheel and sdist
uv run task docs # Build documentation
uv run task docs-clean # Clean documentation artifacts-
src/pylinkage/components/: Base classes and fixed frame elements
Component: Abstract base class for all kinematic elementsConnectedComponent: Base for elements with parent connectionsGround: Fixed point on the frame (ground link)PointTracker: Sensor component that tracks a point on a moving link_AnchorProxy: Proxy for actuator output connections
-
src/pylinkage/actuators/: Motor-driven input drivers
Crank: Motor-driven rotary input (rotating around ground)ArcCrank: Crank with limited angular range (oscillating arc motion)LinearActuator: Motor-driven linear input (oscillating piston/cylinder)
-
src/pylinkage/dyads/: Pure Assur groups (0 DOF structural units)
RRRDyad: Circle-circle intersection (two links meeting at one joint)RRPDyad: Circle-line intersection (slider mechanism)PPDyad: Line-line intersection (double slider mechanism)FixedDyad: Deterministic polar projectionBinaryDyad: Base class for binary Assur groupsTranslatingCamFollower: Translating follower driven by cam profileOscillatingCamFollower: Oscillating (rocker) follower driven by cam profilecreate_dyad(): Factory function to create dyads from isomer signatures- Note: Re-exports Ground, Crank, LinearActuator, Linkage for backwards compatibility
-
src/pylinkage/cam/: Cam profile definitions for cam-follower mechanisms
CamProfile: Base class for cam profilesFunctionProfile: Profile from motion law + timing parametersPointArrayProfile: Profile from discrete points with spline interpolation- Motion laws:
HarmonicMotionLaw,CycloidalMotionLaw,ModifiedTrapezoidalMotionLaw,PolynomialMotionLaw - Factory functions:
polynomial_345(),polynomial_4567()
-
src/pylinkage/simulation/: Simulation containers
Linkage: Container orchestrating components into a mechanism
-
src/pylinkage/mechanism/: Low-level Links + Joints model
Joint,RevoluteJoint,PrismaticJoint,GroundJoint: Joint classesLink,DriverLink,GroundLink: Rigid body classesMechanism: Main orchestrator class- Conversion:
pylinkage.dyads.to_mechanism()builds aMechanismfrom a componentLinkage. The reverse direction does not exist:mechanism_to_linkage()andmechanism_from_linkage()were deleted inb0cbc3bwith the legacy joints module. - Serialization:
mechanism_to_json(),mechanism_from_json()
-
src/pylinkage/linkage/: Linkage class that orchestrates joint collections
Linkage: Main class managing joints, solving order, and simulation viastep()methodSimulation: Container for simulation results (loci, steps)analysis.py: Helper functions likebounding_box()andkinematic_default_test()sensitivity.py: Sensitivity analysis (sensitivity_analysis()) and tolerance analysis (tolerance_analysis()) for manufacturing/dimensional variation
-
src/pylinkage/optimization/: Optimization algorithms
grid_search.py:trials_and_errors_optimization()- exhaustive searchparticle_swarm.py:particle_swarm_optimization()- PSO using PySwarmsscipy_optimize.py:differential_evolution_optimization(),minimize_linkage()- scipy-basedmulti_objective.py:multi_objective_optimization()- Pareto-optimal solutions using NSGA-II/III (requirespymoo)async_optimization.py: Async variants of all optimizers with progress tracking (OptimizationProgress)collections/pareto.py:ParetoFront,ParetoSolutionfor multi-objective resultsutils.py:@kinematic_minimization/@kinematic_maximizationdecorators andgenerate_bounds()
-
src/pylinkage/geometry/: 2D geometry utilities
core.py: Distance calculations, coordinate conversionssecants.py: Circle-circle and circle-line intersections
-
src/pylinkage/visualizer/: Multi-backend visualization
static.py,animated.py: Matplotlib backend (show_linkage(), GIF output)plotly_viz.py: Plotly backend for interactive HTML (plot_linkage_plotly())drawsvg_viz.py: drawsvg backend for publication-quality SVG (save_linkage_svg())pso_plots.py: PSO visualization dashboards
-
src/pylinkage/hypergraph/: Hierarchical hypergraph representation (new)
- Abstract mathematical foundation for linkage definition
HypergraphLinkage: Graph with nodes, edges, and hyperedgesComponent: Reusable linkage subgraph with ports and parametersHierarchicalLinkage: Composition of component instances- Built-in components:
FOURBAR,CRANK_SLIDER,DYAD - Conversion functions:
to_linkage(),from_linkage(),to_assur_graph()
-
src/pylinkage/assur/: Assur group decomposition
- Graph-based representation using formal kinematic theory
LinkageGraph: Nodes (joints) and edges (links)- Assur groups:
DyadRRR,DyadRRP,DyadRPR,DyadPRR decompose_assur_groups(): Structural decomposition algorithmgraph_to_linkage(): Convert graph representation to Linkage
-
src/pylinkage/solver/: High-performance numba simulation backend
- Pure-numba JIT-compiled solver for optimization hot loops
SolverData: Numeric arrays replacing Python objectssimulate(): Fast trajectory computationlinkage_to_solver_data(): Convert Linkage for fast simulation
-
src/pylinkage/synthesis/: Classical mechanism synthesis methods
function_generation.py: Match input/output angle relationships (Freudenstein)path_generation.py: Coupler point traces through specified pointsmotion_generation.py: Guide body through specified posesburmester.py: Burmester theory for circle point/center point curvesutils.py: Grashof criterion checking (is_grashof(),is_crank_rocker())conversion.py:fourbar_from_lengths(),solution_to_linkage()
-
src/pylinkage/symbolic/: Symbolic computation using SymPy
joints.py:SymStatic,SymCrank,SymRevolutesymbolic joint classeslinkage.py:SymbolicLinkagefor symbolic trajectory expressionssolver.py:solve_linkage_symbolically(),compute_trajectory_numeric()optimization.py:SymbolicOptimizerfor gradient-based optimizationgeometry.py: Symbolic geometry primitives
-
src/pylinkage/bridge/: Conversion utilities between representations
- Bridges between Linkage, Assur graph, Hypergraph, and Solver representations
Component-Based Definition Flow (Preferred API):
- Create
Groundpoints for fixed frame locations (frompylinkage.components) - Create
CrankorLinearActuatorfor motor-driven input (frompylinkage.actuators) - Add
RRRDyadorRRPDyadfor constrained connections (frompylinkage.dyads) - Wrap in
Linkageand callstep()to simulate (frompylinkage.simulation) - Use
show_linkage()to visualize
Note: For backwards compatibility, Ground, Crank, LinearActuator, and Linkage are also re-exported from pylinkage.dyads.
Alternative Definition via Hypergraph:
- Use component instances from library (
FOURBAR,DYAD, etc.) - Connect via
HierarchicalLinkagewithConnectionobjects - Call
flatten()thento_linkage()to get simulatable Linkage
Alternative Definition via Assur Graph:
- Create
LinkageGraphwithNodeandEdgeobjects - Use
decompose_assur_groups()for structural analysis - Call
graph_to_linkage()to convert to Linkage
Optimization Flow:
- Define a fitness function decorated with
@kinematic_minimizationor@kinematic_maximization - Generate bounds with
generate_bounds(linkage.get_constraints()) - Call
particle_swarm_optimization()ortrials_and_errors_optimization(); every optimizer returns anEnsemble - Apply results via
linkage.set_constraints(results[0].dimensions)(orresults.show(0)to draw a member)
Synthesis Flow (Design from requirements):
- Define precision points, angle pairs, or poses depending on synthesis type
- Call
function_generation(),path_generation(), ormotion_generation() - Iterate over
SynthesisResult.solutionsto get candidate linkages - Validate with
grashof_check()oris_crank_rocker() - Convert to
Linkagewithsolution_to_linkage()for simulation
Symbolic Computation Flow:
- Create symbolic linkage with
fourbar_symbolic()orlinkage_to_symbolic() - Get closed-form expressions via
solve_linkage_symbolically() - Evaluate numerically with
compute_trajectory_numeric() - Optional: Use
SymbolicOptimizerfor gradient-based optimization
Constraint System:
get_constraints(): Returns flat list of distances/anglesset_constraints(): Applies constraints back to jointsget_num_constraints()/set_num_constraints(): deprecated aliases since 1.2.0 (warn, removed in 2.0)get_coords()/set_coords(): Joint positions (used for initial positions in optimization)
Exceptions:
UnbuildableError: Raised when a linkage cannot be assembled (geometric impossibility)UnderconstrainedError: Raised when a linkage is underconstrained (too few constraints)NotCompletelyDefinedError: Raised when joint parameters are incomplete
Multi-Objective Optimization Flow:
- Define multiple objective functions decorated with
@kinematic_minimization - Call
multi_objective_optimization()with list of objectives (requirespymoo:pip install pylinkage[moo]) - Get
ParetoFrontcontaining non-dominatedParetoSolutionobjects - Iterate solutions to explore trade-offs between competing objectives
Sensitivity/Tolerance Analysis Flow:
- Define linkage with named joints for interpretable results
- Call
sensitivity_analysis(linkage)to compute sensitivity indices per constraint - Call
tolerance_analysis(linkage, tolerances, n_samples)for Monte Carlo simulation - Results include mean/std deviation of output paths and statistical distributions
Historical note — legacy pylinkage.joints removal:
The legacy pylinkage.joints module (with Static, Revolute, Linear, Fixed, and the old Crank signature) was removed in commit 9c1515f. All code now uses the components/actuators/dyads API. If you encounter old snippets elsewhere, the mapping was:
Static(x, y)→Ground(x, y)frompylinkage.componentsCrank(joint0=A, distance=r, angle=v)→Crank(anchor=A, radius=r, angular_velocity=v)frompylinkage.actuatorsRevolute(joint0=A, joint1=B, distance0=d0, distance1=d1)→RRRDyad(anchor1=A, anchor2=B, distance1=d0, distance2=d1)frompylinkage.dyadsLinear(...)→RRPDyad(...)frompylinkage.dyadsFixed(...)→FixedDyad(...)frompylinkage.dyads
Note: the local submodules src/pylinkage/solver/joints.py and src/pylinkage/symbolic/joints.py are unrelated internal helpers (numba joint solvers and symbolic joint classes) — they are not the deprecated API.
Requires Python >= 3.10
Core: numpy, numba, scipy, matplotlib, pyswarms, tqdm, plotly, drawsvg, sympy
Optional extras:
moo: pymoo (for multi-objective optimization)cad: ezdxf, build123d (for CAD export)analysis: pandas (for data analysis)
Dev (managed via uv): pytest, pytest-cov, hypothesis, mypy, ruff, sphinx, sphinx-rtd-theme, myst-parser, taskipy