Skip to content

Conversation

kliwongan
Copy link

@kliwongan kliwongan commented Sep 18, 2025

Changes Made

Related Issues

#4704

Checklist

  • Documented in API Docs (if applicable)
  • Documented in User Guide (if applicable)
  • If adding a new documentation page, doc is added to docs/mkdocs.yml navigation
  • Documentation builds and is formatted properly (tag @/ccmao1130 for docs review)

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Summary

This PR implements power/exponentiation functionality (pow and power) across the Daft library stack. The implementation adds support for computing base^exponent operations through several layers:

Core Array Operations: New pow.rs module in daft-core implements power operations for Float32Array and Float64Array using native powf() methods, with automatic casting of integer types to floats.

Function Registration: The Pow struct is registered in the numeric functions module (daft-functions) as a scalar UDF that handles type checking and ensures the exponent is a scalar value.

Python API: Both pow() and power() methods are added to the Series class and Expression class, providing dual naming conventions for compatibility. The type stub file is updated to include the new PySeries.pow() method.

Spark Connect Integration: The pow function is registered in the math module with both 'pow' and 'power' aliases for Spark compatibility.

The implementation follows established patterns in the codebase by using the scalar UDF framework, proper type coercion (casting integers to Float64), and maintaining Float32 precision where possible. The function accepts two arguments where the exponent must be a scalar float value, which is consistent with common mathematical operation design patterns.

Confidence score: 1/5

  • This PR has critical implementation issues that will cause runtime failures and inconsistent behavior
  • Score reflects multiple serious bugs including parameter mismatches, incorrect argument parsing, and duplicate conflicting registrations
  • Pay close attention to src/daft-functions/src/numeric/pow.rs, daft/expressions/expressions.py, and src/daft-connect/src/functions/math.rs

8 files reviewed, 13 comments

Edit Code Review Bot Settings | Greptile

parent.add_fn(Log10);
parent.add_fn(Ln);
parent.add_fn(Log1p);
parent.add_fn(Pow);
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: The pow function is registered in Rust but missing from the Python daft/functions/numeric.py module and daft/functions/__init__.py, making it inaccessible to users.

@kliwongan
Copy link
Author

Fixing up the PR and then adding tests

Copy link

codecov bot commented Sep 18, 2025

Codecov Report

❌ Patch coverage is 32.20339% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.60%. Comparing base (10086b2) to head (6ae58d6).

Files with missing lines Patch % Lines
src/daft-functions/src/numeric/pow.rs 20.00% 24 Missing ⚠️
src/daft-core/src/series/ops/pow.rs 0.00% 11 Missing ⚠️
src/daft-core/src/array/ops/pow.rs 0.00% 3 Missing ⚠️
daft/series.py 50.00% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #5237      +/-   ##
==========================================
+ Coverage   74.24%   74.60%   +0.36%     
==========================================
  Files         973      976       +3     
  Lines      125213   124558     -655     
==========================================
- Hits        92959    92925      -34     
+ Misses      32254    31633     -621     
Files with missing lines Coverage Δ
daft/expressions/expressions.py 97.22% <100.00%> (+0.01%) ⬆️
daft/functions/__init__.py 100.00% <ø> (ø)
daft/functions/numeric.py 100.00% <100.00%> (ø)
src/daft-core/src/series/ops/mod.rs 100.00% <ø> (ø)
src/daft-functions/src/numeric/mod.rs 100.00% <100.00%> (ø)
daft/series.py 92.53% <50.00%> (-0.27%) ⬇️
src/daft-core/src/array/ops/pow.rs 0.00% <0.00%> (ø)
src/daft-core/src/series/ops/pow.rs 0.00% <0.00%> (ø)
src/daft-functions/src/numeric/pow.rs 20.00% <20.00%> (ø)

... and 18 files with indirect coverage changes

🚀 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.

@kliwongan kliwongan marked this pull request as draft September 18, 2025 23:41
@kliwongan kliwongan changed the title Add pow expression featL Add pow expression Sep 19, 2025
@kliwongan kliwongan changed the title featL Add pow expression feat: Add pow expression Sep 19, 2025
@github-actions github-actions bot added the feat label Sep 19, 2025
@kliwongan kliwongan marked this pull request as ready for review September 19, 2025 03:15
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Summary

This review covers only the changes made since the last review, not the entire PR. The latest changes have completely refactored the pow and power expression implementations to follow the established architectural pattern used throughout the Daft codebase.

The key changes include:

  1. Expression methods refactor: The pow and power methods in Expression class now delegate to daft.functions.pow and daft.functions.power respectively, following the same pattern as other mathematical operations like sqrt, exp, and log. This eliminates the previous direct native function calls and addresses all parameter inconsistencies from earlier reviews.

  2. Functions module implementation: Added pow and power functions to daft/functions/numeric.py that call the builtin scalar function "pow". Both functions are identical and properly exported through daft/functions/__init__.py.

  3. Rust backend modernization: The Rust implementation has been updated to use generic DataArray<T> with DaftFloatType trait bounds instead of separate Float32/Float64 implementations. The functions module now uses the modern #[derive(FunctionArgs)] proc macro pattern.

  4. Testing: Added representation tests for both pow and power methods that verify correct string output and deep copy behavior.

  5. Spark Connect cleanup: Removed pow function registration from Spark Connect integration, marking them as TODO items since the functionality is now handled at the core expression level.

This architectural change centralizes mathematical operations in the core expression system rather than having them scattered across integration layers, ensuring consistent behavior across all entry points. The implementation now matches the established patterns used by other mathematical functions in the codebase.

Confidence score: 2/5

  • This PR has critical parameter ordering inconsistency that will cause runtime errors
  • Score reflects serious API design flaw where functions module expects (base, exp) but Expression methods pass (self, exp)
  • Pay close attention to the function parameter ordering mismatch between daft/functions/numeric.py and the Rust implementation

Context used:

Rule - Import statements should be placed at the top of the file rather than inline within functions or methods. (link)

9 files reviewed, 2 comments

Edit Code Review Bot Settings | Greptile

Comment on lines +196 to +203
def pow(base: Expression, expr: Expression) -> Expression:
"""The base^expr of a numeric expression."""
return Expression._call_builtin_scalar_fn("pow", base, expr)


def power(base: Expression, expr: Expression) -> Expression:
"""The base^expr of a numeric expression."""
return Expression._call_builtin_scalar_fn("pow", base, expr)
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Both pow and power functions have identical docstrings and implementations. Consider differentiating the docstrings or adding a note about their relationship for clarity.

let s = self.cast(&DataType::Float64)?;
Ok(s.f64()?.pow(exp)?.into_series())
}
DataType::Float32 => Ok(self.f32()?.pow(exp as f32)?.into_series()),
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Casting f64 to f32 may cause precision loss for the exponent when working with Float32 series

@colin-ho colin-ho self-requested a review September 23, 2025 20:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant