Skip to content

feat: Add native fixed-array dot - #28504

Merged
ritchie46 merged 20 commits into
pola-rs:mainfrom
0guban0v:feat/native-fixed-array-dot
Aug 14, 2026
Merged

ritchie46 merged 20 commits into
pola-rs:mainfrom
0guban0v:feat/native-fixed-array-dot

Conversation

@0guban0v

@0guban0v 0guban0v commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Closes #17456 and benefits embedding pipelines doing similarity scoring or candidate reranking.

Related #24652 is tracked separately by #28614: vertical fixed-array aggregation Expr.sum() reduces Array rows into one Array, while arr.dot reduces each row into one scalar.

Issue example uses Expr.dot, which reduces columns to one scalar. This PR adds arr.dot because requested operation is row-wise over each fixed-size array.

  df.select(
      score=pl.col("embedding").arr.dot(query)
  )

API distinction:

# Column reduction producing one scalar
pl.col("a").dot("b")

# Row-wise Array operation producing one scalar per row
pl.col("a").arr.dot("b")

Existing equivalent materializes a rows × width multiplication result before reducing:

(pl.col("a") * pl.col("b")).arr.sum()

Native kernel fuses multiplication and reduction, so it does not materialize rows × width product. Fragmented inputs still rechunk, so peak memory can include contiguous copies of lhs and rhs in addition to scalar output.
Attached benchmark in comments.

Scope:

  • matching Float32 or Float64 inner dtypes;
  • equal widths;
  • equal row counts or one-row broadcasting;
  • outer null propagation;
  • inner-null products ignored;
  • fragmented inputs rechunk through existing behavior;
  • raw Python sequences, one-dimensional NumPy arrays, and literal vectors are cast to the left Array dtype;
  • non-literal expressions and Series still require exactly matching Array dtypes;

Limitations:

  • do not support Integer or mixed-float

Update (2026-07-30): arr.dot now accepts Python sequences and one-dimensional NumPy arrays directly. These raw query vectors are cast after selector or wildcard expansion, when each left expression has concrete Array dtype. Explicit expressions and Series operands retain their dtype and must match the left operand.

AI disclosure:
OpenAI Codex drafted early benchmark code and plots with my revision, fixed my grammar and phrasing.

@github-actions github-actions Bot added enhancement New feature or an improvement of an existing feature python Related to Python Polars rust Related to Rust Polars changes-dsl Do not merge if this label is present and red. labels Jul 24, 2026
@codecov

codecov Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.27891% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.56%. Comparing base (5f3ba39) to head (9c62320).

Files with missing lines Patch % Lines
crates/polars-ops/src/chunked_array/array/dot.rs 96.70% 3 Missing ⚠️
...polars-plan/src/plans/aexpr/function_expr/array.rs 95.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #28504      +/-   ##
==========================================
+ Coverage   81.53%   81.56%   +0.02%     
==========================================
  Files        1883     1884       +1     
  Lines      266376   266521     +145     
  Branches     3224     3227       +3     
==========================================
+ Hits       217188   217384     +196     
+ Misses      48355    48305      -50     
+ Partials      833      832       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@0guban0v
0guban0v force-pushed the feat/native-fixed-array-dot branch from c9347ef to c28fe48 Compare July 24, 2026 13:40
@0guban0v

0guban0v commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor Author

Possible follow-up, separate from this PR, is to fuse sibling arr.dot expressions that score same Array column against several constant query vectors.

df.select(
    score_a=pl.col("embedding").arr.dot(query_a),
    score_b=pl.col("embedding").arr.dot(query_b),
)

Today each expression traverses embedding independently. Planner could read each embedding array once, compute both scores in one multi-query kernel, and still return two separately named columns. Expressions that cannot be fused would continue using existing arr.dot execution.

Standalone Apple M4 experiment found this faster from two query vectors onward, but it has not been validated inside Polars and floating-point tolerance is unresolved. I don't want to mess with baseline.

@0guban0v

Copy link
Copy Markdown
Contributor Author

Addressed raw-query-vector API mismatch. IntoExpr includes Python lists and NumPy arrays, but these previously became List or primitive Series literals and failed fixed-size Array requirement. Basically, bump data science product fit.

@0guban0v

0guban0v commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor Author

In-memory engine. Not much difference between Float 32 and Float 64. I will check rechunk impact on memory in the follow-up work.

m4-product-canary-trace-f32 m4-product-canary-peak-rss

@0guban0v
0guban0v marked this pull request as ready for review July 30, 2026 19:44
@0guban0v
0guban0v force-pushed the feat/native-fixed-array-dot branch from d3df4a5 to 3e79306 Compare July 30, 2026 20:21
@0guban0v

Copy link
Copy Markdown
Contributor Author

@orlp , @ritchie46 , whenever you have time, this one is ready. I don't want to grow scope, it's reasonable with limitations and follow-up work. Happy to answer your questions.

@0guban0v

Copy link
Copy Markdown
Contributor Author

Verified current implementation explicitly on

  q.collect(engine="streaming")
  q.collect(engine="in-memory")

both produced identical results

@0guban0v
0guban0v force-pushed the feat/native-fixed-array-dot branch 2 times, most recently from 564905d to 9d28983 Compare August 4, 2026 16:42
@0guban0v
0guban0v requested a review from wence- as a code owner August 4, 2026 17:06
@0guban0v
0guban0v force-pushed the feat/native-fixed-array-dot branch 4 times, most recently from e7dac5b to a284fad Compare August 7, 2026 18:39
@0guban0v

0guban0v commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

It’s ready for review, but it’s currently blocked by new, unrelated CI bugs. I have to convert it back to a draft due to my two-PR limit so I can prioritize CI fix. Feel free to review this PR anyway.

@wence- If you want to see additional measurements, let me know which.

Comment thread crates/polars-ops/src/chunked_array/array/dot.rs
Comment thread crates/polars-ops/src/chunked_array/array/dot.rs Outdated
Comment thread py-polars/src/polars/expr/array.py
Comment thread py-polars/src/polars/series/array.py
@0guban0v
0guban0v marked this pull request as draft August 13, 2026 17:24
@0guban0v
0guban0v force-pushed the feat/native-fixed-array-dot branch from 9d845e8 to 9c62320 Compare August 13, 2026 19:57
@0guban0v
0guban0v marked this pull request as ready for review August 13, 2026 21:20
@0guban0v
0guban0v requested a review from ritchie46 August 13, 2026 21:21
@ritchie46
ritchie46 merged commit 60cb846 into pola-rs:main Aug 14, 2026
34 checks passed
robertop-intern pushed a commit to robertop-intern/polars that referenced this pull request Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changes-dsl Do not merge if this label is present and red. enhancement New feature or an improvement of an existing feature python Related to Python Polars rust Related to Rust Polars

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot dot two Array(float64, N) columns

3 participants