Dane Sabo cebf8c167a Initial umbrella repo: thesis + FRET pipeline + plant model with first controllers
Folds three previously-separate pieces into one preliminary-example repo
for the HAHACS thesis:

- thesis/ (submodule) → gitea Thesis.git — the PhD proposal
- fret-pipeline/ — FRET requirements to AIGER controller (was
  ~/Documents/fret_processing/; prior single-commit history abandoned
  per user decision)
- plant-model/ — 10-state PKE + lumped T/H PWR model (was
  ~/Documents/PKE_Playground/; never version-controlled before)
- presentations/2026DICE/ (submodule) → gitea 2026DICE.git
- reachability/, hardware/ — empty placeholders for Thrust 3 and HIL
- docs/architecture.md — how the discrete and continuous layers compose
- claude_memory/ — session notes and scratch knowledge pattern

Plant model refactored to thesis naming (x, plant, u, ref); pke_th_rhs
now takes u as an explicit arg instead of reading rho_ext from the
params struct. First two controllers built to the contract
u = ctrl_<mode>(t, x, plant, ref): ctrl_null (baseline) and
ctrl_operation (stabilizing, proportional on T_avg). Validated under a
100% -> 80% Q_sg step: ctrl_operation reduces steady-state T_avg drift
~47% vs. the unforced plant.

Root CLAUDE.md emphasizes that CLAUDE.md files are living documents and
that any knowledge not captured before a session ends is lost forever;
claude_memory/ holds the session-level notes that haven't stabilized
enough to graduate into a CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 16:24:11 -04:00

124 lines
4.7 KiB
Markdown

# synthesize.sh -- ltlsynt Reactive Synthesis Wrapper
## Purpose
Reads a synthesis configuration JSON and invokes `ltlsynt` (from the Spot
library) to produce a reactive controller as an AIGER circuit. This is the
step where temporal logic specifications become a concrete implementation.
## Usage
```bash
bash scripts/synthesize.sh <synthesis_config.json> [output_dir]
```
- `synthesis_config.json`: output of `fret_to_synth.py`
- `output_dir`: where to write the `.aag` file (default: `circuits/`)
## How It Works
### Parameter Extraction
A small inline Python script reads the JSON config and extracts:
- `INPUTS`: comma-separated list of environment variables (uncontrollable)
- `OUTPUTS`: comma-separated list of system variables (controllable)
- `LTL_FORMULA`: the conjoined LTL specification
- `SPEC_NAME`: used for the output filename
### The ltlsynt Call
```bash
ltlsynt -f "$LTL_FORMULA" \
--ins="$INPUTS" \
--outs="$OUTPUTS" \
--aiger=both+ud+dc \
--simplify=bwoa-sat
```
**Flags explained:**
| Flag | Meaning |
|---|---|
| `-f` | LTL formula (the specification) |
| `--ins` | Input (environment/uncontrollable) propositions |
| `--outs` | Output (system/controllable) propositions |
| `--aiger=both+ud+dc` | Output as ASCII AIGER with both latches and gates, using unit delays and don't-cares for optimization |
| `--simplify=bwoa-sat` | Best simplification: bounded width with SAT-based optimization. Produces the smallest circuit |
### The Two-Player Game
Reactive synthesis frames controller design as a two-player game:
- **Player 1 (Environment)**: controls the `--ins` variables. Assumed to be
adversarial -- the synthesizer must find a strategy that wins against ALL
possible environment behaviors, including worst-case.
- **Player 2 (Controller)**: controls the `--outs` variables. The synthesizer
searches for a strategy (a Mealy machine) such that no matter what Player 1
does, the LTL specification is satisfied.
**Why adversarial?** Because we need a controller that works in ALL operating
conditions, not just typical ones. If the environment CAN violate a safety
invariant, the synthesizer assumes it WILL, and the controller must handle it.
This is why the old PWR_Hybrid specs produced a degenerate controller: the
environment could force `t_max_exceeded` every step, triggering irrecoverable
SCRAM. The only winning strategy was to SCRAM immediately. The new specs
have proper guard conditions that give the controller legal responses to every
environment move.
### Realizability
ltlsynt outputs one of two results:
- **REALIZABLE**: A winning strategy exists. The AIGER circuit is printed
after the `REALIZABLE` line. The script strips this line and writes the
circuit to `{output_dir}/{spec_name}.aag`.
- **UNREALIZABLE**: No controller can satisfy the specification against all
possible environment behaviors. This means the specs have a fundamental
conflict. Use FRET's realizability checking and counterexample traces to
diagnose which requirements conflict.
### Output: AIGER Format
The `.aag` (ASCII AIGER) file is a text-based hardware description:
```
aag M I L O A Header: Max-var, Inputs, Latches, Outputs, ANDs
<input literals> One per line, even numbers (2, 4, 6, ...)
<latch definitions> current_lit next_lit (state registers)
<output literals> Which literals are outputs
<AND gates> lhs rhs0 rhs1 (lhs = rhs0 AND rhs1)
<symbol table> i0 name, o0 name, etc.
```
**Literal encoding:** Variable `n` has literal `2n` (positive) and `2n+1`
(negated). Literal `0` = constant FALSE, literal `1` = constant TRUE.
XOR-ing a literal with 1 flips its polarity.
**Latches** are 1-bit registers that initialize to 0. With `L` latches,
the controller has up to `2^L` internal states. The circuit is a Mealy
machine: outputs depend on both current state (latches) and current inputs.
## Future Improvements
- **Environment assumptions**: ltlsynt supports `--from-ltlf` for finite
traces and can separate assumptions from guarantees. Adding support for
environment assumptions (e.g., fairness constraints like "the environment
won't hold t_max_exceeded forever") would expand the class of realizable
specifications.
- **Decomposed synthesis**: For large specs, monolithic synthesis can be
expensive. Compositional synthesis (synthesizing each mode's controller
separately and composing them) could improve scalability.
- **Alternative output formats**: The AIGER circuit could be translated to
Verilog, VHDL, or Simulink/Stateflow for integration with existing
control system toolchains.
- **Timeout handling**: Currently no timeout on the ltlsynt call. Very
complex specifications can take hours. Adding a configurable timeout
with a clear error message would improve usability.