|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +"""Canonical Mechanics authority for a control-ready Rig + Skin pair. |
| 4 | +
|
| 5 | +The Compiler does not invent mechanical behavior here. It qualifies that the |
| 6 | +already-canonical AXIS rig program is executable by the declared deterministic |
| 7 | +Mechanics semantics, that zero-control rest is stable, and that CanonicalSkin can |
| 8 | +be deformed without lineage or numeric failure. The canonical output seals that |
| 9 | +execution contract for runtime/Motion consumers. |
| 10 | +""" |
| 11 | + |
| 12 | +from dataclasses import asdict, dataclass, field, replace |
| 13 | +from typing import Any |
| 14 | +import math |
| 15 | + |
| 16 | +import numpy as np |
| 17 | + |
| 18 | +from .canonical_mesh_ir_v1 import CanonicalMeshIR, canonical_mesh_lineage_hash_v1 |
| 19 | +from .canonical_rig_ir_v2 import CanonicalRigIRV2, canonical_rig_lineage_hash_v2 |
| 20 | +from .canonical_skin_ir_v2 import CanonicalSkinIRV2, canonical_skin_lineage_hash_v2 |
| 21 | +from .hashing import content_sha256 |
| 22 | +from .mechanics_control_solver_v1 import deform_mesh_lbs_v1, solve_control_pose_v1 |
| 23 | +from .rig_constraint_schema_v1 import RIG_CONSTRAINT_OPERATOR_IDS_V1, validate_rig_constraint_operator_v1 |
| 24 | +from .types import QualificationError |
| 25 | + |
| 26 | +Json = dict[str, Any] |
| 27 | + |
| 28 | + |
| 29 | +@dataclass(frozen=True) |
| 30 | +class CanonicalMechanicsIRV1: |
| 31 | + mesh_lineage_hash: str |
| 32 | + rig_lineage_hash: str |
| 33 | + deform_structure_hash: str |
| 34 | + control_program_hash: str |
| 35 | + skin_lineage_hash: str |
| 36 | + constraint_schedule: tuple[str, ...] |
| 37 | + executable_operator_ids: tuple[str, ...] |
| 38 | + rest_pose_hash: str |
| 39 | + qualification_hash: str |
| 40 | + mechanics_lineage_hash: str |
| 41 | + schema_version: str = "RealSaS.CanonicalMechanicsIR.v1" |
| 42 | + metadata: Json = field(default_factory=dict) |
| 43 | + |
| 44 | + def to_dict(self) -> dict: |
| 45 | + return asdict(self) |
| 46 | + |
| 47 | + |
| 48 | +def canonical_mechanics_lineage_hash_v1(value: CanonicalMechanicsIRV1) -> str: |
| 49 | + payload = value.to_dict() |
| 50 | + payload.pop("mechanics_lineage_hash", None) |
| 51 | + return content_sha256(payload) |
| 52 | + |
| 53 | + |
| 54 | +def _validate_local_frames(rig: CanonicalRigIRV2) -> None: |
| 55 | + by_joint = {f.canonical_joint_id: f for f in rig.local_frames} |
| 56 | + joint_ids = {j.canonical_joint_id for j in rig.joints} |
| 57 | + if set(by_joint) != joint_ids: |
| 58 | + raise QualificationError("MECHANICS_FRAME_COVERAGE_DRIFT") |
| 59 | + for jid, frame in by_joint.items(): |
| 60 | + basis = np.asarray(frame.basis, dtype=np.float64) |
| 61 | + if basis.shape != (3, 3) or not np.isfinite(basis).all(): |
| 62 | + raise QualificationError("MECHANICS_FRAME_INVALID") |
| 63 | + gram = basis @ basis.T |
| 64 | + if float(np.max(np.abs(gram - np.eye(3)))) > 1e-4: |
| 65 | + raise QualificationError(f"MECHANICS_FRAME_NOT_ORTHONORMAL:{jid}") |
| 66 | + det = float(np.linalg.det(basis)) |
| 67 | + if not math.isfinite(det) or abs(det - 1.0) > 1e-4: |
| 68 | + raise QualificationError(f"MECHANICS_FRAME_NOT_RIGHT_HANDED:{jid}") |
| 69 | + |
| 70 | + |
| 71 | +def mint_canonical_mechanics_v1( |
| 72 | + mesh: CanonicalMeshIR, |
| 73 | + rig: CanonicalRigIRV2, |
| 74 | + skin: CanonicalSkinIRV2, |
| 75 | + *, |
| 76 | + rest_position_tolerance: float = 1e-5, |
| 77 | + rest_constraint_tolerance: float = 1e-4, |
| 78 | +) -> CanonicalMechanicsIRV1: |
| 79 | + if mesh.mesh_lineage_hash != canonical_mesh_lineage_hash_v1(mesh): |
| 80 | + raise QualificationError("MECHANICS_MESH_LINEAGE_DRIFT") |
| 81 | + if rig.rig_lineage_hash != canonical_rig_lineage_hash_v2(rig): |
| 82 | + raise QualificationError("MECHANICS_RIG_LINEAGE_DRIFT") |
| 83 | + if skin.skin_lineage_hash != canonical_skin_lineage_hash_v2(skin): |
| 84 | + raise QualificationError("MECHANICS_SKIN_LINEAGE_DRIFT") |
| 85 | + if rig.mesh_lineage_hash != mesh.mesh_lineage_hash or skin.mesh_lineage_hash != mesh.mesh_lineage_hash: |
| 86 | + raise QualificationError("MECHANICS_PARENT_MESH_DRIFT") |
| 87 | + if skin.deform_structure_hash != rig.deform_structure_hash: |
| 88 | + raise QualificationError("MECHANICS_DEFORM_STRUCTURE_DRIFT") |
| 89 | + if rest_position_tolerance < 0.0 or rest_constraint_tolerance < 0.0: |
| 90 | + raise QualificationError("MECHANICS_TOLERANCE_INVALID") |
| 91 | + |
| 92 | + _validate_local_frames(rig) |
| 93 | + chain_ids = {c.canonical_chain_id for c in rig.chains} |
| 94 | + control_ids = {c.canonical_control_id for c in rig.controls} |
| 95 | + joint_ids = {j.canonical_joint_id for j in rig.joints} |
| 96 | + for constraint in rig.constraints: |
| 97 | + if any(cid not in control_ids for cid in constraint.input_canonical_control_ids): |
| 98 | + raise QualificationError("MECHANICS_CONSTRAINT_CONTROL_UNKNOWN") |
| 99 | + if any(jid not in joint_ids for jid in constraint.output_canonical_joint_ids): |
| 100 | + raise QualificationError("MECHANICS_CONSTRAINT_JOINT_UNKNOWN") |
| 101 | + if constraint.canonical_chain_id is not None and constraint.canonical_chain_id not in chain_ids: |
| 102 | + raise QualificationError("MECHANICS_CONSTRAINT_CHAIN_UNKNOWN") |
| 103 | + validate_rig_constraint_operator_v1( |
| 104 | + operator_id=constraint.operator_id, |
| 105 | + input_control_count=len(constraint.input_canonical_control_ids), |
| 106 | + output_joint_count=len(constraint.output_canonical_joint_ids), |
| 107 | + has_chain=constraint.canonical_chain_id is not None, |
| 108 | + parameters=constraint.parameters, |
| 109 | + ) |
| 110 | + |
| 111 | + rest_pose = solve_control_pose_v1(rig, ()) |
| 112 | + deformed = deform_mesh_lbs_v1(mesh, rig, skin, rest_pose) |
| 113 | + mesh_by_id = {str(v.canonical_mesh_vertex_id): np.asarray(v.P, dtype=np.float64) for v in mesh.vertices} |
| 114 | + max_rest_drift = 0.0 |
| 115 | + for vid, value in zip(deformed.canonical_mesh_vertex_ids, deformed.positions): |
| 116 | + max_rest_drift = max(max_rest_drift, float(np.linalg.norm(np.asarray(value, dtype=np.float64) - mesh_by_id[vid]))) |
| 117 | + max_constraint_residual = max((float(v) for _, v in rest_pose.constraint_residuals), default=0.0) |
| 118 | + if max_rest_drift > float(rest_position_tolerance): |
| 119 | + raise QualificationError(f"MECHANICS_REST_POSE_DRIFT:{max_rest_drift:.9g}") |
| 120 | + if max_constraint_residual > float(rest_constraint_tolerance): |
| 121 | + raise QualificationError(f"MECHANICS_REST_CONSTRAINT_RESIDUAL:{max_constraint_residual:.9g}") |
| 122 | + |
| 123 | + rest_pose_hash = content_sha256(rest_pose.to_dict()) |
| 124 | + qualification = { |
| 125 | + "schema": "RealSaS.MechanicsQualification.v1", |
| 126 | + "status": "PASS_MECHANICS_EXECUTABLE", |
| 127 | + "frame_coverage": len(rig.local_frames), |
| 128 | + "control_count": len(rig.controls), |
| 129 | + "chain_count": len(rig.chains), |
| 130 | + "constraint_count": len(rig.constraints), |
| 131 | + "max_rest_position_drift": max_rest_drift, |
| 132 | + "max_rest_constraint_residual": max_constraint_residual, |
| 133 | + "rest_position_tolerance": float(rest_position_tolerance), |
| 134 | + "rest_constraint_tolerance": float(rest_constraint_tolerance), |
| 135 | + "constraint_solver_executed": True, |
| 136 | + "rig_program_mutated": False, |
| 137 | + "skin_mutated": False, |
| 138 | + "learned_inference_performed": False, |
| 139 | + } |
| 140 | + qualification_hash = content_sha256(qualification) |
| 141 | + value = CanonicalMechanicsIRV1( |
| 142 | + mesh_lineage_hash=mesh.mesh_lineage_hash, |
| 143 | + rig_lineage_hash=rig.rig_lineage_hash, |
| 144 | + deform_structure_hash=rig.deform_structure_hash, |
| 145 | + control_program_hash=rig.control_program_hash, |
| 146 | + skin_lineage_hash=skin.skin_lineage_hash, |
| 147 | + constraint_schedule=tuple(c.canonical_constraint_id for c in rig.constraints), |
| 148 | + executable_operator_ids=RIG_CONSTRAINT_OPERATOR_IDS_V1, |
| 149 | + rest_pose_hash=rest_pose_hash, |
| 150 | + qualification_hash=qualification_hash, |
| 151 | + mechanics_lineage_hash="", |
| 152 | + metadata={ |
| 153 | + "authority": "COMPILER_CANONICAL_MECHANICS", |
| 154 | + "constraint_execution_semantics": "MECHANICS_CONTROL_SOLVER_V1", |
| 155 | + "post_bind_frame_observability_owner": "MECHANICS", |
| 156 | + "canonical_constraint_solution_rest_pose": True, |
| 157 | + "runtime_control_pose_solution_uses_same_solver": True, |
| 158 | + "compiler_rig_synthesis_performed": False, |
| 159 | + "motion_authority_claimed": False, |
| 160 | + "graphics_authority_claimed": False, |
| 161 | + }, |
| 162 | + ) |
| 163 | + return replace(value, mechanics_lineage_hash=canonical_mechanics_lineage_hash_v1(value)) |
| 164 | + |
| 165 | + |
| 166 | +def canonical_mechanics_from_dict_v1(payload: dict[str, Any]) -> CanonicalMechanicsIRV1: |
| 167 | + schema = str(payload.get("schema_version") or payload.get("schema") or "") |
| 168 | + if schema != "RealSaS.CanonicalMechanicsIR.v1": |
| 169 | + raise QualificationError("MECHANICS_SCHEMA_DRIFT") |
| 170 | + value = CanonicalMechanicsIRV1( |
| 171 | + mesh_lineage_hash=str(payload["mesh_lineage_hash"]), |
| 172 | + rig_lineage_hash=str(payload["rig_lineage_hash"]), |
| 173 | + deform_structure_hash=str(payload["deform_structure_hash"]), |
| 174 | + control_program_hash=str(payload["control_program_hash"]), |
| 175 | + skin_lineage_hash=str(payload["skin_lineage_hash"]), |
| 176 | + constraint_schedule=tuple(map(str, payload.get("constraint_schedule") or ())), |
| 177 | + executable_operator_ids=tuple(map(str, payload.get("executable_operator_ids") or ())), |
| 178 | + rest_pose_hash=str(payload["rest_pose_hash"]), |
| 179 | + qualification_hash=str(payload["qualification_hash"]), |
| 180 | + mechanics_lineage_hash=str(payload["mechanics_lineage_hash"]), |
| 181 | + schema_version=schema, |
| 182 | + metadata=dict(payload.get("metadata") or {}), |
| 183 | + ) |
| 184 | + if value.mechanics_lineage_hash != canonical_mechanics_lineage_hash_v1(value): |
| 185 | + raise QualificationError("MECHANICS_LINEAGE_HASH_DRIFT") |
| 186 | + if value.metadata.get("authority") != "COMPILER_CANONICAL_MECHANICS": |
| 187 | + raise QualificationError("MECHANICS_AUTHORITY_DRIFT") |
| 188 | + return value |
| 189 | + |
| 190 | + |
| 191 | +__all__ = [ |
| 192 | + "CanonicalMechanicsIRV1", "canonical_mechanics_from_dict_v1", |
| 193 | + "canonical_mechanics_lineage_hash_v1", "mint_canonical_mechanics_v1", |
| 194 | +] |
0 commit comments