Releases: HugoFara/pylinkage
v0.9.0: Co-optimization and the Topology Catalog
Pylinkage 0.9.0 is a research-plus-polish release. Two new capabilities: topology enumeration and co-optimization of topology + dimensions. It moves pylinkage from "simulate and optimize a given mechanism" to "design the mechanism itself." Alongside them, a round of API ergonomics (factory helpers, analysis plot methods, trajectory extraction, skip_unbuildable) cuts visible boilerplate across every notebook. It is a release to stabilize the API around the features that really matter.
pip install pylinkage # Lightweight core
pip install pylinkage[full] # EverythingHighlights
Topology catalog and enumeration
The first pylinkage release to treat mechanism structure itself as a searchable object. Systematic enumeration of every non-isomorphic 1-DOF planar linkage up to 8 links — 19 topologies total (1 four-bar + 2 six-bars + 16 eight-bars), validated against Mruthyunjaya's 1984 three-part catalog (I, II, III). Graph isomorphism detection via 1-WL color refinement plus backtracking verification. Ships as a pre-built TopologyCatalog with JSON-serialized hypergraphs and metadata.
from pylinkage.topology import load_catalog
catalog = load_catalog() # 19 canonical topologies
for entry in catalog.entries.values():
print(entry.family, entry.num_joints, entry.link_assortment)Co-optimization of topology + dimensions
A mixed-variable NSGA-II optimizer that jointly searches the discrete topology space and the continuous dimensional space via pymoo. Custom operators: MixedCrossover blends dimensions with BLX-α while swapping topologies via a neighborhood graph; MixedMutation does Gaussian perturbation plus topology-neighbor jumps. Simultaneous triad placement via scipy.optimize.least_squares handles circular dependencies in Stephenson six-bars. Warm-start from Phase 3 synthesis results.
from pylinkage.optimization import co_optimize, CoOptimizationConfig
from pylinkage.topology import load_catalog
result = co_optimize(
objectives=[path_error, stroke_range],
catalog=load_catalog(),
config=CoOptimizationConfig(n_generations=50),
)
for sol in result.solutions:
print(sol.topology_entry.family, sol.chromosome.dimensions, sol.scores)Six-bar mechanisms end-to-end
Mechanism.step() now uses Assur group decomposition internally, solving dyads and triads via solve_group() dispatch. Watt and Stephenson six-bars can be simulated start to finish. Convenience builders watt_from_lengths() and stephenson_from_lengths() from seven link lengths.
Population abstractions
pylinkage.population introduces Member / Ensemble / Population — numpy-style containers for batch mechanism work. Topology-bound ensembles run all variants through the numba-compiled solver in one pass, with columnar scores for vectorized rank(), top(), filter_by_score(). Every optimizer now returns an Ensemble instead of ad-hoc lists. SynthesisResult.ensemble lazily builds one from synthesis solutions.
result = particle_swarm_optimization(fitness, linkage, ...) # → Ensemble
top3 = result.top(3, ascending=False) # three best by score
good = result.filter_by_score(min_val=0.8) # every member above threshold
good.show(idx=0) # render a chosen memberErgonomic helpers
fourbar(crank, coupler, rocker, ground, ...)andslider_crank(crank, rod, ...)inpylinkage.mechanism— canonical topologies in one line instead of the eight-lineMechanismBuilderchain.extract_trajectory(loci, joint=-1)/extract_trajectories(loci, linkage=None)— replaces the recurring[(p[i][0], p[i][1]) for p in loci if p[i][0] is not None]pattern with(xs, ys)numpy arrays.TransmissionAngleAnalysis.plot(ax=None)— one call for the acceptable-range + 90° optimum diagram.skip_unbuildable=TrueonLinkage.step()— yieldsNonetuples for unbuildable frames instead of aborting, so non-Grashof and double-rocker linkages recover the valid trajectory on both sides of their dead zones.
More optimizers
dual_annealing_optimization()— scipy's generalized simulated annealing for expensive single-objective problems with many local minima.chain_optimizers()— run optimizers in sequence, piping each stage's best into the next (e.g. DE → Nelder-Mead).
Notebooks
Now we have 15 notebooks showing the extent of the app with all the user facing features. Have a look for examples, tutorials and more!
Breaking changes
- Optimizers return
Ensemble, notlist[Agent],list[MutableAgent], orParetoFront. Unpack viamember = result[0]; member.score, member.dimensionsinstead of Agent tuple unpacking. fourbar_from_lengths()now returnsSimLinkage(component/actuator/dyad API) instead of the legacyLinkage. Access joints via.components, not.joints.- Default simulation resolution: 63 → 360 steps per rotation. Default angular velocity for
Crank,ArcCrank,DriverLink,ArcDriverLinkchanged from0.1rad/step totau / 360(~one sample per degree). Old value is exported asDEFAULT_ANGULAR_VELOCITYif you need it. - PSO is now pure NumPy. Dropped the pyswarms dependency (unmaintained since 2021); replaced with a built-in local-best ring-topology PSO. API unchanged. The
psoextra still exists but is empty. - Removed deprecated aliases:
HypostaticError(useUnderconstrainedError),Linear(usePrismatic). - Parameter renames (old names still accepted as kwargs):
iters→iterations,pos→initial_positions,init_positions→initial_positions.
Deprecations
SynthesisResultcollection protocol (len(result),result[i], iteration,bool(result)) now emitsDeprecationWarning. Useresult.ensembleinstead. Will be removed in 1.0.0.
Fixes
compute_dofhyperedge counting — was counting a k-node hyperedge as (k−1) links instead of 1 rigid body, producing wrong DOF for every mechanism with ternary-or-higher links (all six-bars and eight-bars).- Change-point kinematics flake in
test_fourbar_simulation_with_kinematics— test geometry was on the Grashof boundary, hitting singular configurations non-deterministically on some CI hosts. - API exception leak —
validate_and_buildinapi/services/echoed raw exception messages into API responses (CodeQL "information exposure" alert). Domain-levelUnbuildableErrormessages still pass through; unexpected exceptions are now logged server-side with a generic response.
Security
- Pillow 12.1.1 → 12.2.0 (FITS GZIP decompression bomb, CVE high).
- pytest 9.0.2 → 9.0.3 (insecure tmpdir handling, CVE moderate).
Full changelog: v0.8.0...v0.9.0
v0.8.0: Symbolic, Synthetic, and Complete
Pylinkage 0.8.0 is the biggest release in the project's history. It transforms pylinkage from a kinematic simulator with PSO into a complete planar mechanism design toolkit. It covers synthesis, simulation, optimization, analysis, and CAD export in a single pip install.
pip install pylinkage # Lightweight core (numpy + tqdm)
pip install pylinkage[full] # Everything includedHighlights
Classical linkage synthesis. Design four-bar linkages from motion requirements using Burmester theory, Freudenstein's equation, and path/function/motion generation. Specify precision points and get back buildable linkages. No manual geometry needed.
from pylinkage.synthesis import path_generation, solution_to_linkage
result = path_generation([(0, 1), (1, 2), (2, 1.5), (3, 0)])
for sol in result.solutions:
linkage = solution_to_linkage(sol)Symbolic computation. Get closed-form trajectory expressions via SymPy, compute analytical gradients, and run gradient-based optimization with SymbolicOptimizer. Mix symbolic parameters with numerical simulation seamlessly.
Multi-objective optimization. Pareto-optimal solutions via NSGA-II/III (pymoo). Optimize competing objectives — path accuracy vs. transmission angle, stroke range vs. compactness; with pareto.plot(), hypervolume indicators, and best-compromise selection.
Velocity & acceleration analysis. Full first and second-order kinematics with Numba-compiled solvers. Query joint.velocity and joint.acceleration directly, or use linkage.step_with_derivatives() for batch computation.
Cam-follower mechanisms. New pylinkage.cam module with translating and oscillating followers, standard motion laws (harmonic, cycloidal, modified trapezoidal, polynomial 3-4-5), and Numba-compiled profile evaluation.
Triad support & topology analysis. Class II Assur groups (triads) enable six-bar mechanisms (Watt, Stephenson), addressing the long-standing question of multi-bar support (#2). New compute_dof() implements Grübler's formula on the hypergraph representation.
Sensitivity & tolerance analysis. Monte Carlo tolerance analysis for manufacturing variation, sensitivity ranking of constraints, and DataFrame export for further analysis.
Transmission angle & stroke analysis. Built-in quality metrics for mechanism evaluation, directly usable as optimization constraints.
CAD export. DXF (via ezdxf) and STEP (via build123d) export for fabrication workflows, alongside publication-quality SVG.
SciPy integration. Differential evolution optimizer and scipy.optimize.minimize wrappers, complementing the existing PSO.
Numba JIT solver. High-performance compiled simulation backend for optimization hot loops (#11).
Lightweight by default
The core install now requires only numpy and tqdm. Everything else is optional:
| Extra | What it adds |
|---|---|
numba |
JIT-compiled solvers |
scipy |
Differential evolution, synthesis solvers |
pso |
Particle Swarm Optimization (pyswarms) |
symbolic |
SymPy closed-form expressions & gradient optimization |
viz |
Matplotlib visualization and animation |
plotly |
Interactive HTML visualization |
svg |
Publication-quality SVG (drawsvg) |
cad |
DXF and STEP export (ezdxf, build123d) |
moo |
Multi-objective optimization (pymoo) |
analysis |
Pandas DataFrame export for sensitivity data |
api |
FastAPI REST backend |
full |
All visualization, solver, and optimization extras |
Combine as needed: pip install pylinkage[viz,scipy,pso]
Modular API
New component-based API replaces the legacy joint classes (#13):
Legacy (pylinkage.joints) |
New | Module |
|---|---|---|
Static |
Ground |
pylinkage.components |
Crank |
Crank |
pylinkage.actuators |
Revolute / Pivot |
RRRDyad |
pylinkage.dyads |
Fixed |
FixedDyad |
pylinkage.dyads |
Linear |
RRPDyad |
pylinkage.dyads |
| — | LinearActuator |
pylinkage.actuators |
| — | ArcCrank |
pylinkage.actuators |
Plus a link-first fluent MechanismBuilder API with JSON serialization.
11 tutorial notebooks
New Jupyter notebooks walk through real design problems end-to-end:
- Straight-line synthesis — approximate straight-line coupler paths
- Coupler curve optimization — PSO-driven curve matching
- Tolerance analysis — Monte Carlo manufacturing variation
- Cam-follower timing — motion laws and profile design
- Symbolic coupler curves — closed-form SymPy expressions
- Function generation — Freudenstein equation synthesis
- Motion generation — rigid body guidance (Burmester theory)
- Velocity & acceleration — first/second-order kinematics
- Transmission angle & DOF — Grübler's formula, quality metrics
- MechanismBuilder — fluent API vs. legacy comparison
- Multi-objective & SciPy optimization — DE, Nelder-Mead, NSGA-II Pareto fronts
Breaking changes
- Python ≥ 3.10 required (3.9 dropped). Python 3.14 added to CI.
Linkage.step_fast_with_kinematics()now returns(positions, velocities, accelerations)instead of a 2-tuple.LinearActuator.velocityrenamed toLinearActuator.speed(conflict with newComponent.velocityproperty).
Bug fixes
- PSO score sign:
particle_swarm_optimization()returned the negated pyswarms cost when usingorder_relation=max, producing incorrect (often negative) scores. - Mechanism builder branch selection:
MechanismBuilder.set_branch()produced inconsistent assembly configurations because circle-circle constraints arrived in non-deterministic order. Constraints are now sorted by center position before intersection.
See Also
- pylinkage-editor — Visual linkage design tool (#9). Draw mechanisms interactively, run synthesis from the GUI, and export results.
- bastidas/Acinonyx — A polished visualization frontend for pylinkage. @bastidas's project was the inspiration to finally get these changes out of
devand publish this release.
Full changelog: v0.7.0...v0.8.0
0.7.0: abstractions and visualisation!
This release goes back to the roots of the project: linkage analysis.
It separates the abstractions between fast numerical solving (numba), meaningful abstraction (hypergraphs),
while aligning the user API with elegant classical concepts (Assur groups)
I ended up releasing a bit faster than expected as I noticed 0.6.0 was apparently broken.
[0.7.0] - 2025-12-13
Added in 0.7.0
- Serialization: adds linkage serialization features.
- Typing: adds typing.
- Test: adds complete testing coverage.
- Adds support for Python 3.14.
- Hypergraph as the base theory for linkages.
Changed in 0.7.0
- Switches to
uv. - Renames
HypostaticErrortoUnderconstrainedErrorandhyperstaticity()toindeterminacy().
Old names kept as deprecated aliases. - Separate linkage definition from actual solving:
- The internal solver is now numba + NumPy, almost 100x faster!
- The user-facing code is now based on Assur groups, that is more formal.
Fixed in 0.7.0
__find_solving_order__()is now properly tested and implemented (#16).
Deprecated in 0.7.0
Linearjoint term is now deprecated in favor ofPrismatic.
Removed in 0.7.0
- Removed support for Python 3.9.
Full Changelog: v0.6.0...v0.7.0
Linear joint / Package Structure Rewrite
This release is a huge effort to make pylinkage better!
It features abstract Joint, the new Linear joint, and the package structure was rewritten to make contributions easier.
Added
- New joint: the
Linearjoint! - New sub-package: optimization.collections.
optimization.collections.Agentandoptimization.collections.MutableAgentare two new classes that should standardize the format of
optimization, related to (#5).Agentis immutable and inherits from a namedtuple. It is recommended to use it, as it is a bit faster.MutableAgentis mutable. It may be deprecated/removed ifAgentis satisfactory.
- New sub-package: geometry.
- It introduces two new functions
line_from_pointsandcircle_line_intersection.
- It introduces two new functions
- New examples:
examples/strider.pyfrom HugoFara/leggedsnake,
based on the Strider Linkage.examples/inverted_stroke_engine.pyis a demo of a four-stroke engine featuring a Linear joint.
Linkage.set_completelyis a new method combining bothLinkage.set_num_constraintsandLinkage.set_coords.- New exception
NotCompletelyDefinedError, when a joint is reloading but its anchor coordinates are set to None. - Some run configuration files added for users of PyCharm:
- Run all tests with "All Tests".
- Regenerate documentation with "Sphinx Documentation".
Changed
- Optimization return type changed (#5):
trials_and_error_optimizationreturn an array ofMutableAgent.particle_swarm_optimizationreturn an array of oneAgent.- It should not be a breaking change for most users.
- Changes to the "history" style.
- It is no longer a global variable in example scripts.
- It was in format iterations[dimensions, score], now it is a standard iterations[score, dimensions, initial pos].
repr_polar_swarm(in example scripts) changed to follow the new format.swarm_tiled_reprtakes (index, swarm) as input argument. swarm is (score, dim, pos) for each agent for this
iteration.
repr_polar_swarmreload frame only when a new buildable linkage is generated.- This makes the display much faster.
- For each iteration, you may see linkages that do not exist anymore.
- Folders reorganization:
- The
geometrymodule is now a package (pylinkage/geometry) - New package
pylinkage/linkage:pylinkage/linkage.pyseparated and inserted in this package.
- New package:
pylinkage/joints- Joints definition are in respective files.
- New package
pylinkage/optimization/pylinkage/optimizer.pysplit and inserted in.- Trials-and-errors related functions goes to
grid_search.py. - Particle swarm optimization is at
particle_swarm.py. - New file
utils.pyforgenerate_bounds.
- Tests follow the same renaming.
- From the user perspective, no change (execution may be a bit faster)
source/renamed tosphinx/because it was confusing and only for Sphinx configuration.
- The
- Transition from Numpydoc to reST for docstrings (#12).
__secant_circles_intersections__renamed to
secant_circles_intersections(inpylinkage/geometry/secants.py).
Fixed
swarm_tiled_reprinvisualizer.pywas wrongly assigning dimensions.- Setting
locus_highlightinplot_static_linkagewould result in an error. Pivot.reloadwas returning arbitrary point when we had an infinity of solutions.- The highlighted locus was sometimes buggy in
plot_static_linkagein
visualizer.py.
Deprecated
- Using
tqdm_verbosityis deprecated in favor of usingdisable=Truein a tqdm object. - The
Pivotclass is deprecated in favor of theRevoluteclass.
The name "Pivot joint" is not standard.
Related to #13. - The
hyperstaticitymethod is renamedindeterminacyinLinkage
(linkage.py)
Removed
- Drops support for Python 3.7 and 3.8 as both versions reached end-of-life.
movement_bounding_bowis replaced bymovement_bounding_box(typo in function name).
Big Dust Blowing
This is the first release in almost two years of inactivity, and as such it contains a lot of practice changes.
It mainly prepares code for a non-backward compatible leap in a future 0.6.0.
Added
- We now checked compatibility with Python 3.10 and 3.11.
pyproject.tomlis now the official definition of the package.Linkage.hyperstaticitynow clearly outputs a warning when used.
Changed
masterbranch is nowmain.docs/example/fourbar_linkage.pycan now be used as a module (not the target but anyway).docs/examplesmoved toexamples/(main folder).- Now
docs/only contains sphinx documentation.
- Now
docs/examples/imagesmoved toimages/.
Fixed
- Setting a motor with a negative rotation angle do no longer break
get_rotation_period
(#7). Pivot.reloadandLinkage.__find_solving_order__were raising Warnings (stopping the code), when they should only print a message (intended behavior).- Fixed many typos in documentation as well as in code.
- The
TestPSO.test_convergenceis now faster on average, and when it fails in the first time, it launches a bigger test. - Minor linting in the demo file
docs/example/fourbar_linkage.py.
Deprecated
- Using Python 3.7 is officially deprecated (end of life by 2023-06-27). It will no longer be tested, use it at your own risks!
A bit of user-friendlyness
This release bring some convenient functionalities.
Added
- You can see the best score and best dimensions updating in
trials_and_errors_optimization.
Changed
- The optimizers tests are 5 times quicker (~1 second now) and raise less
false positive. - The sidebar in the documentation make navigation easier.
- A bit of reorganization in optimizers, it should not affect users.
Progress bar is progress, some requirements added
Added
- The trials and errors optimization now have a progress bar (same kind of the one in particle swarm optimization), using the tqdm.
Changed
- matplotlib and tqdm are now required.
Optimized optimizers, simplified workflow
End alpha development! The package is now robust enough to be used by a mere human. This version introduces a lot of changes and simplifications, so everything is not perfect yet, but it is complete enough to be considered a beta version.
Git tags will no longer receive a "-alpha" mention.
Added
- It is now possible and advised to import useful functions from pylinkage.{object}, without full path. For instance, use
from pylinkage import Linkageinstead offrom pylinkage.linkage import Linkage. - Each module had his header improved.
- The
generate_boundsfunctions is a simple way to generate bounds before optimization. - The
order_relationarguments ofparticle_swarm_optimizationandtrials_and_errors_optimizationlet you choose between maximization and minimization problem. - You can specify a custom order relation with
trials_and_errors_optimization. - The
verboseargument in optimizers can disable verbosity. Staticjoints can now be defined implicitely.- The
utilitymodule provides two useful decaratorskinematic_minimizationandkinematic_optimizatino. They greatly simplify the workflow of defining fitness functions. - Versionning is now done thanks to bump2version.
Changed
- The
particle_swarm_optimizationeval_funcsignature is now similar to the one oftrials_and_errorsoptimization. Wrappers are no longer needed! - The
trials_and_errors_optimizationfunction now asks for bounds instead of dilatation and compression factors. - In
trials_and_errors_optimizationabsolute stepdelta_dimis now replaced by number of subdivisionsdivisions.
Fixed
- After many hours of computations, default parameters in
particle_swarm_optimizationare much more efficient. With the demofourbar_linkage, the output wasn't even convergent some times. Now we have a high convergence rate (~100%), and results equivalent to thetrials_and_errors_optimization(in the example). variatorfunction ofoptimizermodule was poorly working.- The docstrings were not displayed properly in documentation, this is fixed.
Documentation upgraded and package reorganisation
This release introduces a good documentation with sphinx and a website hosted on GitHub pages.
Added
- The legend in
visualizer.pyis back! - Documentation published to GitHub pages! It is contained in the
docs/folder. setup.cfgnow include links to the website.
Changed
- Examples moved from
pylinkage/examples/todocs/examples/. - Tests moved from
pylinkage/tests/totests/.
Visualization and code linting
This release introduces a lot of renaming. It also correct bugs introduced in alpha version v0.3.0. We ultimately deleted the built-in PSO.
Added
- The
bounding_boxmethod of geometry allow to compute the bounding box of a finite set of 2D points. - You can now customize colors of linkage's bars with the
COLOR_SWITCHERvariable ofvisualizer.py. movement_bounding_boxinvisualizer.pyto get the bounding box of multiple loci.parametersis optional intrials_and_errors_optimization(formerexhaustive_optimization)pylinkage/tests/test_optimizer.pyfor testing the optimizers, but it is a bit ugly as for now.
Fixed
set_num_constraintsinLinkagewas misbehaving due to update 0.3.0.- Cost history is no longer plotted automatically after a PSO.
Changed
exhaustive_optimizationis now known astrials_and_errors_optimizattion.- Axis on linkage visualization are now named "x" and "y", and no longer "Points abcsices" and "Ordinates".
- A default view of the linkage is displayed in
plot_static_linkage. - Default padding in linkage representation was changed from an absolute value of 0.5 to a relative 20%.
- Static view of linkage is now aligned with its kinematic one.
get_posmethod ofLinkageis now known asget_coordsfor consistency.- Parameters renamed, reorganized and removed in
particle_swarm_optimizationto align to PySwarms. README.mdupdated consequently to the changes.
Removed
- Legacy built-in Particle Swarm Optimization, to avoid confusions.
- We do no longer show a default legend on static representation.
Security
- Flake8 validation in
tox.ini