{"id":16705113,"url":"https://github.com/mcabbott/transmutedims.jl","last_synced_at":"2026-01-02T05:26:23.694Z","repository":{"id":45097843,"uuid":"211280917","full_name":"mcabbott/TransmuteDims.jl","owner":"mcabbott","description":"⚛️","archived":false,"fork":false,"pushed_at":"2024-04-24T19:22:47.000Z","size":149,"stargazers_count":6,"open_issues_count":5,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-24T20:50:48.455Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mcabbott.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-27T09:11:36.000Z","updated_at":"2024-04-24T19:07:39.000Z","dependencies_parsed_at":"2024-04-24T20:29:42.535Z","dependency_job_id":"d1a5ed74-4488-422c-9f77-d7b0b8c89c44","html_url":"https://github.com/mcabbott/TransmuteDims.jl","commit_stats":{"total_commits":130,"total_committers":6,"mean_commits":"21.666666666666668","dds":0.4,"last_synced_commit":"0733d66ba16881bf4540b5b637db6298e6a2fcee"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcabbott%2FTransmuteDims.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcabbott%2FTransmuteDims.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcabbott%2FTransmuteDims.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcabbott%2FTransmuteDims.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mcabbott","download_url":"https://codeload.github.com/mcabbott/TransmuteDims.jl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243561249,"owners_count":20311064,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-12T19:28:43.766Z","updated_at":"2026-01-02T05:26:23.669Z","avatar_url":"https://github.com/mcabbott.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TransmuteDims.jl\n\n[![Build Status](https://github.com/mcabbott/TransmuteDims.jl/workflows/CI/badge.svg)](https://github.com/mcabbott/TransmuteDims.jl/actions)\n[![Docstrings](https://img.shields.io/badge/docs-juliahub-blue?labelColor=333)](https://juliahub.com/docs/TransmuteDims/)\n\nThis package provides generalisations of Julia's `permutedims` function and `PermutedDimsArray` wrapper, which allow things other than permutations. These can replace `dropdims` and many uses of `reshape`.\n\nThe first generalisation is that you may introduce trivial dimensions. This can be thought of as re-positioning the implicit trivial dimensions beyond `ndims(A)`, such as the 4th and 5th dimensions here:\n\n```julia\nA = ones(10,20,30);\nntuple(d -\u003e size(A,d), 5)          # (10, 20, 30, 1, 1)\n\npermutedims(A, (2,3,1)) |\u003e size    # (20, 30, 10)\n\nusing TransmuteDims\ntransmute(A, (4,2,3,5,1)) |\u003e size  # (1, 20, 30, 1, 10)\n```\n\nHere `(4,2,3,5,1)` is a valid permutation of `1:5`, but the positions of `4,5` don't matter, so in fact this is normalised to `(0,2,3,0,1)`. Zeros indicate trivial output dimensions.\n\nSecond, input dimensions below `ndims(A)` may also be omitted, provided they are of size 1:\n\n```julia\nA2 = sum(A, dims=2); size(A2)      # (10, 1, 30)\ntransmute(A2, (3,1)) |\u003e size       # (30, 10)\n\ntry transmute(A, (3,1)) catch err; err end  # ArgumentError, \"... not allowed when size(A, 2) = 20\"\n```\n\nThird, you may also repeat numbers, to place an input dimension \"diagonally\" into several output dimensions:\n\n```julia\nusing LinearAlgebra\ntransmute(1:10, (1,1)) == Diagonal(1:10)  # true\n\ntransmute(A, (2,2,0,3,1)) |\u003e size  # (20, 20, 1, 30, 10)\n```\n\nThe function `transmute` is always lazy, but also tries to minimise the number of wrappers. Ideally to none at all, by un-wrapping and reshaping:\n\n```julia\ntransmute(A, (4,2,3,5,1)) isa TransmutedDimsArray{Float64, 5, (0,2,3,0,1), (5,2,3), \u003c:Array}\n\ntransmute(A, (1,0,2,3)) isa Array{Float64, 4}\n\ntransmute(PermutedDimsArray(A, (2,3,1)),(3,1,0,2)) isa Array{Float64, 4}\n\ntransmute(Diagonal(1:10), (3,1)) isa TransmutedDimsArray{Int64, 2, (0,1), (2,), \u003c:UnitRange}\ntransmute(Diagonal(rand(10)), (3,1)) isa Matrix\n```\n\nCalling the constructor directly `TransmutedDimsArray(A, (3,2,0,1))` simply applies the wrapper. \nThere is also a method `transmute(A, Val((3,2,0,1)))` which works out any un-wrapping at compile-time:\n\n```julia\nusing BenchmarkTools\n@btime transmute($A, (2,3,1));           #   6.996 ns (1 allocation: 16 bytes)\n@btime PermutedDimsArray($A, (2,3,1));   # 386.738 ns (4 allocations: 176 bytes)\n@btime transmute($A, Val((2,3,1)));      #   1.430 ns (0 allocations: 0 bytes)\n\n@btime transmute($A, (1,2,0,3));         #  45.642 ns (2 allocations: 128 bytes)\n@btime reshape($A, (10,20,1,30));        #  34.479 ns (1 allocation: 80 bytes)\n```\n\nFinally, there is also an eager variant, which tries always to return a `DenseArray`. \nThis will similarly un-wrap `Transpose` etc, and prefers to reshape if possible, copying data only when necessary. \nIt uses [Strided.jl](https://github.com/Jutho/Strided.jl) to speed this up, when possible, so should be faster than Base's `permutedims`:\n\n```julia\ntransmutedims(A, (3,2,0,1)) isa Array{Float64, 4}\ntransmutedims(1:3, (2,1)) isa Matrix\n\n@btime transmutedims($(rand(40,50,60)), (3,2,1));  #  57.365 μs (61 allocations: 944.62 KiB)\n@btime permutedims($(rand(40,50,60)), (3,2,1));    # 172.643 μs (2 allocations: 937.58 KiB)\n\n@strided(transmute(A, (3,2,0,1))) isa StridedView{Float64, 4}\n@strided(transmutedims(A, (3,2,0,1))) isa StridedView{Float64, 4}\n```\n\nThe `StridedView` type is general enough to allow the insertion/removal of trivial dimensions, in addition to permutations, so these functions preserve it.\n\nThe lower-case functions also treat tuples as if they were vectors:\n\n```julia\ntransmute((1,2,3), (1,)) isa AbstractVector\ntransmutedims((1,2,3), (nothing,1)) isa Matrix\n```\n\n### About\n\nThis was written largely for [TensorCast.jl](https://github.com/mcabbott/TensorCast.jl).\nThe immediate issue there was that a `reshape(transpose(::GPUArray))` may fail to trigger GPU broadcasting. \nThis package replaces that with at most one wrapper, ideally none.\nCalling `transmute` also allowed `@cast` to express what it needs more cleanly.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcabbott%2Ftransmutedims.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmcabbott%2Ftransmutedims.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcabbott%2Ftransmutedims.jl/lists"}