math-solver / solver /engine.py
Cuong2004
Deploy API from GitHub Actions
1993d5c
Raw
History Blame Contribute Delete
5.73 kB
"""Geometry Engine: Modular Geometry IR Pipeline Coordinator."""
from __future__ import annotations
import logging
from typing import Any, Dict, List, Optional
from .models import Point, Constraint
from .constraint_compiler import ConstraintCompiler, CompiledSystem
from .constructors import StandardGeometryConstructor
from .coordinate_solver import CoordinateSolver
from .geometry_normalizer import GeometryNormalizer
from .topology_builder import TopologyBuilder
from .result_assembler import ResultAssembler
logger = logging.getLogger(__name__)
class GeometryEngine:
"""
Modular Geometry Solver Engine (P4 Architecture):
- ConstraintCompiler: Compiles DSL & constraints into algebraic systems
- StandardGeometryConstructor: Canonical analytical construction for standard primitives
- CoordinateSolver: Numerical & symbolic solver for equations
- GeometryNormalizer: Centering, orientation, bounding box and coordinate scaling
- TopologyBuilder: Drawing phases, faces, solids, and complete visualization graph
- ResultAssembler: Canonical Geometry IR assembly
"""
def __init__(self):
self.compiler = ConstraintCompiler()
self.constructor = StandardGeometryConstructor()
self.coord_solver = CoordinateSolver()
self.normalizer = GeometryNormalizer()
self.topology_builder = TopologyBuilder()
self.assembler = ResultAssembler()
def solve(
self,
points: List[Point] | Dict[str, Point],
constraints: List[Constraint],
is_3d: bool = False,
) -> Optional[Dict[str, Any]]:
if not points:
logger.error("[GeometryEngine] No points to solve.")
return None
pt_list = list(points.values()) if isinstance(points, dict) else list(points)
logger.info(f"==[GeometryEngine] Starting solve with {len(pt_list)} points, {len(constraints)} constraints (is_3d={is_3d})==")
# 1. Compile constraints and symbols
system: CompiledSystem = self.compiler.compile(pt_list, constraints, is_3d=is_3d)
# 2. Step 0: Try Canonical Hierarchical Constructor
canonical_res = self.constructor.try_construct(
pt_list,
system.real_constraints,
system.solids_meta,
is_3d,
)
if canonical_res and "coordinates" in canonical_res:
logger.info("[GeometryEngine] Successfully constructed canonical standard representation.")
return self._build_result(
canonical_res["coordinates"],
system.polygon_order,
system.circles_meta,
system.solids_meta,
system.segments_meta,
system.lines_ext,
system.rays_ext,
pt_list,
system.real_constraints,
)
# 3. Solve numerical / symbolic coordinate equations
raw_coords = self.coord_solver.solve(system, is_3d=is_3d)
if not raw_coords:
logger.error("[GeometryEngine] CoordinateSolver failed to find a valid solution.")
return None
# 4. Normalize coordinates and assemble Geometry IR
return self._build_result(
raw_coords,
system.polygon_order,
system.circles_meta,
system.solids_meta,
system.segments_meta,
system.lines_ext,
system.rays_ext,
pt_list,
system.real_constraints,
)
def _build_result(
self,
coords: Dict[str, List[float]],
polygon_order: List[str],
circles_meta: List[Dict[str, Any]],
solids_meta: List[Dict[str, Any]],
segments_meta: List[List[str]],
lines_meta: List[List[str]],
rays_meta: List[List[str]],
pt_list: List[Point],
constraints_meta: Optional[List[Constraint]] = None,
) -> Dict[str, Any]:
"""Backward-compatible result builder delegating to modular topology & assembler."""
# 1. Clean float zero residuals and restore explicit coordinates
cleaned_coords = coords.copy()
for p in pt_list:
if p.id in cleaned_coords:
if p.x is not None:
cleaned_coords[p.id][0] = float(p.x)
elif abs(cleaned_coords[p.id][0]) < 1e-10:
cleaned_coords[p.id][0] = 0.0
if p.y is not None:
cleaned_coords[p.id][1] = float(p.y)
elif abs(cleaned_coords[p.id][1]) < 1e-10:
cleaned_coords[p.id][1] = 0.0
if len(cleaned_coords[p.id]) >= 3:
if p.z is not None:
cleaned_coords[p.id][2] = float(p.z)
elif abs(cleaned_coords[p.id][2]) < 1e-10:
cleaned_coords[p.id][2] = 0.0
for pid in list(cleaned_coords.keys()):
for idx in range(len(cleaned_coords[pid])):
if abs(cleaned_coords[pid][idx]) < 1e-10:
cleaned_coords[pid][idx] = 0.0
# 2. Build topology and visualization graph
topology_data = self.topology_builder.build_topology(
coords=cleaned_coords,
polygon_order=polygon_order,
circles_meta=circles_meta,
solids_meta=solids_meta,
segments_meta=segments_meta,
lines_meta=lines_meta,
rays_meta=rays_meta,
pt_list=pt_list,
constraints_meta=constraints_meta,
)
# 3. Assemble canonical IR
return self.assembler.assemble(
coordinates=cleaned_coords,
topology_data=topology_data,
)