Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add adapt #110

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Add adapt #110

wants to merge 3 commits into from

Conversation

Sbozzolo
Copy link
Member

@Sbozzolo Sbozzolo commented Mar 27, 2025

Add adapt so that we can drop Adapt as explicit dependency in downstream pacakages. (And Adapt fits within the scope of ClimaComms)

Summary by CodeRabbit

  • New Features
    • Introduced new methods for adapting objects to specific devices and communication contexts.
    • Added the adapt function to enhance package flexibility and usability.
  • Bug Fixes
    • Corrected typos in comments for improved clarity.
  • Documentation
    • Updated documentation to include the new adapt method for AbstractDevice.
  • Tests
    • Introduced a new test suite to validate the behavior of the adapt function across various data types and structures.

Copy link

coderabbitai bot commented Mar 27, 2025

Walkthrough

The changes introduce two new methods in the src/adapt.jl file for adapting objects to specific contexts or devices: adapt(device::AbstractDevice, x) and adapt(context::AbstractCommsContext, x). Additionally, a new entry is added to the release notes in NEWS.md for the adapt function, which allows downstream packages to eliminate their explicit dependency on the Adapt package. Updates to the GitHub Actions workflow in .github/workflows/OS-Tests.yml include changing the version of the actions/cache action from v1 to v4. Documentation in docs/src/apis.md is updated to reflect the new method signature, and a new test suite is introduced in test/adapt.jl.

Changes

File Path Change Summary
src/adapt.jl Added adapt(device::AbstractDevice, x) and adapt(context::AbstractCommsContext, x) methods. Fixed typos in comments.
NEWS.md Added release note for the new adapt function, enhancing package flexibility.
.github/workflows/OS-Tests.yml Updated actions/cache action version from v1 to v4.
docs/src/apis.md Added method signature for Adapt.adapt(::ClimaComms.AbstractDevice, ::Any).
test/adapt.jl Introduced a new test suite for the adapt function covering various data types and structures.
test/runtests.jl Added new test set labeled "Adapt" to include the new tests from adapt.jl.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/adapt.jl (2)

39-41: Add documentation for the new adapt function.

The new adapt function lacks documentation unlike other functions in this file. Consider adding a docstring that explains its purpose and provides usage examples. This will help users understand that this function is a wrapper around Adapt.adapt and its role in avoiding explicit dependencies on Adapt in downstream packages.

+"""
+    adapt(to, x)
+
+Adapts an object `x` to a specified type or context `to`.
+This function serves as a wrapper around `Adapt.adapt`,
+allowing downstream packages to avoid explicit dependencies on the `Adapt` package.
+
+# Example
+
+```julia
+adapt(Array, cuda_array) # Convert a CUDA array to a CPU array
+```
+"""
 function adapt(to, x)
     return Adapt.adapt(to, x)
 end

39-41: Consider adding type annotations for consistency.

The other methods in this file use explicit type annotations for their parameters. For consistency, consider adding type annotations to the adapt function as well. This would also provide better documentation and possibly enable more efficient method dispatch.

-function adapt(to, x)
+function adapt(to::Any, x::Any)
     return Adapt.adapt(to, x)
 end
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c968350 and 79ea5f6.

📒 Files selected for processing (1)
  • src/adapt.jl (1 hunks)

@Sbozzolo
Copy link
Member Author

@CodeRabbit help

Copy link

coderabbitai bot commented Mar 27, 2025

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

@Sbozzolo
Copy link
Member Author

@CodeRabbit generate docstrings

Copy link

coderabbitai bot commented Mar 27, 2025

✅ Actions performed

Docstrings generation started.

Copy link

coderabbitai bot commented Mar 27, 2025

Caution

No docstrings were generated.

Copy link

coderabbitai bot commented Mar 27, 2025

Oops, something went wrong! Please try again later. 🐰 💔

@Sbozzolo
Copy link
Member Author

@CodeRabbit generate docstrings

Copy link

coderabbitai bot commented Mar 27, 2025

✅ Actions performed

Docstrings generation started.

Copy link

coderabbitai bot commented Mar 27, 2025

Caution

No docstrings were generated.

Copy link

coderabbitai bot commented Mar 27, 2025

Oops, something went wrong! Please try again later. 🐰 💔

@Sbozzolo
Copy link
Member Author

@CodeRabbit generate unit tests

Copy link

coderabbitai bot commented Mar 27, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the implementation of the adapt function
cat src/adapt.jl

# Look at the directory structure to understand how tests are organized
find test -type f | sort

# Check the content of test files to understand the testing patterns
find test -name "*.jl" -exec cat {} \;

# Check the Project.toml to understand dependencies
cat Project.toml

Length of output: 13721


@Sbozzolo Here are unit tests for the new adapt function. These tests should be added to the existing @testset "Adapt" block in runtests.jl:

# Test the new adapt function
# Verify it gives the same results as Adapt.adapt
@test ClimaComms.adapt(Array, ClimaComms.CUDADevice()) ==
      Adapt.adapt(Array, ClimaComms.CUDADevice())
@test ClimaComms.adapt(Array, ClimaComms.CPUSingleThreaded()) ==
      Adapt.adapt(Array, ClimaComms.CPUSingleThreaded())

# Test with contexts
@test ClimaComms.adapt(Array, ClimaComms.context(ClimaComms.CUDADevice())) ==
      Adapt.adapt(Array, ClimaComms.context(ClimaComms.CUDADevice()))
@test ClimaComms.adapt(Array, ClimaComms.context(ClimaComms.CPUSingleThreaded())) ==
      Adapt.adapt(Array, ClimaComms.context(ClimaComms.CPUSingleThreaded()))

# Additional tests for CUDA when available
@static if ClimaComms.device() isa ClimaComms.CUDADevice
    @test ClimaComms.adapt(CUDA.CuArray, ClimaComms.CUDADevice()) ==
          Adapt.adapt(CUDA.CuArray, ClimaComms.CUDADevice())
    @test ClimaComms.adapt(CUDA.CuArray, ClimaComms.CPUSingleThreaded()) ==
          Adapt.adapt(CUDA.CuArray, ClimaComms.CPUSingleThreaded())
    
    # Test with contexts
    @test ClimaComms.adapt(CUDA.CuArray, ClimaComms.context(ClimaComms.CUDADevice())) ==
          Adapt.adapt(CUDA.CuArray, ClimaComms.context(ClimaComms.CUDADevice()))
    @test ClimaComms.adapt(CUDA.CuArray, ClimaComms.context(ClimaComms.CPUSingleThreaded())) ==
          Adapt.adapt(CUDA.CuArray, ClimaComms.context(ClimaComms.CPUSingleThreaded()))
end

# Test basic types to ensure the function works for non-device types
arr = [1, 2, 3]
@test ClimaComms.adapt(Array, arr) == Adapt.adapt(Array, arr)

These tests verify that your new adapt function correctly forwards calls to Adapt.adapt with the same behavior for various argument types, ensuring it will work as expected for downstream packages.


🧠 Learnings used
Learnt from: Sbozzolo
PR: CliMA/ClimaComms.jl#110
File: src/adapt.jl:39-41
Timestamp: 2025-03-27T22:24:24.348Z
Learning: The `adapt` function in ClimaComms.jl should be exported to allow downstream packages to use it without the qualified name, supporting the removal of the `Adapt` package as an explicit dependency.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/adapt.jl (1)

20-20: Fix typo in docstrings

There's a typo in the docstrings: "conver" should be "convert".

-    there is currently no way to conver to a CPUMultiThreaded device.
+    there is currently no way to convert to a CPUMultiThreaded device.

Also applies to: 38-38

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79ea5f6 and ef4bacd.

📒 Files selected for processing (1)
  • src/adapt.jl (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Sbozzolo
PR: CliMA/ClimaComms.jl#110
File: src/adapt.jl:39-41
Timestamp: 2025-03-27T22:24:24.348Z
Learning: The `adapt` function in ClimaComms.jl should be exported to allow downstream packages to use it without the qualified name, supporting the removal of the `Adapt` package as an explicit dependency.
src/adapt.jl (1)
Learnt from: Sbozzolo
PR: CliMA/ClimaComms.jl#110
File: src/adapt.jl:39-41
Timestamp: 2025-03-27T22:24:24.348Z
Learning: The `adapt` function in ClimaComms.jl should be exported to allow downstream packages to use it without the qualified name, supporting the removal of the `Adapt` package as an explicit dependency.
🔇 Additional comments (1)
src/adapt.jl (1)

3-5: LGTM: Import and export of the adapt function

The import and export statements for the adapt function are correctly implemented, which aligns with the PR objective of allowing downstream packages to use this function without an explicit dependency on the Adapt package.

@Sbozzolo
Copy link
Member Author

@CodeRabbit review

Copy link

coderabbitai bot commented Mar 27, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/adapt.jl (1)

44-46: Add documentation for the new adapt method

The implementation looks good, but it's missing documentation unlike other functions in this file.

+"""
+    adapt(device::AbstractDevice, x)
+
+Adapt `x` to work with the given device by using the array type associated with the device.
+
+# Example
+
+```julia
+adapt(ClimaComms.CUDADevice(), array) # Returns a CUDA array
+adapt(ClimaComms.CPUSingleThreaded(), cu_array) # Returns a CPU array
+```
+"""
 function adapt(device::AbstractDevice, x)
     return adapt(array_type(device), x)
 end
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef4bacd and 90a1ce0.

📒 Files selected for processing (1)
  • src/adapt.jl (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/adapt.jl (1)
Learnt from: Sbozzolo
PR: CliMA/ClimaComms.jl#110
File: src/adapt.jl:39-41
Timestamp: 2025-03-27T22:24:24.348Z
Learning: The `adapt` function in ClimaComms.jl should be exported to allow downstream packages to use it without the qualified name, supporting the removal of the `Adapt` package as an explicit dependency.
🔇 Additional comments (3)
src/adapt.jl (3)

3-5: LGTM: Properly importing and exporting adapt

The code correctly imports adapt from the Adapt module and exports it, allowing downstream packages to use it without an explicit dependency on Adapt, which aligns with the PR objectives.


20-20: Fixed typo in documentation

Good catch fixing the typo from "conver" to "convert".


38-38: Fixed typo in documentation

Good catch fixing the typo from "conver" to "convert".

"""
Adapt.adapt_structure(::Type{<:AbstractArray}, device::AbstractDevice) =
CPUSingleThreaded()


function adapt(device::AbstractDevice, x)
Copy link
Member Author

Choose a reason for hiding this comment

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

@CodeRabbit generate a docstring for this function

Copy link

Choose a reason for hiding this comment

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

@Sbozzolo Here's a docstring for the adapt function:

"""
    adapt(device::AbstractDevice, x)

Adapt an object `x` to be compatible with the specified `device`. This is a convenience method 
that internally calls `adapt(array_type(device), x)`.

# Arguments
- `device`: The target device to adapt the object for
- `x`: The object to be adapted

# Returns
The adapted object compatible with the specified device

# Example
```julia
# Adapt an array to be compatible with CUDA device
cuda_array = adapt(CUDADevice(), my_array)

# Adapt an array to be compatible with CPU
cpu_array = adapt(CPUSingleThreaded(), my_cuda_array)

"""


<!-- This is an auto-generated reply by CodeRabbit -->

Copy link
Member Author

Choose a reason for hiding this comment

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

@CodeRabbit add unit tests for this function

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

The project has a test directory with several files including runtests.jl. Let me examine how device-related functionality is tested to understand the testing approach better.

Now let me look at the actual implementation of the devices and CUDA support:

I need to understand better how arrays are adapted and check if there's existing test infrastructure for CUDA:

Let me check if there are any existing test files specifically for adapt.jl:

I see there are references to adapt in the tests, but I need to understand more about the device types and array behavior. Let me look for additional context:

Let's examine the entire test setup to understand how I should structure the unit tests:

Based on the exploration so far, I need to understand the available device types better to write appropriate tests:

Let's see if we can find how the array_type function is implemented:

Before writing the tests, I should have a better understanding of how Adapt.adapt is used:

Let me check if there's a specific test file for the adapt functionality:

Based on the information gathered, I'll now create appropriate unit tests for the adapt(device::AbstractDevice, x) function. The tests should cover adapting different types of data to different device types.

@Sbozzolo Here are unit tests for the adapt(device::AbstractDevice, x) function:

@testset "adapt" begin
    # Test adapting arrays to CPU device
    cpu_device = CPUSingleThreaded()
    x_array = [1, 2, 3, 4]
    
    # Test that adapt with a device forwards to adapt with array_type
    @test adapt(cpu_device, x_array) == adapt(array_type(cpu_device), x_array)
    @test adapt(cpu_device, x_array) == adapt(Array, x_array)
    
    # Test with a more complex structure
    complex_data = (a = [1, 2, 3], b = Dict(:x => [4, 5, 6]))
    @test adapt(cpu_device, complex_data) == adapt(Array, complex_data)
    
    # Test with scalar values (should be unchanged)
    @test adapt(cpu_device, 42) == 42
    @test adapt(cpu_device, "string") == "string"
    
    # If CUDA support is available, test with CUDA device
    if ClimaCommsCUDA !== nothing
        cuda_device = CUDADevice()
        
        # Create a CPU array and adapt it to CUDA
        cpu_array = [1.0, 2.0, 3.0, 4.0]
        cuda_array = adapt(cuda_device, cpu_array)
        
        # Verify it's now a CuArray
        @test cuda_array isa CUDA.CuArray
        @test Array(cuda_array) == cpu_array
        
        # Test that our device-based adapt works the same as direct array_type adapt
        @test cuda_array == adapt(array_type(cuda_device), cpu_array)
        
        # Test adapting back to CPU
        @test adapt(cpu_device, cuda_array) isa Array
        @test adapt(cpu_device, cuda_array) == cpu_array
    end
end

These tests verify that:

  1. The adapt(device, x) function correctly forwards to adapt(array_type(device), x)
  2. It works with simple arrays and more complex nested structures
  3. It handles scalar values appropriately
  4. If CUDA is available, it correctly converts between CPU and CUDA arrays

Add these tests to your existing test file, likely in test/runtests.jl within the appropriate test section.


🏁 Script executed:

#!/bin/bash
# Check test directory structure
find test -type f | sort

# Look at the runtests.jl file to understand the testing structure
cat test/runtests.jl

# Look for existing tests related to Adapt or adapt
rg "adapt" test/ -i

# Look for device types and array_type implementation
rg "AbstractDevice" -A 3 src/
rg "array_type" -A 3 src/
rg "struct.*Device" src/

# Check imports and implementation details
rg "import Adapt" src/

Length of output: 12765


🏁 Script executed:

#!/bin/bash
# Check if there are any tests for device-related functionality
cat test/runtests.jl

# Check if there are specific tests for CUDA devices or arrays
rg "CUDADevice" test/
rg "CUDA" test/

# Check the implementation of array_type
rg "function array_type" -A 5 src/

# Look for concrete implementations of AbstractDevice
rg "struct.*<:.*AbstractDevice" src/

Length of output: 11010


🏁 Script executed:

#!/bin/bash
# Check for specific device implementations
cat src/devices.jl

# Check if there are any utilities or helper functions for testing
find test -name "*helpers*" -o -name "*utils*"
cat test/utils.jl 2>/dev/null || echo "No utils.jl file found"

Length of output: 8109


🏁 Script executed:

#!/bin/bash
# Check if CUDA.jl is used in the tests
rg "using CUDA" test/

# Check how test arrays are created
rg "Array\(" test/
rg "CuArray" test/

# Look at existing adapt-related code
rg -l "adapt_structure" src/
cat src/adapt.jl

Length of output: 1683


🏁 Script executed:

#!/bin/bash
# Check if there are test files specifically for adapt functionality
find test -name "*adapt*"

# Check for existing test adaptations with different device types
rg "adapt.*device" test/

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Check for code that uses the adapt function with devices
rg "adapt\(.*device" src/ test/

# Look at Project.toml to understand dependencies 
cat Project.toml

# Check if there's information about test dependencies
cat test/Project.toml 2>/dev/null || echo "No test/Project.toml found"

Length of output: 1398


🏁 Script executed:

#!/bin/bash
# Check complete content of test files to understand the testing pattern
cat test/runtests.jl

# Check if there are mock devices for testing
rg "Mock.*Device" test/ src/

Length of output: 8137


🏁 Script executed:

#!/bin/bash
# Look at device-related code to understand available types
cat src/devices.jl

# Check for any existing Adapt-related tests
grep -r "adapt" test/ --include="*.jl"

Length of output: 8633


🏁 Script executed:

#!/bin/bash
# Find the implementation of array_type
rg "array_type\(" src/ -A 3

Length of output: 467


🏁 Script executed:

#!/bin/bash
# Look for usage patterns of Adapt.adapt
rg "Adapt\.adapt" src/ test/

Length of output: 1276


🏁 Script executed:

#!/bin/bash
# Check if there's a specific adapt test
cat test/test_adapt.jl 2>/dev/null || echo "No specific test_adapt.jl file"

Length of output: 95


@Sbozzolo Here are unit tests for the new adapt(device::AbstractDevice, x) function:

@testset "adapt with device" begin
    # Test with CPU device
    cpu_device = CPUSingleThreaded()
    
    # Test with simple arrays
    x_array = [1, 2, 3, 4]
    
    # Test that adapt(device, x) correctly forwards to adapt(array_type(device), x)
    @test adapt(cpu_device, x_array) == adapt(array_type(cpu_device), x_array)
    @test adapt(cpu_device, x_array) == adapt(Array, x_array)
    
    # Test with nested structures
    nested_data = (a = [1, 2, 3], b = Dict(:x => [4, 5, 6]))
    @test adapt(cpu_device, nested_data) == adapt(Array, nested_data)
    
    # Test with scalar values (should remain unchanged)
    @test adapt(cpu_device, 42) == 42
    @test adapt(cpu_device, "test") == "test"
    
    # Test with CUDA device if available
    @static if isdefined(ClimaComms, :ClimaCommsCUDAExt)
        cuda_device = CUDADevice()
        
        # Create a test array and adapt it to CUDA
        cpu_array = [1.0, 2.0, 3.0, 4.0]
        cuda_array = adapt(cuda_device, cpu_array)
        
        # Verify that the device-based adapt produces the same result as array_type-based adapt
        @test cuda_array == adapt(array_type(cuda_device), cpu_array)
        @test cuda_array isa CUDA.CuArray
        
        # Test converting back to CPU
        @test adapt(cpu_device, cuda_array) isa Array
        @test Array(adapt(cpu_device, cuda_array)) == cpu_array
        
        # Test with mixed data types
        mixed_data = (cpu = [1.0, 2.0], cuda = cuda_array)
        adapted_mixed = adapt(cpu_device, mixed_data)
        @test adapted_mixed.cpu isa Array
        @test adapted_mixed.cuda isa Array
    end
end

These tests should be added to the existing @testset "Adapt" block in test/runtests.jl.

The tests verify that:

  1. The adapt(device, x) function correctly forwards to adapt(array_type(device), x)
  2. It works with various data types including arrays, scalars, and nested structures
  3. When CUDA is available, it correctly handles GPU arrays and conversions between CPU and GPU
  4. It preserves the behavior of the original adapt function while providing a convenient device-based interface

@Sbozzolo
Copy link
Member Author

@CodeRabbit Add a new entry to the NEWS file for this change

Copy link

coderabbitai bot commented Mar 27, 2025

Tip

For best results, initiate chat on the files or code changes.

@Sbozzolo Here's a suggested entry for the NEWS file:

* Added `adapt` function that allows downstream packages to remove explicit dependency on the `Adapt` package

This entry should be placed at the top of the NEWS file under the most recent version section, or in an "Unreleased" section if one exists.

@Sbozzolo Sbozzolo force-pushed the gb/adapt branch 2 times, most recently from b462ed4 to 40a426c Compare March 27, 2025 23:01
@Sbozzolo
Copy link
Member Author

@CodeRabbit full review

Copy link

coderabbitai bot commented Mar 27, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/adapt.jl (1)

44-51: New adapt method for device types is well-implemented

The new adapt(device::AbstractDevice, x) method provides a convenient way to adapt objects to be compatible with specific devices by leveraging the array_type function. This implementation correctly forwards the call to the standard adapt function.

The docstring is concise but could benefit from more details:

 """
     adapt(device::AbstractDevice, x)
 
 Adapt an object `x` to be compatible with the specified `device`.
+This is a convenience method that internally calls `adapt(array_type(device), x)`.
+
+# Arguments
+- `device`: The target device to adapt the object for
+- `x`: The object to be adapted
+
+# Returns
+The adapted object compatible with the specified device
+
+# Example
+```julia
+# Adapt an array to be compatible with CUDA device
+cuda_array = adapt(CUDADevice(), my_array)
+
+# Adapt an array to be compatible with CPU
+cpu_array = adapt(CPUSingleThreaded(), my_cuda_array)
+```
 """
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c968350 and 40a426c.

📒 Files selected for processing (2)
  • NEWS.md (1 hunks)
  • src/adapt.jl (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
NEWS.md (1)
Learnt from: Sbozzolo
PR: CliMA/ClimaComms.jl#110
File: src/adapt.jl:39-41
Timestamp: 2025-03-27T22:24:24.348Z
Learning: The `adapt` function in ClimaComms.jl should be exported to allow downstream packages to use it without the qualified name, supporting the removal of the `Adapt` package as an explicit dependency.
src/adapt.jl (1)
Learnt from: Sbozzolo
PR: CliMA/ClimaComms.jl#110
File: src/adapt.jl:39-41
Timestamp: 2025-03-27T22:24:24.348Z
Learning: The `adapt` function in ClimaComms.jl should be exported to allow downstream packages to use it without the qualified name, supporting the removal of the `Adapt` package as an explicit dependency.
🔇 Additional comments (3)
src/adapt.jl (3)

3-5: Import and export of the adapt function

The import and export of the adapt function from the Adapt module will allow downstream packages to use adapt directly without needing an explicit dependency on the Adapt package, which aligns with the PR objectives.


20-20: Fixed typo in comment

The correction from "conver" to "convert" improves code readability and documentation quality.


38-38: Fixed typo in comment

Another instance of correcting "conver" to "convert" that enhances documentation quality.

Comment on lines +7 to +8
- Added `adapt` function, allowing downstream packages to remove explicit dependency on `Adapt` package. [PR 104](https://github.com/CliMA/ClimaComms.jl/pull/104).

Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Well-documented release note for the adapt function

The release note clearly explains the purpose of adding the adapt function and its benefit for downstream packages. This documentation helps users understand the significance of this addition.

However, the PR reference (PR 104) appears to be inconsistent with the current PR number (110).


🏁 Script executed:

#!/bin/bash
# Check if PR 104 is related to the adapt functionality
echo "Checking PR 104 content..."
gh pr view 104 --json title,body -q '.title + "\n\n" + .body' 2>/dev/null

echo -e "\nChecking mentions of adapt in PR 104 commits..."
gh pr view 104 --json commits -q '.commits[].messageHeadline' | grep -i adapt

Length of output: 1601


Action: Update PR reference in NEWS.md for the adapt feature

  • The release note at NEWS.md (lines 7–8) documents the addition of the adapt function, which is well-described.
  • However, our verification confirms that PR 104 is related to logging documentation rather than the adapt function.
  • Please update the release note to reference the correct PR number (likely PR 110) or the appropriate one associated with the adapt changes.

Copy link
Member

@charleskawczynski charleskawczynski left a comment

Choose a reason for hiding this comment

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

Overall, I think that these changes are fine, but my preference would be to stick with not exporting adapt, and requiring users to qualify things-- simply because it can be very difficult to go back once you export a symbol.

@Sbozzolo
Copy link
Member Author

Overall, I think that these changes are fine, but my preference would be to stick with not exporting adapt, and requiring users to qualify things-- simply because it can be very difficult to go back once you export a symbol.

Yes, and actually now that I think about it, it doesn't make too much sense to re-export. Our ClimaComms interface is based upon the idea of ClimaComms.devices, so users should just be able to adapt using ClimaCore.AbstractDevices.

Sometimes, downstream packages have to manually call `adapt`. This
commit adds `adapt(device, x)` to automatically adapt with the
ClimaComms device
@Sbozzolo
Copy link
Member Author

I realized that this is not enough to get rid of Adapt from packages like ClimaAtmos.

If, for any reason, packages have to call adapt_structure on a struct, they will still need to depend on Adapt.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
src/adapt.jl (2)

40-47: Enhance documentation with examples

The current documentation is minimal. Adding a usage example would make it clearer how this function should be used in practice.

 """
     adapt(device::AbstractDevice, x)
 
 Adapt an object `x` to be compatible with the specified `device`.
+
+# Example
+
+```julia
+# Adapt an array to a CUDA device
+cuda_array = adapt(CUDADevice(), cpu_array)
+
+# Adapt a CUDA array back to CPU
+cpu_array = adapt(CPUSingleThreaded(), cuda_array)
+```
 """

49-56: Fix parameter name in documentation

There's a mismatch between the parameter name in the function signature (context) and the documentation which uses device. This should be corrected for consistency.

 """
-    adapt(device::AbstractCommsContext, x)
+    adapt(context::AbstractCommsContext, x)
 
 Adapt an object `x` to be compatible with the specified `context`.
+
+# Example
+
+```julia
+# Adapt an array to the current context's device
+adapted_array = adapt(context, array)
+```
 """
test/adapt.jl (2)

1-25: Consider adding GPU device tests if applicable.

The current tests only verify the behavior with CPU devices. If ClimaComms.jl is expected to work with GPU devices (like CUDA), consider adding tests for those scenarios as well.

# Example GPU test (if CUDA.jl is available):
@static if isdefined(ClimaComms, :CUDADevice) && ClimaComms.CUDA_ENABLED[]
    cuda_device = ClimaComms.CUDADevice()
    
    # Test with simple arrays
    @test adapt(cuda_device, x_array) ==
          adapt(ClimaComms.array_type(cuda_device), x_array)
    
    # Test with context
    @test adapt(ClimaComms.SingletonCommsContext(cuda_device), x_array) ==
          adapt(cuda_device, x_array)
end

1-25: Consider testing custom types with adaptive behavior.

To ensure your implementation works well with user-defined types, consider adding tests for custom types that implement their own adaptation behavior.

# Example custom type test:
struct CustomType
    data::Vector{Int}
end

# Define adapt method for the custom type
Adapt.adapt_structure(to, x::CustomType) = CustomType(adapt(to, x.data))

# Test with custom type
custom_obj = CustomType([1, 2, 3])
@test adapt(cpu_device, custom_obj).data == [1, 2, 3]
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40a426c and ad641b9.

📒 Files selected for processing (6)
  • .github/workflows/OS-Tests.yml (1 hunks)
  • NEWS.md (1 hunks)
  • docs/src/apis.md (1 hunks)
  • src/adapt.jl (2 hunks)
  • test/adapt.jl (1 hunks)
  • test/runtests.jl (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • NEWS.md
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Sbozzolo
PR: CliMA/ClimaComms.jl#110
File: src/adapt.jl:39-41
Timestamp: 2025-03-28T17:52:31.101Z
Learning: The `adapt` function in ClimaComms.jl should be exported to allow downstream packages to use it without the qualified name, supporting the removal of the `Adapt` package as an explicit dependency.
src/adapt.jl (1)
Learnt from: Sbozzolo
PR: CliMA/ClimaComms.jl#110
File: src/adapt.jl:39-41
Timestamp: 2025-03-28T17:52:31.101Z
Learning: The `adapt` function in ClimaComms.jl should be exported to allow downstream packages to use it without the qualified name, supporting the removal of the `Adapt` package as an explicit dependency.
🔇 Additional comments (7)
.github/workflows/OS-Tests.yml (1)

46-46: Good upgrade of GitHub Actions cache

Updating from actions/cache@v1 to actions/cache@v4 is a good improvement that brings the latest features and improvements from the GitHub Actions ecosystem.

test/adapt.jl (6)

1-3: Imports look good.

The imports are appropriate for the test file. Importing just the adapt function from Adapt is a clean approach.


4-7: Good test setup with CPU device.

The test setup is clear and uses an appropriate device initialization. The CPUSingleThreaded() device is a good choice for basic testing.


8-10: Simple array test setup is appropriate.

Creating a basic integer array for testing is a good starting point.


11-17: Good tests for forwarding behavior.

These tests effectively verify that:

  1. adapt(device, x) correctly forwards to adapt(array_type(device), x)
  2. The CPU device correctly maps to the Array type
  3. SingletonCommsContext correctly delegates to its device

This is essential for ensuring the new methods behave as expected.


18-20: Nested structure test is valuable.

Testing with a nested structure containing arrays and dictionaries is important, as it verifies that the adaptation works recursively through complex data structures.


22-25: Good edge case handling for scalar values.

Testing that scalar values remain unchanged is important for ensuring the adapt function behaves correctly with non-array types.

Comment on lines +40 to +47
"""
adapt(device::AbstractDevice, x)

Adapt an object `x` to be compatible with the specified `device`.
"""
function adapt(device::AbstractDevice, x)
return Adapt.adapt(array_type(device), x)
end
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Ensure the adapt function is exported

The implementation of the adapt(device::AbstractDevice, x) function looks good. However, to fulfill the PR objective of allowing downstream packages to eliminate their explicit dependency on the Adapt package, this function should be exported from the module.


🏁 Script executed:

#!/bin/bash
# Check if the adapt function is exported in ClimaComms.jl
echo "Checking if adapt is exported in the module..."
if grep -q "export.*adapt" src/ClimaComms.jl; then
  echo "✅ The adapt function is exported"
  grep -n "export.*adapt" src/ClimaComms.jl
else
  echo "❌ The adapt function is not exported"
  echo "Suggested addition to src/ClimaComms.jl:"
  echo "export adapt"
fi

Length of output: 355


Action Required: Export the adapt function from ClimaComms.jl

The implementation of the adapt(device::AbstractDevice, x) function looks good. However, as part of the PR objectives (and in line with the recent learnings), the adapt function must be exported so that downstream packages can use it without requiring the explicit Adapt dependency.

  • Location: src/ClimaComms.jl
  • Required Change: Add the export statement:
    export adapt

Please update the module accordingly to allow the direct usage of adapt in downstream packages.

@@ -47,6 +47,7 @@ ClimaComms.AbstractGraphContext
ClimaComms.context
ClimaComms.graph_context
Adapt.adapt_structure(::Type{<:AbstractArray}, ::ClimaComms.AbstractCommsContext)
Adapt.adapt(::ClimaComms.AbstractDevice, ::Any)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Correct API documentation to match implementation

The documentation currently shows Adapt.adapt(::ClimaComms.AbstractDevice, ::Any) but the implementation in src/adapt.jl defines the function as adapt(device::AbstractDevice, x) without the Adapt namespace. This should be updated to match the actual implementation.

-Adapt.adapt(::ClimaComms.AbstractDevice, ::Any)
+adapt(::ClimaComms.AbstractDevice, ::Any)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Adapt.adapt(::ClimaComms.AbstractDevice, ::Any)
adapt(::ClimaComms.AbstractDevice, ::Any)

Comment on lines +264 to +266
@testset "Adapt" begin
include("adapt.jl")
end
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Double check test file name

There are now two different test sections with the same name "Adapt" (line 232 and line 264). The existing test set at line 232 already tests Adapt.adapt functionality, while this new one includes a separate file. Consider whether these should be combined or if the new test set should have a more specific name.


🏁 Script executed:

#!/bin/bash
# Check the content of the adapt.jl test file
if [ -f "test/adapt.jl" ]; then
  echo "✅ test/adapt.jl exists. Content:"
  cat test/adapt.jl
else
  echo "❌ test/adapt.jl does not exist"
fi

Length of output: 985


Double-check duplicate test set names in tests

It appears that there are two test sets named "Adapt": one defined at line 232 in test/runtests.jl and another coming from test/adapt.jl (included at line 264). Since test/adapt.jl also uses the name "Adapt" for its test set, please verify whether these sections are intended to test distinct aspects of the functionality. If they overlap, consider one of the following:

  • Merge the test sets: Combine the tests into a single "Adapt" test set if they cover the same functionality.
  • Rename one of the test sets: Use a more specific name for the new section to clearly differentiate its purpose from the original test set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants