diff --git a/.github/workflows/Typos.yml b/.github/workflows/Typos.yml new file mode 100644 index 0000000000000..f9fa20fff5d12 --- /dev/null +++ b/.github/workflows/Typos.yml @@ -0,0 +1,56 @@ +name: Typos + +permissions: {} + +on: [pull_request] + +jobs: + typos-check: + name: Check for new typos + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout the JuliaLang/julia repository + uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Check spelling with typos + #uses: crate-ci/typos@master + env: + GH_TOKEN: "${{ github.token }}" + run: | + git fetch --depth=1 origin ${{ github.base_ref }} + OLD_FILES=$(git diff-index --name-only --diff-filter=ad FETCH_HEAD) + NEW_FILES=$(git diff-index --name-only --diff-filter=d FETCH_HEAD) + + mkdir -p "${{ runner.temp }}/typos" + RELEASE_ASSET_URL="$( + gh api /repos/crate-ci/typos/releases/latest \ + --jq '."assets"[] | select(."name" | test("^typos-.+-x86_64-unknown-linux-musl\\.tar\\.gz$")) | ."browser_download_url"' + )" + wget --secure-protocol=TLSv1_3 --max-redirect=1 --retry-on-host-error --retry-connrefused --tries=3 \ + --quiet --output-document=- "${RELEASE_ASSET_URL}" \ + | tar -xz -C "${{ runner.temp }}/typos" ./typos + "${{ runner.temp }}/typos/typos" --version + + echo -n $NEW_FILES | xargs "${{ runner.temp }}/typos/typos" --format json >> ${{ runner.temp }}/new_typos.jsonl || true + git checkout FETCH_HEAD -- $OLD_FILES + echo -n $OLD_FILES | xargs "${{ runner.temp }}/typos/typos" --format json >> ${{ runner.temp }}/old_typos.jsonl || true + + python -c ' + import sys, json + old = set() + with open(sys.argv[1]) as old_file: + for line in old_file: + old.add(json.loads(line)["typo"]) + clean = True + with open(sys.argv[2]) as new_file: + for line in new_file: + new = json.loads(line) + if new["typo"] not in old: + if len(new["typo"]) > 6: # Short typos might be false positives. Long are probably real. + clean = False + print("::warning file={},line={},col={}::perhaps \"{}\" should be \"{}\".".format( + new["path"], new["line_num"], new["byte_offset"], + new["typo"], " or ".join(new["corrections"]))) + sys.exit(1 if not clean else 0)' "${{ runner.temp }}/old_typos.jsonl" "${{ runner.temp }}/new_typos.jsonl" diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 00ce21ed6fcae..79444333d5ec1 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -730,6 +730,9 @@ and define matrix-matrix operations. [Dongarra-1990]: https://dl.acm.org/doi/10.1145/77626.79170 ```@docs +LinearAlgebra.BLAS.gemmt! +LinearAlgebra.BLAS.gemmt(::Any, ::Any, ::Any, ::Any, ::Any, ::Any) +LinearAlgebra.BLAS.gemmt(::Any, ::Any, ::Any, ::Any, ::Any) LinearAlgebra.BLAS.gemm! LinearAlgebra.BLAS.gemm(::Any, ::Any, ::Any, ::Any, ::Any) LinearAlgebra.BLAS.gemm(::Any, ::Any, ::Any, ::Any) diff --git a/stdlib/LinearAlgebra/src/blas.jl b/stdlib/LinearAlgebra/src/blas.jl index 8da19baee5045..998eed6a7d24c 100644 --- a/stdlib/LinearAlgebra/src/blas.jl +++ b/stdlib/LinearAlgebra/src/blas.jl @@ -63,6 +63,8 @@ export # xSYR2 # xSPR2 # Level 3 + gemmt!, + gemmt, gemm!, gemm, symm!, @@ -1481,6 +1483,88 @@ end # Level 3 ## (GE) general matrix-matrix multiplication +""" + gemmt!(uplo, tA, tB, alpha, A, B, beta, C) + +Update the lower or upper triangular part specified by [`uplo`](@ref stdlib-blas-uplo) of `C` as +`alpha*A*B + beta*C` or the other variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. +Return the updated `C`. + +!!! compat "Julia 1.11" + `gemmt!` requires at least Julia 1.11. +""" +function gemmt! end + +for (gemmt, elty) in + ((:dgemmt_,:Float64), + (:sgemmt_,:Float32), + (:zgemmt_,:ComplexF64), + (:cgemmt_,:ComplexF32)) + @eval begin + # SUBROUTINE DGEMMT(UPLO,TRANSA,TRANSB,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) + # * .. Scalar Arguments .. + # DOUBLE PRECISION ALPHA,BETA + # INTEGER K,LDA,LDB,LDC,N + # CHARACTER UPLO,TRANSA,TRANSB + # * .. Array Arguments .. + # DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) + function gemmt!(uplo::AbstractChar, transA::AbstractChar, transB::AbstractChar, + alpha::Union{($elty), Bool}, + A::AbstractVecOrMat{$elty}, B::AbstractVecOrMat{$elty}, + beta::Union{($elty), Bool}, + C::AbstractVecOrMat{$elty}) + chkuplo(uplo) + require_one_based_indexing(A, B, C) + m = size(A, transA == 'N' ? 1 : 2) + ka = size(A, transA == 'N' ? 2 : 1) + kb = size(B, transB == 'N' ? 1 : 2) + n = size(B, transB == 'N' ? 2 : 1) + if ka != kb || m != n || m != size(C,1) || n != size(C,2) + throw(DimensionMismatch(lazy"A has size ($m,$ka), B has size ($kb,$n), C has size $(size(C))")) + end + chkstride1(A) + chkstride1(B) + chkstride1(C) + ccall((@blasfunc($gemmt), libblastrampoline), Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Clong, Clong, Clong), + uplo, transA, transB, n, + ka, alpha, A, max(1,stride(A,2)), + B, max(1,stride(B,2)), beta, C, + max(1,stride(C,2)), 1, 1, 1) + C + end + function gemmt(uplo::AbstractChar, transA::AbstractChar, transB::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) + gemmt!(uplo, transA, transB, alpha, A, B, zero($elty), similar(B, $elty, (size(A, transA == 'N' ? 1 : 2), size(B, transB == 'N' ? 2 : 1)))) + end + function gemmt(uplo::AbstractChar, transA::AbstractChar, transB::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) + gemmt(uplo, transA, transB, one($elty), A, B) + end + end +end + +""" + gemmt(uplo, tA, tB, alpha, A, B) + +Return the lower or upper triangular part specified by [`uplo`](@ref stdlib-blas-uplo) of `A*B` or the other three variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. + +!!! compat "Julia 1.11" + `gemmt` requires at least Julia 1.11. +""" +gemmt(uplo, tA, tB, alpha, A, B) + +""" + gemmt(uplo, tA, tB, A, B) + +Return the lower or upper triangular part specified by [`uplo`](@ref stdlib-blas-uplo) of `A*B` or the other three variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. + +!!! compat "Julia 1.11" + `gemmt` requires at least Julia 1.11. +""" +gemmt(uplo, tA, tB, A, B) + """ gemm!(tA, tB, alpha, A, B, beta, C) diff --git a/stdlib/LinearAlgebra/test/blas.jl b/stdlib/LinearAlgebra/test/blas.jl index 4252d9ee7938b..0b2ec14de4197 100644 --- a/stdlib/LinearAlgebra/test/blas.jl +++ b/stdlib/LinearAlgebra/test/blas.jl @@ -447,6 +447,40 @@ Random.seed!(100) end end end + @testset "gemmt" begin + for (wrapper, uplo) in ((LowerTriangular, 'L'), (UpperTriangular, 'U')) + @test wrapper(BLAS.gemmt(uplo, 'N', 'N', I4, I4)) ≈ wrapper(I4) + @test wrapper(BLAS.gemmt(uplo, 'N', 'T', I4, I4)) ≈ wrapper(I4) + @test wrapper(BLAS.gemmt(uplo, 'T', 'N', I4, I4)) ≈ wrapper(I4) + @test wrapper(BLAS.gemmt(uplo, 'T', 'T', I4, I4)) ≈ wrapper(I4) + @test wrapper(BLAS.gemmt(uplo, 'N', 'N', el2, I4, I4)) ≈ wrapper(el2 * I4) + @test wrapper(BLAS.gemmt(uplo, 'N', 'T', el2, I4, I4)) ≈ wrapper(el2 * I4) + @test wrapper(BLAS.gemmt(uplo, 'T', 'N', el2, I4, I4)) ≈ wrapper(el2 * I4) + @test wrapper(BLAS.gemmt(uplo, 'T', 'T', el2, I4, I4)) ≈ wrapper(el2 * I4) + I4cp = copy(I4) + @test wrapper(BLAS.gemmt!(uplo, 'N', 'N', one(elty), I4, I4, elm1, I4cp)) ≈ wrapper(Z4) + @test I4cp ≈ Z4 + I4cp[:] = I4 + @test wrapper(BLAS.gemmt!(uplo, 'N', 'T', one(elty), I4, I4, elm1, I4cp)) ≈ wrapper(Z4) + @test I4cp ≈ Z4 + I4cp[:] = I4 + @test wrapper(BLAS.gemmt!(uplo, 'T', 'N', one(elty), I4, I4, elm1, I4cp)) ≈ wrapper(Z4) + @test I4cp ≈ Z4 + I4cp[:] = I4 + @test wrapper(BLAS.gemmt!(uplo, 'T', 'T', one(elty), I4, I4, elm1, I4cp)) ≈ wrapper(Z4) + @test I4cp ≈ Z4 + M1 = uplo == 'U' ? U4 : I4 + @test wrapper(BLAS.gemmt(uplo, 'N', 'N', I4, U4)) ≈ wrapper(M1) + M2 = uplo == 'U' ? I4 : U4' + @test wrapper(BLAS.gemmt(uplo, 'N', 'T', I4, U4)) ≈ wrapper(M2) + @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'N', 'N', one(elty), I43, I4, elm1, I43) + @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'N', 'N', one(elty), I4, I4, elm1, Matrix{elty}(I, 5, 5)) + @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'N', 'N', one(elty), I43, I4, elm1, I4) + @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'T', 'N', one(elty), I4, I43, elm1, I43) + @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'N', 'T', one(elty), I43, I43, elm1, I43) + @test_throws DimensionMismatch BLAS.gemmt!(uplo, 'T', 'T', one(elty), I43, I43, elm1, Matrix{elty}(I, 3, 4)) + end + end @testset "gemm" begin @test all(BLAS.gemm('N', 'N', I4, I4) .== I4) @test all(BLAS.gemm('N', 'T', I4, I4) .== I4) @@ -455,7 +489,7 @@ Random.seed!(100) @test all(BLAS.gemm('N', 'N', el2, I4, I4) .== el2 * I4) @test all(BLAS.gemm('N', 'T', el2, I4, I4) .== el2 * I4) @test all(BLAS.gemm('T', 'N', el2, I4, I4) .== el2 * I4) - @test all(LinearAlgebra.BLAS.gemm('T', 'T', el2, I4, I4) .== el2 * I4) + @test all(BLAS.gemm('T', 'T', el2, I4, I4) .== el2 * I4) I4cp = copy(I4) @test all(BLAS.gemm!('N', 'N', one(elty), I4, I4, elm1, I4cp) .== Z4) @test all(I4cp .== Z4) diff --git a/typos.toml b/typos.toml new file mode 100644 index 0000000000000..b9a9311946bc4 --- /dev/null +++ b/typos.toml @@ -0,0 +1,2 @@ +[default] +extend-ignore-words-re = ["^[a-zA-Z]?[a-zA-Z]?[a-zA-Z]?[a-zA-Z]?$"]