Skip to content

36 orchestration v1#39

Merged
pevab merged 57 commits into
mainfrom
36-orchestration-v1
Jul 6, 2026
Merged

36 orchestration v1#39
pevab merged 57 commits into
mainfrom
36-orchestration-v1

Conversation

@moh

@moh moh commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

ArthurDanjou and others added 30 commits June 22, 2026 17:00
Synchronous, in-memory orchestration following adr-2026-06-12 and
adr-2026-06-19:

- Orchestrator: drives runs (one parameter point per run, user writes the
  sweep loops), registers channels, owns the data. Fail-fast.
- Metric: named write-handle channel; dtype-checked pushes with
  skip_if_exists; data lives on the orchestrator, not the metric.
- MetricDataFrame: per-metric storage using pandas internally; parameters
  stored once per run and exposed as a parameter-indexed frame, sliceable
  by parameter values.
- Current run state kept in plain module globals (no context manager, no
  stack), per adr-2026-06-19.
Add a Sphinx reference page for krum.orchestration (Orchestrator, Metric,
MetricDataFrame) and register it in the documentation index.
Wrap the experiment call in run() so that a failure re-raises a RuntimeError
identifying the run's parameters, with the original exception chained. Keeps
fail-fast (the sweep still stops) while making failures diagnosable.
Each run's parameters are already encoded in its sample key
(tuple(sorted(params.items()))), so the separate _params dict duplicated them.
Reconstruct parameters from the key with dict(run_key) when materialising the
frame, and keep a single _param_order list for column ordering (run keys are
sorted, so they cannot preserve the user's parameter order). Output is
unchanged.
Resolve each experiment's default arguments in run() (via the signature) so a
defaulted parameter is recorded whether or not it was passed explicitly, giving
runs that differ only in that respect one identity. When materialising the
frame, fill a parameter a run lacks with NaN instead of raising, so runs with
differing parameter sets render cleanly.
Cover Metric (creation/push guards, dtype enforcement, name validation),
Orchestrator (run/get, sweep accumulation, reserved names, fail-fast wrapping,
default resolution) and MetricDataFrame (params stored once, slicing, NaN fill,
copy semantics).
Record the experiment on the first run() and reject a later run() that passes a
different one with a clear ValueError. A run is identified only by its
parameters, so allowing several experiments in one orchestrator would let their
samples silently collide; use a separate orchestrator per experiment.
Cover that repeated runs of the same experiment are allowed and that running a
different experiment on the same orchestrator raises ValueError.
A backport of PEP 814's builtin frozendict for the supported Python versions
(3.10-3.14): an immutable, hashable Mapping with order-independent equality and
hashing but order-preserving iteration. On Python 3.15+ the module aliases the
builtin frozendict directly. Behaviour was verified against the real 3.15.0b1
builtin (constructor forms, equality, lazy hashing, repr, immutability).
Make the fallback mapping shallowly immutable and preserve its hash and equality invariants.

Add dict-like construction from mappings and iterable pairs, generic key/value typing, union operators, shallow and deep copy behavior, pickling support, and recursive-safe representation. Expand focused tests to cover the new behavior.
Use FrozenDict parameter mappings as stable run keys and remove the separately maintained parameter-order state.

Replace dataframe() with to_pandas(), materializing flat parameter columns from their ordered union and marking parameters absent from a run with pd.NA. Keep step and value as ordinary columns instead of constructing a MultiIndex.

Make filtered calls return independent MetricDataFrame stores, preserving the distinction between absent parameters and parameters explicitly set to None. Update dataframe, metric, and orchestrator tests for the new API and behavior.
Remove the single-experiment binding so one orchestrator can collect metrics from multiple experiment functions.

Record each run with module-qualified experiment metadata and a 12-character SHA-256 source hash, falling back to the qualified identity when source inspection is unavailable. Reserve the generated metadata column names to avoid user-parameter collisions.

Update orchestrator tests for non-colliding multiple experiments, metadata columns, exact source hashing, fallback hashing, and reserved experiment metadata names.
The hash-consistency and immutability tests probed the backport's private
__data attribute, which the built-in frozendict (Python 3.15+, aliased by the
module) does not expose, making them fail there. Gate those probes behind a
_USES_BUILTIN check while keeping the public-contract assertions on every
version. Verified against the real 3.15.0b1 builtin.
Calling a MetricDataFrame now returns a narrowed MetricDataFrame, not a pandas
frame; update the example comment and show to_pandas() for the pandas export.
Rename run helpers to hidden_vulnerability_experiment and
krum_experiment,
and invoke them via Orchestrator.run in experiment scripts. Import
Orchestrator in experiments and retrieve metrics after runs.

Remove aggregator_f and evaluate_fn parameters from
CentralisedSimulation.
Use Metric to record loss/error in krum_experiment. Implement
KrumSimulation.evaluate and add an evaluate stub for
HiddenVulnerabilitySimulation.
Bring the layer into compliance with the pre-commit checks: add the missing
package docstring, apply ruff formatting, annotate FrozenDict's slotted
attributes and type its constructor dict so ty can resolve them, and switch the
intentional private-attribute probes in the tests to ty's own ignore codes.
Define evaluation by overriding CentralisedSimulation.evaluate rather
than supplying a callable. Document protocol-specific metric tuples
(e.g. Krum -> (test_loss, test_error); HiddenVulnerability ->
(test_loss, test_error, test_accuracy)). Change eval_every to be a
hint for experiment scripts and remove evaluate_fn docs. Fix metric
ordering in HiddenVulnerability and add an explicit return docstring
to Krum.evaluate.
Run parameters form the run's identity key, so each value must be hashable.
Recursively convert dicts to FrozenDict, lists/tuples to tuples, and sets to
frozensets before building the key, so structured parameters such as
attack_kwargs={...} are accepted (the experiment still receives the original
values). Reject any value that is unhashable even after freezing with a clear
error naming the parameter, instead of a deep TypeError. The freezing lives in
the orchestrator, not FrozenDict, so behaviour matches the 3.15 builtin.
Filtering a metric store is clearer as an explicit `.filter(**params)` method
than call syntax. Update the package example and the dataframe tests; the
returned store still shallow-copies each run's samples so it stays independent.
Tag each run with the experiment function itself under the reserved
'experiment' key, dropping the name + SHA-256 source-hash scheme and the
'experiment_hash' column. Update the orchestration tests accordingly.
ArthurDanjou and others added 16 commits June 26, 2026 12:00
…e=True)

PyTorch 2.11+ defaults nn.Module.zero_grad() to set_to_none=True, which
drops parameter .grad attributes. Because Model caches a flat gradient
view that shares storage with those .grad tensors, the cached view became
stale after a plain zero_grad() call: every subsequent worker returned the
same outdated gradient, aggregators produced identical metrics, and training
diverged.

The gradients property now detects when the cached flat buffer is out of
sync with the module's .grad tensors and calls relink_gradients()
automatically. relink_gradients() also handles the case where the flat
gradient view has never been initialized.

Includes regression tests in tests/primitives/test_model.py.
- Extend HiddenVulnerability to accept aggregator_f and forward to
  aggregator_kwargs
- Use a per-parameter RNG for Xavier initialization to preserve global
  RNG determinism
- Pass default f and n to the aggregator when not provided
- Rename MNIST F_DECLARED to MNIST_F_DECLARED_BULYAN and update
  references
- Remove deprecated Krum CONFIGS in krum_nips_2017/experiment_3.py and
  standardize run signature
Clarify two Byzantine budgets: the real count f and the declared
aggregator_f, and state that an explicit f in aggregator_kwargs
takes precedence over aggregator_f

Add a guard in CentralisedSimulation to prevent aggregator=None when
f > 0 and an attack is configured, raising a clear error to avoid
a scenario where Byzantine gradients have nowhere to go.

When aggregator=None, compute the mean of produced gradients instead
of passing through all_gradients, ensuring correct behavior when
Byzantine gradients are absent
- Increase ROUNDS to 100 and set BYZ_WORKERS as round(WORKERS * 0.33)
- Double WORKERS to 20 and adjust LR to 0.1 and BATCH to 8
- Add MultiKrum support and pass aggregator_kwargs configure 3
- Remove error metric plotting and related data retrieval
@moh

moh commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

Added simple matplotlib plotting (step vs value per metric), output saved as experiment_1.png in the run directory.
Note: the decentralized experiment_1 is a debug/smoke-test script, it doesn't reproduce any paper figures yet.

pevab
pevab previously requested changes Jul 1, 2026
Comment thread experiments/centralised/krum_nips_2017/experiment_2.py
Comment thread experiments/centralised/krum_nips_2017/experiment_3.py
Comment thread krum/orchestration/_context.py Outdated

from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pourquoi ce if?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Le if TYPE_CHECKING: évite un import circulaire : orchestrator.py importe déjà begin_run/end_run depuis _context.py, donc importer Orchestrator normalement ici créerait un cycle orchestrator.py → _context.py → orchestrator.py.

Le fichier a "from future import annotations" en haut, donc les annotations (comme orchestrator: Orchestrator) ne sont jamais évaluées à l'exécution, juste stockées comme texte. Orchestrator n'a donc pas besoin d'exister comme vrai objet au runtime — seulement pour les type checkers (on a ty dans les pre-commit hooks). TYPE_CHECKING vaut False à l'exécution (donc jamais de vrai import, pas de cycle) mais True pour ty/l'IDE, qui peuvent donc quand même vérifier les types correctement (ex: le retour de active_orchestrator() -> Orchestrator).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je trouve bizarre ces dépendances circulaires entre fichiers. Pourquoi ne pas merger _context.py dans orchestrator.py?

Comment thread krum/orchestration/_frozendict.py
Comment thread krum/orchestration/metric.py Outdated
Comment thread krum/primitives/aggregators/krum.py
Comment thread krum/primitives/aggregators/multikrum.py
Comment thread krum/primitives/attacks/full_gradient_negation.py
ArthurDanjou and others added 7 commits July 2, 2026 09:38
Metric names are only ever used as internal dict keys; nothing
downstream (file paths, CLI, columns) requires them to be space-free.
FrozenDict only ever serves as a hashable dict key in this codebase, so
drop the unused dict-parity methods (__or__/__ror__/__ior__, copy,
__deepcopy__, __reduce__) and their tests. Verified equivalent behaviour
on the Python 3.15 builtin frozendict alias path.
@pevab pevab marked this pull request as ready for review July 3, 2026 11:33
_context.py only existed to break a circular import between Orchestrator
and its own ambient run-state functions; moving that state next to the
Orchestrator class removes the cycle (and the TYPE_CHECKING workaround)
entirely. Also drop the unrelated TYPE_CHECKING-only Callable import,
which had no circularity to avoid in the first place.
@pevab pevab dismissed their stale review July 6, 2026 13:28

all good

@pevab pevab merged commit 618902b into main Jul 6, 2026
6 checks passed
@pevab pevab deleted the 36-orchestration-v1 branch July 6, 2026 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Orchestration v1

3 participants