Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ The reasons, the numbers behind the defaults and what each costs in key space ar
- `check_bittings.py` prints a `SHAPE` line for each rule a key breaks, with pin positions and
never the cuts. They are advice: they do not count as problems or change the exit status.
Name the masters with `--master NAME` (repeat the flag for more than one).
- The solver and `check_system` do not use the rules yet.
- `solve_system.py` follows them too, for every key it chooses (a master is a key that is in
the `masters` of any core). The system file sets them in an optional `shape` object, with
the rule names of the table above and `null` to turn one off (see
[System file format](#system-file-format)).
- `check_system` does not use the rules yet.

```bash
./gen_bittings.py --pins 7 -n 3 --master # three masters, no parity pattern
Expand All @@ -142,6 +146,7 @@ A JSON file. Fields (only `keys` and `cores` are needed for the basics):
| `keys` | `{name: bitting}` of operating keys, including decoded unit keys. |
| `retired_keys` | `{name: bitting}` of old keys that must **not** operate any new core. |
| `control_keys` | `{name: bitting}` of control keys. |
| `shape` | Optional object of [key shape rules](#key-shape-rules) for the keys the solver chooses: any of `max_run`, `max_same_depth`, `forbid_monotone`, `master_min_span` and `min_total_variation`, each a whole number of at least 1 (`forbid_monotone`: `true` or `false`), or `null` to turn the rule off. Rules left out keep their defaults, so a file without `shape` gets them all. An unknown rule name is an error, as is a rule no key of the key space could meet. |
| `pinning` | Optional name of a pinning system (`"A2"`): opts in to the pinning checks. Without it the file is read exactly as before. |
| `cores` | List of `{"name", "change", "masters"}`. `change` is a key name, a wildcard such as `"unit:*"`, or a list of those. `masters` is a list of key names pinned above the change key (may be empty). With `pinning`, each core also needs `"control"`: the name of its control key in `control_keys`. |
| `retired_cores` | Optional, with `pinning`: how the old cores were pinned, as a list of `{"name", "change", "masters", "control"}` shaped like `cores`. `change` names keys in `keys` or `retired_keys` (a wildcard may match nothing yet), and `masters` and `control` name entries in `retired_keys`. |
Expand Down Expand Up @@ -396,6 +401,15 @@ printed, and the printed pins are the ones the checker's own pinner computed.
so look for that line (or `problem(s) flagged`) and do not use the result;
run it again, since each run draws different candidates.

Every key it chooses also follows the [shape rules](#key-shape-rules) of the
file, and it never relaxes one: if no bitting for a key follows them (a thin
pinnable set can leave none, since pinning decides the cuts first), that key is
left `null` in the written file, a `NOT SOLVED` line says which rules turned
the draws down, and the full check is skipped because the file is not complete.
Change or turn off a rule in the `shape` object, or change a known key, and run it
again. With every rule off the solver chooses exactly what it did before the
rules existed.

If the file sets `pinning` (see [Pinning](#pinning-optional)), the solver
also treats a core that cannot be pinned as a hard conflict, and adds the
expected number of undecoded unit keys that cannot take the unit master and
Expand Down
18 changes: 18 additions & 0 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,24 @@ The line comes before `Wrote ...` and again after the full check, so it is the l
the output; the exit status is unchanged. A conflict between known keys alone does not
trigger it: nothing the solver chooses can change that, and the check reports it (D59).

**Shape rules in the solver** (D64). Every key the solver chooses follows the file's shape
rules (`Config.shape`, a `model.ShapeRules`; master means a key in the `masters` of any
core). They filter the candidates and leave the score alone (D6). The single-key search draws
candidates with `shaped_candidates` and walks to neighbours that follow the rules; in the
pinning construction the draws from the exact pinnable set are filtered the same way, and a
set small enough to list is filtered whole. Each gives up after 20,000 draws the rules turned
down (and never fewer than 50 for each candidate asked for): the key is left `null` in the
file, a `NOT SOLVED` line gives the rule tally, and the full check, which would reject the
null, is skipped. Nothing is relaxed. A thin pinnable set (a unit master among many decoded
units) can leave no key that follows the rules; that is reported, not repaired. With every
rule off the same seeded draws are made as before the rules existed, so output is
byte-identical. The summary of chance cross-operation compares the chosen unit master with
typical ones; with rules on those are drawn once for each master from the keys that follow
them (reused in turn when a tight rule set leaves few, left out when it leaves none), never
by a separate bounded search for each sample. For keys built together, the `NOT SOLVED`
reason names a key only if it broke a rule in at least 80% of the draws (any random key
breaks one now and then); the others say they were built together with it.

## The key space

The rules that decide which bittings can be cut live in one frozen object,
Expand Down
9 changes: 6 additions & 3 deletions docs/designs/key-shape-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ only the 7% of bittings with three in a row and leaves the pairs, which is the c
- **Never silently relaxed.** If no candidate passes (a tight system, or a pinnable set
with nothing in it that has the right shape), the tool says which rule left nothing, and
the key stays unsolved under the existing "NOT SOLVED" line (D59). Turning a rule off is
the system file's decision, not the tool's.
the system file's decision, not the tool's. For the solver, "unsolved" means the key is left
`null` in the written file (the file is not complete, so the full check is skipped), and the
NOT SOLVED line names the key and the rules, before `Wrote ...` and again last.
- **Keys that already exist** are not changed. `check_system` and `check_bittings` print
the ones that break a rule as an advisory `SHAPE` line, with positions only and never
values. It does not change the exit status or the count of warnings. Decoded unit
Expand All @@ -143,8 +145,9 @@ what the tools count alone. The risk figures are unchanged.

## What changes in each tool

- **Solver, one key at a time.** `random_candidate` and `neighbors` only produce keys that
pass, so the search never walks through a bad key. The score is unchanged (D6).
- **Solver, one key at a time.** The candidates and the neighbours it walks to only include
keys that pass, so the search never goes through a bad key. The score is unchanged (D6), and
with every rule off the same draws are made as before, so seeded output is byte-identical.
- **Solver, pinning, keys built together** (D60). The pinnable set is built exactly and
its draws are uniform; draws that fail the rules are thrown away and drawn again, up to a
bound, and a set small enough to list is filtered whole. If the bound is reached with
Expand Down
Loading
Loading