ArshPathan's picture
Upload folder using huggingface_hub
f120404
|
Raw History Blame Contribute Delete
13.3 kB

MaterialForge β€” Implementation Plan

AI-Driven Atomic Structure Discovery Environment for the Meta PyTorch OpenEnv Hackathon


1. Project Summary

MaterialForge is an OpenEnv-compliant RL environment where an AI agent designs atomic crystal structures to match target material properties (hardness, conductivity, thermal resistance, elasticity). The agent places/replaces/removes atoms on a lattice grid, and the environment scores each configuration against the target using physics-inspired heuristics + optional LLM judging.

Deadline: April 8, 2026 (Round 1 submission)


2. Architecture Overview

material_forge_env/
β”œβ”€β”€ __init__.py                          # Public exports
β”œβ”€β”€ models.py                            # Action, Observation, State Pydantic models
β”œβ”€β”€ client.py                            # EnvClient (WebSocket client)
β”œβ”€β”€ config.py                            # Constants: atom types, grid size, property ranges
β”œβ”€β”€ lattice.py                           # Core lattice data structure + manipulation
β”œβ”€β”€ physics.py                           # Property estimation from atomic structure
β”œβ”€β”€ scenarios.py                         # Target profile generators (easy/medium/hard)
β”œβ”€β”€ rubrics.py                           # Reward rubrics (heuristic + optional LLM)
β”œβ”€β”€ llm_utils.py                         # Shared LLM client factory (graceful degradation)
β”œβ”€β”€ hint_engine.py                       # Optional LLM hint system for struggling agents
β”œβ”€β”€ openenv.yaml
β”œβ”€β”€ pyproject.toml
β”œβ”€β”€ README.md
└── server/
    β”œβ”€β”€ __init__.py
    β”œβ”€β”€ app.py                           # FastAPI + create_app
    β”œβ”€β”€ material_forge_env_environment.py # Environment implementation
    β”œβ”€β”€ Dockerfile
    └── requirements.txt

3. Core Data Models

3.1 Atom Types & Properties

Atom Symbol Description Cost Hardness Conductivity Thermal Res Elasticity
A A Strong metal 8 High Medium High Low
B B Conductive element 6 Low High Low Medium
C C Insulating ceramic 4 Medium Low High Low
P P Polymer-like 2 Low Low Medium High
. empty Vacant site 0 β€” β€” β€” β€”

3.2 Action Model (MaterialForgeAction)

class MaterialForgeAction(Action):
    action_type: Literal["place", "replace", "remove"]
    row: int          # 0-indexed grid position
    col: int          # 0-indexed grid position
    atom: Optional[Literal["A", "B", "C", "P"]] = None  # required for place/replace

3.3 Observation Model (MaterialForgeObservation)

class MaterialForgeObservation(Observation):
    grid: List[List[str]]              # Current lattice state (e.g. 8x8)
    target: Dict[str, float]           # Target properties {hardness: 90, ...}
    current_properties: Dict[str, float]  # Estimated properties of current structure
    phase: str                         # "crystalline" | "polycrystalline" | "amorphous"
    total_cost: float                  # Sum of atom costs
    cost_budget: float                 # Max allowed cost
    step_number: int                   # Current step in episode
    max_steps: int                     # Episode length limit
    score_breakdown: Dict[str, float]  # Per-component reward breakdown
    hint: Optional[str] = None         # LLM hint (when available)
    # Inherited: done, reward, metadata

3.4 State

Uses the default OpenEnv State with episode_id and step_count.


4. Core Modules

4.1 config.py β€” Constants & Configuration

  • GRID_SIZE = 8 (8Γ—8 lattice)
  • MAX_STEPS = 50 per episode
  • ATOM_TYPES dict with per-atom property contributions and costs
  • PROPERTY_NAMES = ["hardness", "conductivity", "thermal_resistance", "elasticity"]
  • COST_BUDGET_DEFAULT = 80
  • Difficulty presets: easy (wide tolerance), medium, hard (tight tolerance)

4.2 lattice.py β€” Lattice Engine

Core data structure representing the material unit cell.

Class Lattice:

  • __init__(size: int) β€” create empty grid
  • place(row, col, atom) β†’ bool (validates bounds + empty)
  • replace(row, col, atom) β†’ bool (validates bounds + occupied)
  • remove(row, col) β†’ bool (validates bounds + occupied)
  • get_grid() β†’ List[List[str]]
  • count_atoms() β†’ Dict[str, int]
  • total_cost() β†’ float
  • get_neighbors(row, col) β†’ List[str] β€” 8-connected neighbors
  • clone() β†’ Lattice β€” deep copy for rollback
  • from_grid(grid: List[List[str]]) β†’ Lattice β€” deserialize

4.3 physics.py β€” Property Estimation

Heuristic-based physics engine. No real MD simulation, but plausible.

Functions:

  • estimate_properties(lattice: Lattice) β†’ Dict[str, float]
    • Hardness: f(metal density, bonding density, packing ratio, crystal order)
    • Conductivity: f(conductive atom fraction, connectivity paths, percolation)
    • Thermal Resistance: f(insulator fraction, structural order, density)
    • Elasticity: f(polymer fraction, amorphous ratio, void fraction)
  • classify_phase(lattice: Lattice) β†’ str
    • Checks for repeating patterns β†’ "crystalline"
    • Checks for regional order β†’ "polycrystalline"
    • Default β†’ "amorphous"
  • compute_stability(lattice: Lattice) β†’ float
    • Penalizes isolated atoms (no neighbors)
    • Rewards dense bonding clusters
    • Checks structural symmetry

Property calculation strategy: Each property is a weighted sum of atomic contributions + structural bonuses:

property = base_contribution(atom_counts) + neighbor_bonus + phase_bonus + symmetry_bonus

All properties normalized to 0–100 scale.

4.4 scenarios.py β€” Target Profile Generator

Functions:

  • generate_scenario(difficulty: str) β†’ Dict
    • Returns: {target: {hardness, conductivity, ...}, cost_budget, tolerance, max_steps}
    • "easy": large tolerance (Β±20), generous budget, fewer properties to match
    • "medium": moderate tolerance (Β±10), standard budget
    • "hard": tight tolerance (Β±5), tight budget, all properties must match
  • generate_llm_scenario() β€” optional LLM-generated creative scenario with narrative
  • Predefined classic scenarios: "diamond-like", "conductor", "heat-shield", "flexible-polymer"

4.5 rubrics.py β€” Reward System

HeuristicRewardRubric(Rubric):

def forward(action, observation) -> float:
    property_match = 1 - mean(|current[p] - target[p]| / 100 for p in properties)
    stability = compute_stability(lattice)
    phase_bonus = phase_alignment_score(current_phase, target_phase)
    cost_penalty = max(0, (total_cost - budget) / budget)

    reward = (0.50 * property_match
            + 0.25 * stability
            + 0.15 * lattice_quality
            + 0.10 * phase_bonus
            - cost_penalty)
    return clamp(reward, 0.0, 1.0)

MaterialDesignLLMJudge(LLMJudge): (optional, 30% weight when available)

  • Prompt: "Given target {target} and structure {grid}, rate quality 0-10"
  • Uses HF Inference API via HF_TOKEN
  • Falls back to heuristic-only if unavailable

Composite Rubric (via WeightedSum):

  • With LLM: WeightedSum([HeuristicRubric, LLMJudge], [0.7, 0.3])
  • Without LLM: HeuristicRubric only

4.6 llm_utils.py β€” LLM Client

  • Reads HF_TOKEN from environment
  • Creates OpenAIClient pointing to HF Inference API
  • Returns None gracefully when no token available
  • All LLM features degrade gracefully (heuristic-only mode)

4.7 hint_engine.py β€” Agent Hints

  • Rate-limited: max 3 hints per episode
  • Only triggers when agent is struggling (reward declining for 5+ steps)
  • Provides textual guidance: "Try adding more metal atoms in the center for hardness"
  • Returned in observation.hint field

5. Environment Logic (MaterialForgeEnvironment)

5.1 reset()

  1. Generate scenario (random difficulty or specified)
  2. Initialize empty 8Γ—8 lattice
  3. Reset rubric state via self._reset_rubric()
  4. Return initial observation with empty grid + target properties

5.2 step(action)

  1. Validate action (bounds, atom type, action_type logic)
  2. Apply action to lattice (place/replace/remove)
  3. Estimate current properties via physics.estimate_properties()
  4. Classify structural phase via physics.classify_phase()
  5. Compute reward via self._apply_rubric(action, observation)
  6. Check termination: done = (step >= max_steps) or (all properties within tolerance)
  7. Optionally generate hint if agent is struggling
  8. Return observation

5.3 Termination Conditions

  • Success: All target properties within tolerance β†’ bonus reward
  • Budget exceeded: Cost > budget β†’ penalty, episode continues (agent can remove atoms)
  • Max steps: Episode ends, final reward reflects best configuration
  • Perfect match: Early termination with maximum reward

6. Client (MaterialForgeEnv)

Update the existing scaffold client to:

  • Serialize MaterialForgeAction properly
  • Parse MaterialForgeObservation with grid, properties, phase, etc.
  • Handle the new observation fields in _parse_result

7. Deployment

7.1 Local Development

cd material_forge_env
uv sync
uv run server  # http://localhost:8000

7.2 Docker

cd material_forge_env/server
docker build -t material-forge-env:latest .
docker run -p 8000:8000 material-forge-env:latest

7.3 Hugging Face Spaces

cd material_forge_env
openenv push  # Deploys to HF Spaces with Gradio UI

8. Implementation Order

Phase 1: Core Engine (Priority)

  1. config.py β€” Define atom types, grid constants, property definitions
  2. lattice.py β€” Grid data structure with place/replace/remove operations
  3. physics.py β€” Property estimation heuristics + phase classification
  4. models.py β€” Rewrite Action/Observation with full MaterialForge fields
  5. scenarios.py β€” Target profile generator with difficulty levels

Phase 2: Environment Integration

  1. rubrics.py β€” HeuristicRewardRubric
  2. material_forge_env_environment.py β€” Wire up reset/step with lattice + physics + rubric
  3. client.py β€” Update client serialization/parsing
  4. server/app.py β€” Update create_app with rubric

Phase 3: LLM Features (Optional Enhancement)

  1. llm_utils.py β€” HF Inference API client
  2. rubrics.py β€” Add LLMJudge + WeightedSum composite
  3. hint_engine.py β€” Contextual hint generation
  4. scenarios.py β€” LLM-generated creative scenarios

Phase 4: Polish & Submission

  1. README.md β€” Full documentation with examples
  2. Demo script β€” demo.py showing agent interaction loop
  3. Tests β€” Validate environment correctness
  4. Docker build β€” Verify containerized deployment
  5. HF Spaces β€” Deploy and get demo link

9. Evaluation Criteria Alignment

Criteria How We Address It
Correctness Physics heuristics produce plausible material properties; validated with known material archetypes
OpenEnv Compliance Standard Environment base class, Action/Observation models, create_app, rubric system
Task Design Quality Rich action space (place/replace/remove Γ— 4 atoms Γ— 64 positions), meaningful search problem
Reward Logic Multi-component reward: property match + stability + lattice quality + phase alignment - cost
Code Quality Modular design, type hints, docstrings, separated concerns

10. Key Design Decisions

  1. 2D grid (not 3D) β€” Keeps complexity manageable for Round 1; 3D is a Phase 2 extension
  2. Heuristic physics β€” No real DFT/MD simulation; plausible approximations that create a meaningful search landscape
  3. 4 atom types β€” Enough variety for interesting compositions without overwhelming the action space
  4. 8Γ—8 grid β€” 64 positions Γ— 5 options (4 atoms + empty) = rich enough search space
  5. 50 max steps β€” Allows iterative refinement without infinite episodes
  6. LLM features optional β€” Everything works without HF_TOKEN; LLM adds richness when available
  7. WeightedSum rubric β€” Clean separation of heuristic (70%) and LLM (30%) scoring

11. Submission Checklist

  • Public GitHub repository
  • Complete README.md with setup instructions
  • requirements.txt in server/
  • pyproject.toml with all dependencies
  • Demo script (demo.py)
  • Working Docker build
  • Hugging Face Spaces demo link
  • OpenEnv validation passes (openenv validate)