13 Commits

Author SHA1 Message Date
forbes-0023
92ae57751f feat(solver): graph decomposition for cluster-by-cluster solving (phase 3)
Add a Python decomposition layer using NetworkX that partitions the
constraint graph into biconnected components (rigid clusters), orders
them via a block-cut tree, and solves each cluster independently.
Articulation-point bodies propagate as boundary conditions between
clusters.

New module kindred_solver/decompose.py:
- DOF table mapping BaseJointKind to residual counts
- Constraint graph construction (nx.MultiGraph)
- Biconnected component detection + articulation points
- Block-cut tree solve ordering (root-first from grounded cluster)
- Cluster-by-cluster solver with boundary body fix/unfix cycling
- Pebble game integration for per-cluster rigidity classification

Changes to existing modules:
- params.py: add unfix() for boundary body cycling
- solver.py: extract _monolithic_solve(), add decomposition branch
  for assemblies with >= 8 free bodies

Performance: for k clusters of ~n/k params each, total cost drops
from O(n^3) to O(n^3/k^2).

220 tests passing (up from 207).
2026-02-20 22:19:35 -06:00
forbes-0023
533ca91774 feat(solver): full constraint vocabulary — all 24 BaseJointKind types (phase 2)
Add 18 new constraint classes covering all BaseJointKind types from Types.h:
- Point: PointOnLine (2r), PointInPlane (1r)
- Orientation: Parallel (2r), Perpendicular (1r), Angle (1r)
- Surface: Concentric (4r), Tangent (1r), Planar (3r), LineInPlane (2r)
- Kinematic: Ball (3r), Revolute (5r), Cylindrical (4r), Slider (5r),
  Screw (5r), Universal (4r)
- Mechanical: Gear (1r), RackPinion (1r)
- Stubs: Cam, Slot, DistanceCylSph

New modules:
- geometry.py: marker axis extraction, vector ops (dot3, cross3, sub3),
  geometric primitives (point_plane_distance, point_line_perp_components)
- bfgs.py: L-BFGS-B fallback solver via scipy for when Newton fails

solver.py changes:
- Wire all 20 supported types in _build_constraint()
- BFGS fallback after Newton-Raphson in solve()

183 tests passing (up from 82), including:
- DOF counting for every joint type
- Solve convergence from displaced initial conditions
- Multi-body mechanisms (four-bar linkage, slider-crank, revolute chain)
2026-02-20 21:15:15 -06:00
forbes-0023
98051ba0c9 feat: add Phase 1 constraint solver addon, move prior content to GNN/
- Move existing OndselSolver, GNN ML layer, and tooling into GNN/
  directory for integration in later phases
- Add Create addon scaffold: package.xml, Init.py
- Add expression DAG with eval, symbolic diff, simplification
- Add parameter table with fixed/free variable tracking
- Add quaternion rotation as polynomial Expr trees
- Add RigidBody entity (7 DOF: position + unit quaternion)
- Add constraint classes: Coincident, DistancePointPoint, Fixed
- Add Newton-Raphson solver with symbolic Jacobian + numpy lstsq
- Add pre-solve passes: substitution + single-equation
- Add DOF counting via Jacobian SVD rank
- Add KindredSolver IKCSolver bridge for kcsolve integration
- Add 82 unit tests covering all modules

Registers as 'kindred' solver via kcsolve.register_solver() when
loaded by Create's addon_loader.
2026-02-20 20:35:47 -06:00
forbes
c728bd93f7 Merge remote-tracking branch 'public/main'
Some checks failed
CI / datagen (push) Blocked by required conditions
CI / lint (push) Failing after 2m20s
CI / test (push) Has been cancelled
CI / type-check (push) Has been cancelled
2026-02-03 18:03:54 -06:00
forbes
bbbc5e0137 ci: use venv for PEP 668 compatibility on runner 2026-02-03 17:59:05 -06:00
forbes
40cda51142 ci: install internal CA from IPA instead of skipping SSL verification
Fetches the Kindred CA cert from ipa.kindred.internal and installs it
into the system trust store before checkout. Removes GIT_SSL_NO_VERIFY.
2026-02-03 17:57:53 -06:00
forbes
e45207b7cc ci: skip SSL verification for internal Gitea runner 2026-02-03 17:56:13 -06:00
forbes
537d8c7689 ci: add datagen job, adapt workflow for Gitea runner
- Drop actions/setup-python, use system python3
- Use full Gitea-compatible action URLs
- CPU-only torch via pytorch whl/cpu index
- Add datagen job with cache/checkpoint resume and artifact upload
- Manual dispatch with configurable assembly count and worker count
- Datagen runs on push to main (after tests pass) or manual trigger
2026-02-03 17:52:48 -06:00
93bda28f67 feat(mates): add mate-level ground truth labels
Some checks failed
CI / lint (push) Successful in 1m45s
CI / type-check (push) Successful in 2m32s
CI / test (push) Failing after 3m36s
MateLabel and MateAssemblyLabels dataclasses with label_mate_assembly()
that back-attributes joint-level independence to originating mates.
Detects redundant and degenerate mates with pattern membership tracking.

Closes #15
2026-02-03 13:08:23 -06:00
239e45c7f9 feat(mates): add mate-based synthetic assembly generator
Some checks failed
CI / lint (push) Has been cancelled
CI / type-check (push) Has been cancelled
CI / test (push) Has been cancelled
SyntheticMateGenerator wraps existing joint generator with reverse
mapping (joint->mates) and configurable noise injection (redundant,
missing, incompatible mates). Batch generation via
generate_mate_training_batch().

Closes #14
2026-02-03 13:05:58 -06:00
118474f892 feat(mates): add mate-to-joint conversion and assembly analysis
Some checks failed
CI / lint (push) Has been cancelled
CI / type-check (push) Has been cancelled
CI / test (push) Has been cancelled
convert_mates_to_joints() bridges mate-level constraints to the existing
joint-based analysis pipeline. analyze_mate_assembly() orchestrates the
full pipeline with bidirectional mate-joint traceability.

Closes #13
2026-02-03 13:03:13 -06:00
e8143cf64c feat(mates): add joint pattern recognition
Some checks failed
CI / lint (push) Has been cancelled
CI / type-check (push) Has been cancelled
CI / test (push) Has been cancelled
JointPattern enum (9 patterns), PatternMatch dataclass, and
recognize_patterns() function with data-driven pattern rules.
Supports canonical, partial, and ambiguous pattern matching.

Closes #12
2026-02-03 12:59:53 -06:00
9f53fdb154 feat(mates): add mate type definitions and geometry references
Some checks failed
CI / lint (push) Has been cancelled
CI / type-check (push) Has been cancelled
CI / test (push) Has been cancelled
MateType enum (8 types), GeometryType enum (5 types), GeometryRef and
Mate dataclasses with validation, serialization, and context-dependent
DOF removal via dof_removed().

Closes #11
2026-02-03 12:55:37 -06:00
821 changed files with 9485 additions and 137 deletions

View File

@@ -1,65 +0,0 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
pip install ruff mypy
pip install -e ".[dev]" || pip install ruff mypy numpy
- name: Ruff check
run: ruff check solver/ freecad/ tests/ scripts/
- name: Ruff format check
run: ruff format --check solver/ freecad/ tests/ scripts/
type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
pip install mypy numpy
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install torch-geometric
pip install -e ".[dev]"
- name: Mypy
run: mypy solver/ freecad/
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install torch-geometric
pip install -e ".[train,dev]"
- name: Run tests
run: pytest tests/ freecad/tests/ -v --tb=short

View File

@@ -0,0 +1,209 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
inputs:
run_datagen:
description: "Run dataset generation"
required: false
type: boolean
default: false
num_assemblies:
description: "Number of assemblies to generate"
required: false
type: string
default: "100000"
num_workers:
description: "Parallel workers for datagen"
required: false
type: string
default: "4"
env:
PIP_CACHE_DIR: /tmp/pip-cache-solver
TORCH_INDEX: https://download.pytorch.org/whl/cpu
VIRTUAL_ENV: /tmp/solver-venv
jobs:
# ---------------------------------------------------------------------------
# Lint — fast, no torch required
# ---------------------------------------------------------------------------
lint:
runs-on: ubuntu-latest
env:
PATH: /tmp/solver-venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
steps:
- name: Trust internal CA
run: |
curl -sk https://ipa.kindred.internal/ipa/config/ca.crt \
-o /usr/local/share/ca-certificates/kindred-internal.crt
update-ca-certificates
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Set up venv
run: python3 -m venv $VIRTUAL_ENV
- name: Install lint tools
run: pip install --cache-dir $PIP_CACHE_DIR ruff
- name: Ruff check
run: ruff check solver/ freecad/ tests/ scripts/
- name: Ruff format check
run: ruff format --check solver/ freecad/ tests/ scripts/
# ---------------------------------------------------------------------------
# Type check
# ---------------------------------------------------------------------------
type-check:
runs-on: ubuntu-latest
env:
PATH: /tmp/solver-venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
steps:
- name: Trust internal CA
run: |
curl -sk https://ipa.kindred.internal/ipa/config/ca.crt \
-o /usr/local/share/ca-certificates/kindred-internal.crt
update-ca-certificates
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Set up venv
run: python3 -m venv $VIRTUAL_ENV
- name: Install dependencies
run: |
pip install --cache-dir $PIP_CACHE_DIR \
mypy numpy scipy \
torch --index-url $TORCH_INDEX
pip install --cache-dir $PIP_CACHE_DIR torch-geometric
pip install --cache-dir $PIP_CACHE_DIR -e ".[dev]"
- name: Mypy
run: mypy solver/ freecad/
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
test:
runs-on: ubuntu-latest
env:
PATH: /tmp/solver-venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
steps:
- name: Trust internal CA
run: |
curl -sk https://ipa.kindred.internal/ipa/config/ca.crt \
-o /usr/local/share/ca-certificates/kindred-internal.crt
update-ca-certificates
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Set up venv
run: python3 -m venv $VIRTUAL_ENV
- name: Install dependencies
run: |
pip install --cache-dir $PIP_CACHE_DIR \
torch --index-url $TORCH_INDEX
pip install --cache-dir $PIP_CACHE_DIR torch-geometric
pip install --cache-dir $PIP_CACHE_DIR -e ".[train,dev]"
- name: Run tests
run: pytest tests/ freecad/tests/ -v --tb=short
# ---------------------------------------------------------------------------
# Dataset generation — manual trigger or on main push
# ---------------------------------------------------------------------------
datagen:
runs-on: ubuntu-latest
if: >-
(github.event_name == 'workflow_dispatch' && inputs.run_datagen == true) ||
(github.event_name == 'push' && github.ref == 'refs/heads/main')
needs: [test]
env:
PATH: /tmp/solver-venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
steps:
- name: Trust internal CA
run: |
curl -sk https://ipa.kindred.internal/ipa/config/ca.crt \
-o /usr/local/share/ca-certificates/kindred-internal.crt
update-ca-certificates
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Set up venv
run: python3 -m venv $VIRTUAL_ENV
- name: Install dependencies
run: |
pip install --cache-dir $PIP_CACHE_DIR \
torch --index-url $TORCH_INDEX
pip install --cache-dir $PIP_CACHE_DIR torch-geometric
pip install --cache-dir $PIP_CACHE_DIR -e ".[train]"
- name: Restore datagen checkpoint
id: datagen-cache
uses: https://github.com/actions/cache/restore@v4
with:
path: data/synthetic
key: datagen-${{ github.sha }}
restore-keys: |
datagen-
- name: Generate dataset
run: |
NUM=${INPUTS_NUM_ASSEMBLIES:-100000}
WORKERS=${INPUTS_NUM_WORKERS:-4}
echo "Generating ${NUM} assemblies with ${WORKERS} workers"
python3 scripts/generate_synthetic.py \
--num-assemblies "${NUM}" \
--num-workers "${WORKERS}" \
--output-dir data/synthetic
env:
INPUTS_NUM_ASSEMBLIES: ${{ inputs.num_assemblies }}
INPUTS_NUM_WORKERS: ${{ inputs.num_workers }}
- name: Save datagen checkpoint
if: always()
uses: https://github.com/actions/cache/save@v4
with:
path: data/synthetic
key: datagen-${{ github.sha }}
- name: Upload dataset
uses: https://github.com/actions/upload-artifact@v3
with:
name: synthetic-dataset
path: |
data/synthetic/index.json
data/synthetic/stats.json
data/synthetic/shards/
retention-days: 90
- name: Print summary
if: always()
run: |
echo "=== Dataset Generation Results ==="
if [ -f data/synthetic/stats.json ]; then
python3 -c "
import json
with open('data/synthetic/stats.json') as f:
s = json.load(f)
print(f'Total examples: {s[\"total_examples\"]}')
print(f'Classification: {json.dumps(s[\"classification_distribution\"], indent=2)}')
print(f'Rigid: {s[\"rigidity\"][\"rigid_fraction\"]*100:.1f}%')
print(f'Degeneracy: {s[\"geometric_degeneracy\"][\"fraction_with_degeneracy\"]*100:.1f}%')
"
else
echo "stats.json not found — generation may have failed"
ls -la data/synthetic/ 2>/dev/null || echo "output dir missing"
fi

Some files were not shown because too many files have changed in this diff Show More