Changelog

All notable changes to pylinkage are documented here.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

Added

  • Dual Annealing optimizer: dual_annealing_optimization() wraps scipy’s generalized simulated annealing — a single-trajectory global optimizer effective for problems with many local minima and expensive evaluations.

  • Optimizer chaining: chain_optimizers() runs multiple optimizers in sequence, automatically feeding each result as the starting point for the next stage. Common pattern: global search (DE/PSO) → local refinement (Nelder-Mead).

  • Co-optimization of topology + dimensions:

    • Mixed-variable evolutionary optimizer (co_optimize()) jointly searching discrete topology space and continuous dimensional space using NSGA-II/III via pymoo with custom genetic operators.

    • Topology neighborhood graph (build_neighborhood_graph(), topology_neighbors(), topology_distance()) defining adjacency between all 19 catalog topologies via add_dyad, remove_dyad, swap_variant, and restructure operations.

    • Custom pymoo operators: MixedCrossover (BLX-alpha blend + topology swap), MixedMutation (Gaussian perturbation + topology neighbor mutation), warm_start_sampling() (seed population from Phase 3 synthesis results).

    • Virtual edge encoding: expands hyperedges (ternary links) into pairwise distances and adds implicit ground-link virtual edges for chromosome representation.

    • Simultaneous triad placement via scipy.optimize.least_squares for topologies with circular dependencies (e.g., Stephenson six-bar).

    • Warm-start pipeline: warm_start_co_optimization() converts Phase 3 TopologySolution results to MixedChromosome seeds for NSGA-II.

    • New types: MixedChromosome, CoOptimizationConfig, CoOptSolution, CoOptimizationResult.

    • TopologyCatalog.topology_index() and topology_by_index() for integer-indexed topology lookup.

  • Triad solving in mechanism simulation: Mechanism.step() now uses Assur group decomposition internally, solving dyads and triads via solve_group() dispatch. Six-bar linkages (Watt and Stephenson types) can be simulated end-to-end. graph_to_mechanism() handles triad groups (2 internal nodes, 4+ edges), creating the appropriate joints and links.

  • Topology enumeration:

    • Graph isomorphism detection via WL-1 color refinement + backtracking verification: canonical_form(), canonical_hash(), are_isomorphic().

    • Systematic enumeration of all non-isomorphic 1-DOF planar linkage topologies up to 8 links: enumerate_topologies(), enumerate_all(). Validated against Mruthyunjaya 1984: 1 four-bar + 2 six-bars + 16 eight-bars = 19.

    • Built-in topology catalog (TopologyCatalog, CatalogEntry, load_catalog()) with JSON-serialized HypergraphLinkage graphs and metadata (link assortment, family, joint count).

    • All new symbols exported from pylinkage.topology.

Fixed

  • compute_dof hyperedge counting: compute_mobility() counted each hyperedge with k nodes as (k−1) links instead of 1 rigid body, giving wrong DOF for any mechanism with ternary or higher links (all six-bars and eight-bars).

Changed

  • PSO is now pure NumPy: particle_swarm_optimization() no longer depends on pyswarms (unmaintained since 2021). Replaced with a built-in local-best ring-topology PSO. The pso optional extra is kept but empty for backwards compatibility. The API is unchanged.

Removed

  • pyswarms dependency removed from all extras (pso, full, dev group).

[0.8.0] - 2026-03-28

Added

  • Multi-objective optimization:

    • New multi_objective_optimization() function using NSGA-II/NSGA-III algorithms via pymoo.

    • ParetoFront class for storing and analyzing non-dominated solutions.

    • ParetoSolution dataclass for individual Pareto-optimal solutions.

    • Pareto front visualization with pareto.plot().

    • Hypervolume indicator computation with pareto.hypervolume().

    • Best compromise solution selection with pareto.best_compromise().

    • Crowding distance-based filtering with pareto.filter().

    • New optional dependency group: pip install pylinkage[moo].

  • Cam-follower mechanisms:

    • New pylinkage.cam module with motion laws and profile definitions.

    • Motion laws: HarmonicMotionLaw, CycloidalMotionLaw, ModifiedTrapezoidalMotionLaw, PolynomialMotionLaw (with polynomial_345() and polynomial_4567() factory functions).

    • Profile types: FunctionProfile (motion law-based) and PointArrayProfile (spline interpolation).

    • TranslatingCamFollower dyad for linear follower motion driven by cam rotation.

    • OscillatingCamFollower dyad for rocker arm motion driven by cam rotation.

    • Both knife-edge (roller_radius=0) and roller followers supported.

    • Numba-compiled profile evaluation for high-performance simulation.

  • Triad (Class II) Assur groups:

    • New Dyad and Triad classes parameterized by signature string, replacing the per-type classes (DyadRRR, DyadRRP, etc.) which are kept as aliases.

    • signature_to_hypergraph() now generates triad topologies (6-joint signatures).

    • decompose_assur_groups() detects triads when no dyad can be formed, enabling decomposition of six-bar mechanisms (Watt and Stephenson types).

    • Solver dispatches by solver_category (circle-circle, circle-line, line-line) instead of isinstance(), automatically supporting new group signatures.

  • Topology analysis:

    • New pylinkage.topology module with compute_dof() implementing Grübler’s formula (DOF = 3(n−1) 2j₁ j₂) on HypergraphLinkage.

    • compute_mobility() returns full MobilityInfo (DOF, link count, joint counts).

  • SymPy for analytical optimization.

  • Native computation of velocity and acceleration with visualizations.

  • Linkage synthesis with Burgmester’s theory, function, path and motion generation.

  • Adds scipy.

    • Exact optimization solving (better than numpy) + support constraints.

    • Adds a new optimization: differential evolution.

  • High-level velocity/acceleration API:

    • Component.velocity and Component.acceleration properties on all components.

    • simulation.Linkage.set_input_velocity(actuator, omega, alpha) to set crank angular velocity.

    • simulation.Linkage.step_with_derivatives() generator yielding (positions, velocities, accelerations).

    • simulation.Linkage.get_velocities() and get_accelerations() batch query methods.

    • solver.step_single_acceleration() numba-compiled acceleration solver.

    • Exported acceleration solvers: solve_crank_acceleration, solve_revolute_acceleration, solve_fixed_acceleration, solve_prismatic_acceleration.

Fixed

  • PSO score sign: particle_swarm_optimization() returned the negated pyswarms cost when order_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 depending on which connected port was solved first. Constraints are now sorted by center position before intersection, making branch 0/1 deterministic.

Changed

  • Breaking: Dropped Python 3.9 support. Minimum version is now Python 3.10.

  • Added Python 3.14 to CI test matrix.

  • Breaking: Linkage.step_fast_with_kinematics() now returns a 3-tuple (positions, velocities, accelerations) instead of 2-tuple.

  • Breaking: LinearActuator.velocity attribute renamed to LinearActuator.speed to avoid conflict with the new Component.velocity property.

  • simulate_with_kinematics() now computes accelerations in addition to velocities.

[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 HypostaticError to UnderconstrainedError and hyperstaticity() to indeterminacy(). 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

  • Linear joint term is now deprecated in favor of Prismatic.

Removed in 0.7.0

  • Removed support for Python 3.9.

[0.6.0] - 2024-10-02

Added in 0.6.0

  • New joint: the Linear joint!

  • New sub-package: optimization.collections. optimization.collections.Agent and optimization.collections.MutableAgent are two new classes that should standardize the format of optimization, related to (#5).

    • Agent is immutable and inherits from a namedtuple. It is recommended to use it, as it is a bit faster.

    • MutableAgent is mutable. It may be deprecated/removed if Agent is satisfactory.

  • New sub-package: geometry.

    • It introduces two new functions line_from_points and circle_line_intersection.

  • New examples:

  • Linkage.set_completely is a new method combining both Linkage.set_num_constraints and Linkage.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 in 0.6.0

  • Optimization return type changed (#5):

    • trials_and_error_optimization return an array of MutableAgent.

    • particle_swarm_optimization return an array of one Agent.

    • 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_repr takes (index, swarm) as input argument. swarm is (score, dim, pos) for each agent for this iteration.

  • repr_polar_swarm reload 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 geometry module is now a package (pylinkage/geometry)

    • New package pylinkage/linkage:

      • pylinkage/linkage.py separated and inserted in this package.

    • New package: pylinkage/joints

      • Joints definition are in respective files.

    • New package pylinkage/optimization/

      • pylinkage/optimizer.py split and inserted in.

      • Trials-and-errors related functions goes to grid_search.py.

      • Particle swarm optimization is at particle_swarm.py.

      • New file utils.py for generate_bounds.

    • Tests follow the same renaming.

    • From the user perspective, no change (execution may be a bit faster)

    • source/ renamed to sphinx/ because it was confusing and only for Sphinx configuration.

  • Transition from Numpydoc to reST for docstrings (#12).

  • __secant_circles_intersections__ renamed to secant_circles_intersections (in pylinkage/geometry/secants.py).

Fixed in 0.6.0

  • swarm_tiled_repr in visualizer.py was wrongly assigning dimensions.

  • Setting locus_highlight in plot_static_linkage would result in an error.

  • Pivot.reload was returning arbitrary point when we had an infinity of solutions.

  • The highlighted locus was sometimes buggy in plot_static_linkage in visualizer.py.

Deprecated in 0.6.0

  • Using tqdm_verbosity is deprecated in favor of using disable=True in a tqdm object.

  • The Pivot class is deprecated in favor of the Revolute class. The name “Pivot joint” is not standard. Related to #13.

  • The hyperstaticity method is renamed indeterminacy in Linkage (linkage.py)

Removed in 0.6.0

  • Drops support for Python 3.7 and 3.8 as both versions reached end-of-life.

  • movement_bounding_bow is replaced by movement_bounding_box (typo in function name).

[0.5.3] - 2023-06-23

Added in 0.5.3

  • We now checked compatibility with Python 3.10 and 3.11.

  • pyproject.toml is now the official definition of the package.

  • Linkage.hyperstaticity now clearly outputs a warning when used.

Changed in 0.5.3

  • master branch is now main.

  • docs/example/fourbar_linkage.py can now be used as a module (not the target but anyway).

  • docs/examples moved to examples/ (main folder).

    • Now docs/ only contains sphinx documentation.

  • docs/examples/images moved to images/.

Fixed in 0.5.3

  • Setting a motor with a negative rotation angle do no longer break get_rotation_period (#7).

  • Pivot.reload and Linkage.__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_convergence is 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 in 0.5.3

  • 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!

[0.5.2] - 2021-07-21

Added in 0.5.2

  • You can see the best score and best dimensions updating in trials_and_errors_optimization.

Changed in 0.5.2

  • The optimizer tests are 5 times quicker (~1 second now) and raise less false positive.

  • The sidebar in the documentation makes navigation easier.

  • A bit of reorganization in optimizers, it should not affect users.

[0.5.1] - 2021-07-14

Added in 0.5.1

  • The trial and errors optimization now have a progress bar (same kind of the one in particle swarm optimization), using tqdm.

Changed in 0.5.1

[0.5.0] - 2021-07-12

End of 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 an “-alpha” mention.

Added in 0.5.0

  • It is now possible and advised to import useful functions from pylinkage.{object}, without full path. For instance, use from pylinkage import Linkage instead of from pylinkage.linkage import Linkage.

  • Each module had his header improved.

  • The generate_bounds functions is a simple way to generate bounds before optimization.

  • The order_relation arguments of particle_swarm_optimization and trials_and_errors_optimization let you choose between maximization and minimization problem.

  • You can specify a custom order relation with trials_and_errors_optimization.

  • The verbose argument in optimizers can disable verbosity.

  • Static joints can now be defined implicitly.

  • The utility module provides two useful decorators kinematic_minimization and kinematic_optimizatino. They greatly simplify the workflow of defining fitness functions.

  • Versioning is now done thanks to bump2version.

Changed in 0.5.0

  • The particle_swarm_optimization eval_func signature is now similar to the one ot trials_and_errors optimization. Wrappers are no longer needed!

  • The trials_and_errors_optimization function now asks for bounds instead of dilatation and compression factors.

  • In trials_and_errors_optimization absolute step delta_dim is now replaced by number of subdivisions divisions.

Fixed in 0.5.0

  • After many hours of computations, default parameters in particle_swarm_optimization are much more efficient. With the demo fourbar_linkage, the output wasn’t even convergent sometimes. Now we have a high convergence rate (~100%), and results equivalent to the trials_and_errors_optimization (in the example).

  • variator function of optimizer module was poorly working.

  • The docstrings were not displayed properly in documentation, this is fixed.

[0.4.1] - 2021-07-11

Added in 0.4.1

  • The legend in visualizer.py is back!

  • Documentation published to GitHub pages! It is contained in the docs/ folder.

  • setup.cfg now include links to the website.

Changed in 0.4.1

  • Examples moved from pylinkage/examples/ to docs/examples/.

  • Tests moved from pylinkage/tests/ to tests/.

[0.4.0] - 2021-07-06

Added in 0.4.0

  • The bounding_box method of geometry allows computing the bounding box of a 2D points finite set.

  • You can now customize colors of linkage’s bars with the COLOR_SWITCHER variable of visualizer.py.

  • movement_bounding_box in visualizer.py to get the bounding box of multiple loci.

  • parameters is optional in trials_and_errors_optimization (former exhaustive_optimization)

  • pylinkage/tests/test_optimizer.py for testing the optimizers, but it is a bit ugly as for now.

  • Flake8 validation in tox.ini

Fixed in 0.4.0

  • set_num_constraints in Linkage was misbehaving due to update 0.3.0.

  • Cost history is no longer plotted automatically after a PSO.

Changed in 0.4.0

  • exhaustive_optimization is now known as trials_and_errors_optimizattion.

  • Axes on linkage visualization are now named “x” and “y”. It was “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_pos method of Linkage is now known as get_coords for consistency.

  • Parameters renamed, reorganized and removed in particle_swarm_optimization to align to PySwarms.

  • README.md updated consequently to the changes.

Removed in 0.4.0

  • Legacy built-in Particle Swarm Optimization, to avoid confusion.

  • We do no longer show a default legend on static representation.

[0.3.0] - 2021-07-05

Added in 0.3.0

  • Joint objects now have a get_constraints method, consistent with their set_constraints one.

  • Linkage now has a get_num_constraints method as syntactic sugar.

  • Code vulnerabilities checker

  • Walkthrough’s example has been expanded and now seems to be complete.

Changed in 0.3.0

  • Linkage’s method set_num_constraints behaviour changed! You should now add flat=False to come back to the previous behavior.

  • pylinkage/examples/fourbar_linkage.py expanded and finished.

  • The begin parameter of article_swarm_optimization is no longer mandatory. linkage.get_num_constraints() will be used if begin is not provided.

  • More flexible package version in environment.yml

  • Output file name now is formatted as “Kinematic {linkage.name}” in plot_kinematic_linkage function of pylinkage/visualizer.py

  • Python 3.6 is no longer tested in tox.ini. Python 3.9 is now tested.

Fixed in 0.3.0

  • When linkage animation was saved, last frames were often missing in pylinkage/visualizer.py, function plot_kinematic_linkage.

[0.2.2] - 2021-06-22

Added in 0.2.2

  • More continuous integration workflows for multiple Python versions.

Fixed in 0.2.2

  • README.md could not be seen in PyPi.

  • Various types

[0.2.1] - 2021-06-16

Added in 0.2.1

  • swarm_tiled_repr function for pylinkage/visualizer.py, for visualization of PySwarms.

  • EXPERIMENTAL! hyperstaticity method Linkage’s hyperstaticity (over constrained) calculation.

Changed in 0.2.1

  • pylinkage/exception.py now handles exceptions in another file.

  • Documentation improvements.

  • Python style improvements.

  • .gitignore now modified from the standard GitHub gitignore example for Python.

Fixed in 0.2.1

  • circle method of Pivot in pylinkage/linkage.py. It was causing errors

  • tox.ini now fixed.

[0.2.0] - 2021-06-14

Added in 0.2.0

  • pylinkage/vizualizer.py view your linkages using matplotlib!

  • Issue templates in .github/ISSUE_TEMPLATE/

  • .github/workflows/python-package-conda.yml: conda tests with unittest workflow.

  • CODE_OF_CONDUCT.md

  • MANIFEST.in

  • README.md

  • environment.yml

  • setup.cfg now replaces setup.py

  • tox.ini

  • CHANGELOG.md

Changed in 0.2.0

  • .gitignore Python Package specific extensions added

  • MIT LicenseLICENSE

  • lib/pylinkage/

  • tests/pylinkage/tests/

  • Revamped package organization.

  • Cleared setup.py

[0.0.1] - 2021-06-12

Added in 0.0.1

  • lib/geometry.py as a mathematical basis for kinematic optimization

  • lib/linkage.py, linkage builder

  • lib/optimizer.py, with Particle Swarm Optimization (built-in and PySwarms), and exhaustive optimization.

  • MIT License.

  • requirements.txt.

  • setup.py.

  • tests/__init__.py.

  • tests/test_geometry.py.

  • tests/test_linkage.py.

  • .gitignore.