{"id":16705102,"url":"https://github.com/mcabbott/namedplus.jl","last_synced_at":"2026-03-18T21:34:35.138Z","repository":{"id":56427877,"uuid":"204375329","full_name":"mcabbott/NamedPlus.jl","owner":"mcabbott","description":"🏴‍☠️","archived":false,"fork":false,"pushed_at":"2020-11-08T18:31:01.000Z","size":206,"stargazers_count":10,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-13T19:28:30.394Z","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":null,"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":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-26T01:51:40.000Z","updated_at":"2023-04-10T17:44:18.000Z","dependencies_parsed_at":"2022-08-15T18:31:09.169Z","dependency_job_id":null,"html_url":"https://github.com/mcabbott/NamedPlus.jl","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcabbott%2FNamedPlus.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcabbott%2FNamedPlus.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcabbott%2FNamedPlus.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcabbott%2FNamedPlus.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mcabbott","download_url":"https://codeload.github.com/mcabbott/NamedPlus.jl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243561237,"owners_count":20311062,"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:40.301Z","updated_at":"2025-12-28T18:22:48.375Z","avatar_url":"https://github.com/mcabbott.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NamedPlus.jl\n\n[![Github CI](https://img.shields.io/github/workflow/status/mcabbott/NamedPlus.jl/CI?logo=github)](https://github.com/mcabbott/NamedPlus.jl/actions)\n[![Tag Version](https://img.shields.io/github/v/tag/mcabbott/NamedPlus.jl?color=orange\u0026logo=github)](https://github.com/mcabbott/NamedPlus.jl/releases)\n[![Docstrings](https://img.shields.io/badge/julia-docstrings-blue.svg)](https://juliahub.com/docs/NamedPlus/)\n\nThis package exists to experiment with the arrays provided by \n[NamedDims.jl](https://github.com/invenia/NamedDims.jl). \nWhile that package is fairly minimal (and focused on providing a type with great performance), \nthis one defines lots of useful functions. Some of them are only defined when other packages \nthey need are loaded. Here's what works in `v0.0.1`:\n\nSome convenient ways add names (exports `named`, `@named`, `nameless`):\n```julia\n@pirate Base\nm = rand(Int8; i=3, j=4)                 # names from keywords, needs rand(Type, i=...)\nm .+ ones(_=1, j=4, k=2)                 # ones(), zeros(), and fill() all work.\n\nm .- named(parent(m), :i, :j)            # adds names, or refines existing ones, \na_z = named(rand(4,1,1,2), :a, .., :z)   # use .. (from EllipsisNotation) to skip some.\n\n@named g = [n^i for n in 1:20, i in 1:3] # read names (:n,:i) from generator's variables\n\nrename(m, :i =\u003e :z')                     # renames just :i, to :z' == :z′\nnameless(m, (:j, :i)) === transpose(m)   # also @named mt = m{j,i} \n```\n\nSome functions controlled by them:\n```julia\nt = split(g, :n =\u003e (j=4, k=5))           # just reshape, new size (4,5,3),\njoin(t, (:i, :k) =\u003e :χ)                  # copy if non-adjacent, size (4,15).\n\ndropdims(a_z)                            # defaults to :_, and kills all of them\ntranspose(a_z, :a, :z)                   # permutes (4,2,3,1)\n```\n\nA hack to make lots of code propagate names (`NamedInt`):\n```julia\nd,k = size(m); @show d                   # NamedInt, which exists for:\nz = zeros(d,d')                          # ones, fill, rand, etc\nz .= [sqrt(i) for i in 1:d, i′ in 1:d']  # comprehensions propagate names from (1:d)\nreshape(g, k,:,d) .+ g[end, d]           # reshape propagate via sizes\n\nusing Einsum, TensorCast                 # These packages dont't know about names at all,\n@einsum mz[i,k] := m[i,j] * z[i,k]       # works because of Array{}(undef, NamedInt...)\n@cast tm[i⊗j,k] := t[j,k,i] + m[i,j]     # works because of reshape(A, NamedInt)\n```\n\nSome automatic re-ordering of dimensions (`align`, `align_sum!`, `align_prod!`):\n```julia\nalign(m, (:j, :k, :i))                   # lazy generalised permutedims, (:j, :_, :i)\n@named q{i,j,k} = m .+ t                 # used for auto-permuted broadcasting\nalign(m, t) .+ t                         # or to manually fix things up\n\nalign_sum!(Int.(m), t)                   # reduce (:j, :k, :i) into (:i, :j)\n```\n\nIncluding for matrix multiplication (`mul`, `*ᵃ`, `contract`, `batchmul`):\n```julia\nm *ᵃ z == mul(m, z, :i) == m' * z        # matrix multiplication on shared index,\ng *ᵃ m == (m *ᵃ g)'                      # typed *\\^a tab.\n\nusing TensorOperations\ncontract(m, t)                           # shared indices i \u0026 j, leaving only k\nm ⊙ᵃ t == t ⊙ᵃ m                         # infix version, \\odot\\^a tab\n@named @tensor p[j,i′] := m[i,j] * z[i,i′] # named inputs re-arranged via Strided\n\nusing OMEinsum\ncontract(m, t, z)                        # sum over shared :i, leaving (:j, :k, :i′)\nconst *ᵇ = batchmul(:k)                  # batch index :k,\nt *ᵇ rename(t, :i =\u003e :i')                # sum over shared :j, leaving (:i, :i′, :k)\n\nusing Zygote                             \ngradient(m -\u003e sum(contract(m,t)[1]), m)[1] # contract defines a gradient\ngradient(t -\u003e sum(t *ᵇ q), t)[1]         # OMEinsum defines this gradient\n```\n\nSome other bits have moved to [AxisKeys.jl](https://github.com/mcabbott/AxisKeys.jl).\nIf both packages are loaded:\n\n```julia\nusing NamedPlus, AxisKeys, Plots\n@named [n^i for n in 1:2:40, i in 2:4]   # has custom ranges\nscatter(ans, yaxis=:log10)               # labels axes \u0026 series\n```\n\nWhile the functions in [NamedDims.jl](https://github.com/invenia/NamedDims.jl) try very hard \nto be zero-cost (by working hard to exploit constant propagation), this is not true here.\nIn particluar `split`, `join`, `align`, `rename` will cost around 1μs.\n(Perhaps if useful they can be made faster.) \nBut `mul` and `*ᵃ`, and aligned broadcasting via `@named`, should be nearly free, perhaps 5ns.\n\nCompared to Pytorch's [new named tensors](https://pytorch.org/docs/stable/named_tensor.html):\n\n* `refine_names` ⟶ `named`, except with `..` instead of `...`.\n* `unflatten` ⟶ `split` exactly, and `flatten` ⟶ `join`, except that for them \"All of dims must be consecutive in order\" while mine permutes if required.\n* `.align_to` and `.align_as` ⟶ `align`, mine allows the target to be either a subset or a superset (or neither) of the input. Theirs allows `...` again.\n* No support for einsum, but `torch.matmul` handles batched matrix multiplication.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcabbott%2Fnamedplus.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmcabbott%2Fnamedplus.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcabbott%2Fnamedplus.jl/lists"}