RHEED low-level features + embedding-vector query - #86
Merged
Conversation
- Client.get(..., include_low_level_features=True) surfaces the pipeline's region-level RHEED features (area_n, eccentricity_n, fwhm_0_n, ...) as extra timeseries_data columns; RHEEDProvider.to_dataframe flattens the nested low_level_features dict the backend now returns. - client.get_rheed_embeddings(...) returns the raw Chronos window/prototype vectors as a numpy-backed RHEEDEmbeddingResult (with .to_dataframe()). - client.query_rheed_embeddings(...) runs k-NN "find similar" over the embedding index, returning a tidy neighbors DataFrame. - New RHEEDEmbeddingProvider mirrors the SimilarityTrajectoryProvider pattern. Pairs with atom-cloud PR #1840 (backend routes + low_level_features field). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
| Filename | Overview |
|---|---|
| src/atomscale/similarity/embedding_provider.py | New provider for k-NN embedding neighbor queries; clean fetch/build split, robust empty-response handling, good column ordering logic. |
| src/atomscale/timeseries/rheed.py | Adds low-level feature flattening, frame mask attachment, and frame-range utilities. The json_normalize without max_level (noted in previous thread) and the overall logic are sound; mask join on MultiIndex works correctly. |
| src/atomscale/client.py | Adds query_rheed_embeddings, get_frame_masks, and include_masks support to get_rheed_timeseries. All validation, frame scoping, and sentinel-based to_frame logic are correct. |
| src/atomscale/results/rheed_image.py | Extracts decode_mask_rle into a standalone function to share between single-frame and per-frame mask endpoints; existing mask decode path now delegates to it without behavior change. |
| tests/test_rheed_timeseries.py | Good coverage for attach_frame_masks (sparse coverage, empty masks, no-frame-axis passthrough, re-attach idempotency), frame_number_bounds, and include_masks integration via monkeypatched _get. |
Sequence Diagram
sequenceDiagram
participant User
participant Client
participant RHEEDProvider
participant RHEEDEmbeddingProvider
participant Backend
Note over User,Backend: get_rheed_timeseries (include_masks=True)
User->>Client: "get_rheed_timeseries(data_id, include_low_level_features=True, include_masks=True)"
Client->>RHEEDProvider: "fetch_raw(data_id, include_low_level_features=True, ...)"
RHEEDProvider->>Backend: "GET rheed/timeseries/{data_id}/"
Backend-->>RHEEDProvider: "{series_by_angle: [{series: [{low_level_features: {...}}, ...]}]}"
RHEEDProvider->>RHEEDProvider: to_dataframe() → _flatten_low_level_features()
RHEEDProvider-->>Client: ts_df (MultiIndex: Angle, Frame Number)
Client->>RHEEDProvider: frame_number_bounds(ts_df) → (first, last)
Client->>Backend: "GET rheed/images/{data_id}/frame_masks?from=first&to=last"
Backend-->>Client: "[{frame_number, mask_rle, mask_height, mask_width}, ...]"
Client->>RHEEDProvider: attach_frame_masks(ts_df, mask_rows)
RHEEDProvider-->>Client: ts_df with mask columns (sparse NA for unmasked frames)
Client-->>User: DataFrame
Note over User,Backend: query_rheed_embeddings
User->>Client: query_rheed_embeddings(data_id, workflow, window_span, kind, top_k)
Client->>RHEEDEmbeddingProvider: fetch_neighbors_raw(client, data_id, workflow, ...)
RHEEDEmbeddingProvider->>Backend: "GET similarity/{workflow}/{data_id}/embeddings/neighbors/"
Backend-->>RHEEDEmbeddingProvider: "{neighbors: [{data_id, similarity, ...}, ...]}"
RHEEDEmbeddingProvider->>RHEEDEmbeddingProvider: neighbors_to_dataframe(raw)
RHEEDEmbeddingProvider-->>Client: DataFrame (ordered columns)
Client-->>User: DataFrame
Reviews (2): Last reviewed commit: "Merge branch 'main' into claude/agitated..." | Re-trigger Greptile
- RHEEDEmbeddingResult stores cluster_sizes as float64 so partial/missing cluster_size coerces to NaN instead of crashing np.asarray (int64 has no missing-value sentinel); + regression test. - to_result requires point["index"] (raise on malformed data rather than silently indexing rows with None). - _expand_low_level_features pins json_normalize(max_level=1) so a future nested feature value can't silently dot-explode into extra columns. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI's pinned ruff (v0.9.4) flags PD901 where the newer local ruff did not. Rename the `df` DataFrame locals to `frame` in RHEEDEmbeddingResult.to_dataframe and RHEEDEmbeddingProvider.neighbors_to_dataframe, and apply ruff-format. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve conflicts in client.py and timeseries/rheed.py where this branch's RHEED low-level-features + embedding work overlapped main's independently merged embedding support (PRs #87/#88). - rheed.py: keep main's canonical `_flatten_low_level_features`; drop this branch's equivalent `_expand_low_level_features` (this branch's tests exercise the public `to_dataframe` and still pass). - client.py: union of both APIs. Keep this branch's `get_rheed_embeddings` and the new k-NN `query_rheed_embeddings`, and main's `get_embeddings`, `get_similarity_matches`, `get_rheed_timeseries`, and `get_frame`. The auto-merged `get(include_low_level_features=...)` and `_get_timeseries_result` changes are retained. Note: get_embeddings (main) and get_rheed_embeddings (this branch) hit the same embeddings endpoint with different return types — a redundancy to reconcile in a follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With include_masks=True, a last_n / elapsed_seconds query windowed the timeseries but get_frame_masks still fetched the whole video's mask artifact, discarding most rows in the left join. Add RHEEDProvider.frame_number_bounds() and pass from_frame/to_frame derived from the returned series so only the spanned frames' masks are fetched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an "Analysis Results" guide section covering the unified result-fetching upgrade — get_rheed_timeseries(include_low_level_features=, include_masks=) with low-level features and per-frame segmentation masks aligned on the Frame Number axis, get_frame_masks / decode_mask_rle, and embedding fetch/query (get_embeddings, query_rheed_embeddings). Register the new atomscale.results.rheed_embedding module in the API reference. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… API Following the merge of main's embedding support, this branch carried duplicate fetching paths. Consolidate onto the canonical methods so the result-fetching upgrade exposes exactly one way to do each thing: - Drop Client.get_rheed_embeddings (duplicate of main's get_embeddings, same endpoint) and the now-orphaned RHEEDEmbeddingResult / rheed_embedding.py. - Drop the get(include_low_level_features=) flag in favor of get_rheed_timeseries, which already returns low-level features (and masks) aligned by Frame Number. - Keep the genuinely-new query_rheed_embeddings (k-NN "find similar"); trim RHEEDEmbeddingProvider to just the neighbor-query path and align its default window_span to 60.0 to match get_embeddings / get_similarity_matches. - Update the Analysis Results guide and API reference accordingly. Net result-fetching surface: get_embeddings (vectors), query_rheed_embeddings / get_similarity_matches (find similar), get_rheed_timeseries (include_low_level_features / include_masks), get_frame_masks, decode_mask_rle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Author
munrojm
approved these changes
Aug 10, 2026
The `x != x` NaN self-inequality idiom in test_rheed_timeseries only holds for float NaN. After a sparse left join, missing values in the object-dtype mask columns (and what groupby().first() returns for an all-missing group) can be Python None depending on the resolved pandas version, where `None != None` is False. This failed on CI for Python 3.10/3.12/3.13 while 3.11 (a different pandas) passed. pd.isna() returns True for both None and NaN, matching the tests' documented "get NA" intent and making them version-robust. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Exposes two RHEED capabilities through the SDK, backed by atom-cloud PR #1840 (which adds the corresponding backend routes + response field).
Low-level RHEED features
client.get(data_ids, include_low_level_features=True)now surfaces the pipeline's region-level features (area_n,eccentricity_n,fwhm_0_n, profile skews/CVs, …) as extra columns onRHEEDVideoResult.timeseries_data. The backend nests them per-point underlow_level_features;RHEEDProvider.to_dataframeflattens that into raw-named columns. Off by default; ignored for non-RHEED types.Embedding-vector query
The raw Chronos timeseries embeddings (the inputs to similarity matching, vs. the derived curve from
get_similarity_trajectory) are now reachable:client.get_rheed_embeddings(data_id, *, workflow, window_span, kind="window"|"prototype", offset, limit)→RHEEDEmbeddingResult— numpy(N, D)vectors+ time axis / cluster sizes, with.to_dataframe().client.query_rheed_embeddings(data_id, *, workflow, window_span, kind, top_k)→ tidy neighborsDataFrame("find similar growths"), source excluded, sorted by similarity.New
RHEEDEmbeddingProvider(underatomscale/similarity/) mirrors the existingSimilarityTrajectoryProviderfetch/build split;RHEEDEmbeddingResultis exported fromatomscale.results.Tests
New pure unit tests (no network) for the provider/result conversions and the low-level flatten, plus creds-gated live integration tests.
26 passed, 2 skippedlocally; full suite still collects (141) andtest_core/test_alignstay green.🤖 Generated with Claude Code