Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
23b2f60
Mimic cupynumeric Python random number generation (#176)
ejmeitz Aug 14, 2026
3e56672
leverage DEPOT path
krasow Aug 14, 2026
7df9986
fix libcxxwrap
krasow Aug 14, 2026
f663c65
try this
krasow Aug 14, 2026
773fc6f
cp cache into temp storage
krasow Aug 14, 2026
38e70d2
Subtype `AbstractArray` + LinearAlgebra.Diagonal Support (#165)
ejmeitz Aug 15, 2026
8f44dec
Support more unary/binary ops (#177)
ejmeitz Aug 15, 2026
1b18c55
Support argmin, argmax, var, std, mean (#178)
ejmeitz Aug 15, 2026
ceaff87
use concurrency queues to limit jobs of same type clobbering each other
krasow Aug 16, 2026
ae4713d
lock/unlock
krasow Aug 16, 2026
b2cd2d2
Add more linear algebra ops (i.e., eigen) (#183)
ejmeitz Aug 17, 2026
bd99a72
Dynamic Mode Decomposition Example + Benchmark (#184)
ejmeitz Aug 17, 2026
6a544f0
New @accelerate macro explicit julia scoping semantics and CUDA.jl la…
krasow Aug 18, 2026
b79a17f
Add GPU fft/ifft/fft!/ifft! and batched FFT on NDArray (#185)
ejmeitz Aug 18, 2026
2c5bb7c
Support tensor contraction (#186)
ejmeitz Aug 23, 2026
2d48197
Add sort, sort!, sortperm, searchsortedfirst, searchsortedlast (#181)
ejmeitz Aug 24, 2026
b7aa2fc
remvoe some unecessary global keywords
ejmeitz Aug 24, 2026
52c9783
Implement == and != as 0-d Bool reductions. (#187)
ejmeitz Aug 24, 2026
1e0bfb8
support new JLL wrapper
ejmeitz Aug 24, 2026
fda74c7
fix docs project compat
ejmeitz Aug 24, 2026
28f65e9
TensorOperations.jl Package Extension (#188)
ejmeitz Aug 31, 2026
cf5dc4d
update some docs
ejmeitz Sep 1, 2026
f03ede3
return 0D NDArray from TensorOperations API (#190)
ejmeitz Sep 2, 2026
3ecab6f
revamp benchmark suite (#191)
ejmeitz Sep 10, 2026
a7ffb3b
Prototype owning Julia array conversions and preserve FFT submission …
ejmeitz Sep 11, 2026
51a2f40
cuSolverMp (#192)
ejmeitz Sep 13, 2026
4319527
bump compat versions
ejmeitz Sep 14, 2026
1277af1
Container Opt-In CI (#194)
krasow Sep 14, 2026
26d717f
Preserve fused compound broadcasts in accelerate (#200)
krasow Sep 17, 2026
9f47cce
mapreduce (#196)
ejmeitz Sep 18, 2026
50e3f8e
1.13 Julia CI Matrix Support (#197)
krasow Sep 19, 2026
e40358a
Remove Benchmarking Module (#195)
krasow Sep 20, 2026
168a0f3
run CI and build [container]
krasow Sep 20, 2026
c4157d1
CI: simplfy libcxxwrap validation tests
krasow Sep 20, 2026
6134965
Container builder update to Julia 1.13 (#201)
krasow Sep 22, 2026
4d15553
Add some linear algebra ops on vectors (#202)
ejmeitz Sep 23, 2026
b56fdd9
NDScalar Type + Opt-In Auto Unwrapping of Scalars (#203)
ejmeitz Sep 23, 2026
1e7a370
Krylov.jl Extension (#205)
ejmeitz Sep 23, 2026
ea46636
Load CUDACore before Legate to reduce startup recompilation (#209)
ejmeitz Sep 24, 2026
8a4c748
Add NDarray map!, host copyto!, & bfft! (#204)
krasow Sep 25, 2026
56bef00
Support low-storage ODE scalar and muladd operations (#206)
ejmeitz Sep 25, 2026
8db57ea
AoS and SoA Support (#207)
krasow Sep 26, 2026
6bec8e6
Inplace slice assignment (#210)
krasow Sep 26, 2026
999d634
Undef Initialization (#213)
ejmeitz Sep 26, 2026
985906c
1d slice set-index (#215)
krasow Sep 26, 2026
047acfb
Fix buildkite JLL GPU CI triggers (#216)
krasow Sep 27, 2026
62615c0
update cupynumeric-compat requirement (#217)
krasow Sep 27, 2026
462ae64
update compate with benchmark and make [container]
krasow Sep 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .buildkite/developer.pipeline.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
steps:
- label: ":lock: Enter developer CI"
command: "true"
concurrency: 1
concurrency_group: "cunumeric/developer"
agents:
queue: "cuda"

- wait: ~

- group: ":hammer: Developer"
key: "developer"
steps:
Expand All @@ -7,7 +16,7 @@ steps:
- JuliaCI/julia#v1:
version: "{{matrix.julia}}"
# Per version AND fusion: dev wrappers are ABI-specific, and same-depot
# concurrent jobs contend on Pkg locks. Separate from the JLL cache.
# concurrent jobs contend on Pkg locks. Separate from the JLL cache.
cache_dir: "${HOME}/.cache/julia-buildkite-plugin-developer-{{matrix.julia}}-{{matrix.fusion}}"
command: ".buildkite/run_developer_ci.sh"
artifact_paths:
Expand All @@ -32,6 +41,17 @@ steps:
- "1.10"
- "1.11"
- "1.12"
- "1.13"
fusion:
- "on"
- "off"

- wait: ~
continue_on_failure: true

- label: ":unlock: Leave developer CI"
command: "true"
concurrency: 1
concurrency_group: "cunumeric/developer"
agents:
queue: "cuda"
9 changes: 5 additions & 4 deletions .buildkite/jll.pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ steps:
plugins:
- JuliaCI/julia#v1:
version: "{{matrix.julia}}"
# Developer builds install local wrapper overrides into the depot.
# Keep JLL tests in a separate cache so those overrides cannot leak here.
cache_dir: "${HOME}/.cache/julia-buildkite-plugin-jll"
# Isolate Julia versions and compile-time fusion preferences, and keep
# JLL tests separate from developer wrapper overrides.
cache_dir: "${HOME}/.cache/julia-buildkite-plugin-jll-{{matrix.julia}}-{{matrix.fusion}}"
- jquick/pre-hook#v1.2.0:
command: |
if [ "{{matrix.fusion}}" = "on" ]; then
Expand All @@ -23,7 +23,7 @@ steps:
fi
julia --project -e 'using Pkg; Pkg.resolve(); Pkg.instantiate()'
- JuliaCI/julia-test#v1:
test_args: "--quickfail --jobs=8 --verbose"
test_args: "--jobs=8 --verbose"
- JuliaCI/julia-coverage#v1:
dirs:
- src
Expand All @@ -50,6 +50,7 @@ steps:
- "1.10"
- "1.11"
- "1.12"
- "1.13"
fusion:
- "on"
- "off"
Expand Down
2 changes: 1 addition & 1 deletion .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ steps:
command: ".buildkite/upload_gpu_ci.sh"
agents:
queue: "cuda"
if: build.message !~ /\[skip tests\]/
if: build.message !~ /\[skip ci\]/
if_changed: "{src/**,scripts/**,deps/build.jl,Project.toml,lib/CNPreferences/src/**,lib/cunumeric_jl_wrapper/**}"
timeout_in_minutes: 5
16 changes: 12 additions & 4 deletions .buildkite/run_developer_ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,20 @@ sh "$CMAKE_INSTALLER" --skip-license --prefix="$CMAKE_ROOT"
export PATH="$CMAKE_ROOT/bin:$PATH"
cmake --version

# Exercise libcxxwrap cache validation separately from package tests, which run in
# JLL jobs where no build toolchain is installed.
julia --startup-file=no test/build_cxxwrap.jl

# Clean slate so cached state doesn't leak across Julia versions.
rm -f Manifest.toml test/Manifest.toml dev/Manifest.toml \
LocalPreferences.toml test/LocalPreferences.toml

# Dev mode rebuilds the wrapper .so each run, so drop stale overrides and the .ji that
# bake @wrapmodule bindings (cuNumeric/Legate + wrapper JLLs) — a cached .ji would
# mismatch the fresh .so and segfault. libcxxwrap override is kept.
# Build directly in the plugin's persistent cache so artifacts, precompile, and libcxxwrap all
# stay warm across runs. Developer builds are serialized across PRs, while each matrix job in a
# build uses a separate (Julia, fusion) cache.
# Dev mode rebuilds the wrapper .so each run, so drop stale overrides and the .ji that bake
# @wrapmodule bindings (cuNumeric/Legate + wrapper JLLs) — a cached .ji would mismatch the fresh
# .so and segfault. libcxxwrap's dev build is kept and reused.
DEPOT="$(julia --startup-file=no -e 'print(DEPOT_PATH[1])')"
rm -rf "$DEPOT"/packages/*/*/override \
"$DEPOT"/compiled/v*/{cuNumeric,Legate,cunumeric_jl_wrapper_jll,legate_jl_wrapper_jll}
Expand Down Expand Up @@ -83,5 +90,6 @@ cp LocalPreferences.toml test/LocalPreferences.toml

julia --color=yes --project=. -e '
using Pkg
Pkg.test("cuNumeric"; test_args = ["--quickfail", "--jobs=8", "--verbose"])
Pkg.develop(PackageSpec(path = "lib/CNPreferences"))
Pkg.test("cuNumeric"; test_args = ["--jobs=8", "--verbose"])
'
51 changes: 26 additions & 25 deletions .buildkite/upload_gpu_ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,42 @@ set -euo pipefail

readonly JLL_PIPELINE=".buildkite/jll.pipeline.yml"
readonly DEVELOPER_PIPELINE=".buildkite/developer.pipeline.yml"
readonly WRAPPER_PATH="lib/cunumeric_jl_wrapper"
readonly WRAPPER_BASE_BRANCH="main"

branch="${BUILDKITE_BRANCH:-}"
base_branch="${BUILDKITE_PULL_REQUEST_BASE_BRANCH:-}"
pull_request="${BUILDKITE_PULL_REQUEST:-false}"
message="${BUILDKITE_MESSAGE:-}"

run_jll=true
run_developer=true

# Keep both suites for main and PRs into main. For non-main PRs, select the
# suite whose wrapper matches the code under test.
if [[ "$message" =~ \[skip[[:space:]]ci\] ]]; then
echo "Skipping all GPU CI because the build message requests it."
exit 0
fi

if [[ "$message" =~ \[skip[[:space:]]jll\] ]]; then
echo "Skipping JLL GPU CI because the build message contains [skip jll]."
run_jll=false
fi
if [[ "$message" =~ \[skip[[:space:]]dev\] ]]; then
echo "Skipping developer GPU CI because the build message contains [skip dev]."
run_developer=false
fi

# Keep both suites for main and PRs into main. Otherwise use the JLL suite only
# when the wrapper matches the released JLL source.
if [[ "$branch" != "main" && "$base_branch" != "main" ]]; then
if [[ "$message" =~ \[skip[[:space:]]jll\] ]]; then
echo "Skipping JLL GPU CI because the build message contains [skip jll]."
run_jll=false
elif [[ "$pull_request" != "false" && -n "$base_branch" ]]; then
base_ref="refs/remotes/origin/$WRAPPER_BASE_BRANCH"
# The published wrapper JLL tracks main, so compare against main even
# when the pull request targets develop.
git fetch --no-tags origin "+refs/heads/${WRAPPER_BASE_BRANCH}:${base_ref}"

if git diff --quiet "${base_ref}...HEAD" -- "$WRAPPER_PATH"; then
echo "No wrapper changes detected against origin/$WRAPPER_BASE_BRANCH; using JLL GPU CI."
run_developer=false
if scripts/wrapper_changed.sh; then
echo "Wrapper matches the released JLL source; using JLL GPU CI."
run_developer=false
else
diff_status=$?
if ((diff_status == 1)); then
echo "Wrapper differs from the released JLL source; using developer GPU CI."
run_jll=false
else
diff_status=$?
if ((diff_status == 1)); then
echo "Wrapper changes detected against origin/$WRAPPER_BASE_BRANCH; using developer GPU CI."
run_jll=false
else
echo "Could not determine whether the wrapper changed against origin/$WRAPPER_BASE_BRANCH." >&2
exit "$diff_status"
fi
echo "Could not determine whether the wrapper matches the released JLL source." >&2
exit "$diff_status"
fi
fi
fi
Expand Down
47 changes: 47 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Local Julia environments and generated build configuration.
**/*Manifest.toml
**/*LocalPreferences.toml
dev/Project.toml
deps/deps.jl
build_wrapper.sh
compile_wrapper.sh

# Local caches, logs, and editor configuration.
**/__pycache__
**/*.pyc
**/*.log
**/*.err
**/*.prof
**/node_modules
.vscode
.envrc
.localenv
docs/.vitepress
docs/src/.vitepress/cache
docs/src/.vitepress/dist

benchmark/results
benchmark/plots
# Host build products must not overwrite artifacts built inside the image.
**/build
**/*.o
**/*.obj
**/*.so
**/*.so.*
**/*.dylib
**/*.dll
**/*.a
**/*.lib
**/*.exe
deps/cupynumeric-*
deps/lapacke_build
libcupynumeric
install-libcupynumeric
install-cupynumeric
libcxxwrap-julia
lapacke

# Keep root repository metadata intact, including files matching rules above.
**/.git
!.git
!.git/**
69 changes: 69 additions & 0 deletions .github/scripts/check_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
"""Version consistency check for pull requests targeting main."""

import argparse
import json
import re
import subprocess
import sys
import urllib.parse
import urllib.request
from pathlib import Path

REPO_ROOT = Path(subprocess.run(
Expand All @@ -18,6 +21,10 @@

WRAPPER_VERSION_FILE = "lib/cunumeric_jl_wrapper/VERSION"
WRAPPER_JLL_COMPAT_KEY = "cunumeric_jl_wrapper_jll"
WRAPPER_RELEASED_COMMIT_FILE = "lib/cunumeric_jl_wrapper/RELEASED_COMMIT"
WRAPPER_JLL_REPO = "JuliaBinaryWrappers/cunumeric_jl_wrapper_jll.jl"
WRAPPER_JLL_TAG_PREFIX = "cunumeric_jl_wrapper-v"
WRAPPER_SOURCE_REPO = "https://github.com/JuliaLegate/cuNumeric.jl.git"
WRAPPER_SRC_PREFIXES = (
"lib/cunumeric_jl_wrapper/src/",
"lib/cunumeric_jl_wrapper/include/",
Expand Down Expand Up @@ -149,6 +156,67 @@ def check_wrapper_compat_sync(pr_toml: str, errors: list):
print(f"\t\tOK")


def http_get(url: str) -> str:
request = urllib.request.Request(url, headers={"User-Agent": "cuNumeric-version-check"})
with urllib.request.urlopen(request, timeout=30) as response:
return response.read().decode()


def released_wrapper_commit(version: str) -> tuple:
"""Return (tag, source revision) of the newest JLL build of `version`."""
refs = json.loads(http_get(
f"https://api.github.com/repos/{WRAPPER_JLL_REPO}/git/matching-refs/tags/"
f"{WRAPPER_JLL_TAG_PREFIX}{version}+"
))
builds = {}
for ref in refs:
tag = ref["ref"].removeprefix("refs/tags/")
m = re.fullmatch(re.escape(WRAPPER_JLL_TAG_PREFIX + version) + r"\+(\d+)", tag)
if m:
builds[int(m.group(1))] = tag
if not builds:
return None, None
tag = builds[max(builds)]
readme = http_get(
f"https://raw.githubusercontent.com/{WRAPPER_JLL_REPO}/{urllib.parse.quote(tag)}/README.md"
)
m = re.search(re.escape(WRAPPER_SOURCE_REPO) + r" \(revision: `([0-9a-f]{40})`\)", readme)
return tag, m.group(1) if m else None


def check_wrapper_released_commit(pr_toml: str, errors: list):
compat_ver = parse_compat_section(pr_toml).get(WRAPPER_JLL_COMPAT_KEY)
recorded = (REPO_ROOT / WRAPPER_RELEASED_COMMIT_FILE).read_text().strip()

print(f"\t[wrapper released commit]")
print(f"\t\t{WRAPPER_RELEASED_COMMIT_FILE} = {recorded}")
if compat_ver is None:
return # reported by check_wrapper_compat_sync
try:
tag, released = released_wrapper_commit(compat_ver)
except Exception as e:
errors.append(
f"Could not look up the source revision of {WRAPPER_JLL_COMPAT_KEY} {compat_ver}: {e}"
)
return

if tag is None:
errors.append(
f"{WRAPPER_JLL_COMPAT_KEY} {compat_ver} has no release in {WRAPPER_JLL_REPO}.\n"
f"\tRelease the wrapper JLL before merging into main."
)
elif released is None:
errors.append(f"Could not find the source revision in the README of {WRAPPER_JLL_REPO} {tag}.")
elif released != recorded:
errors.append(
f"{WRAPPER_RELEASED_COMMIT_FILE} is stale.\n"
f"\t{tag} was built from {released}, but the file records {recorded}.\n"
f"\tSet {WRAPPER_RELEASED_COMMIT_FILE} to {released}."
)
else:
print(f"\t\tOK (matches {tag})")


def check_subpkg_version(base_ref: str, pr_toml: str, changed: list, errors: list):
src_changed = [f for f in changed if any(f.startswith(p) for p in SUBPKG_SRC_PREFIXES)]
if not src_changed:
Expand Down Expand Up @@ -210,6 +278,7 @@ def main():
check_package_version(base_ref, pr_toml, errors)
check_wrapper_version(base_ref, changed, errors)
check_wrapper_compat_sync(pr_toml, errors)
check_wrapper_released_commit(pr_toml, errors)
check_subpkg_version(base_ref, pr_toml, changed, errors)
print("─" * 60)

Expand Down
Loading
Loading