SymbolicML / DynamicQuantities.jl

Efficient and type-stable physical quantities in Julia
https://symbolicml.org/DynamicQuantities.jl/dev/
Apache License 2.0
120 stars 15 forks source link

Pretty print element types of `QuantityArray` #131

Open BambOoxX opened 2 months ago

BambOoxX commented 2 months ago

So far QuantityArray shows this kind of output

julia> ar = QuantityArray(rand(3), u"m/s") 3-element QuantityArray(::Vector{Float64}, ::Quantity{Float64, Dimensions{DynamicQuantities.FixedRational{Int32, 25200}}}): 0.2729202669351497 m s⁻¹ 0.992546340360901 m s⁻¹ 0.16863543422972482 m s⁻¹

So obviously it is possible to print the unit string from the dimension. As far as I understand the design of QuantityArrays it is not possible to concatenate two arrays of different dimensions, meaning that the unit is shared among all array values. With that in mind, does it make sense to print the unit of each element of the array given that it is shared by all elements, and takes quite a bit of space for matrices such as

julia> A = hcat(QuantityArray(randn(32,5) . 1u"km/s"),QuantityArray(rand(32,2) . 1u"m/s") ) 32×7 QuantityArray(::Matrix{Float64}, ::Quantity{Float64, Dimensions{DynamicQuantities.FixedRational{Int32, 25200}}}): 386.672 m s⁻¹ -81.0539 m s⁻¹ 424.889 m s⁻¹ -39.8638 m s⁻¹ -504.45 m s⁻¹ 0.0555176 m s⁻¹ 0.245589 m s⁻¹ -381.83 m s⁻¹ -2759.87 m s⁻¹ 647.776 m s⁻¹ -298.789 m s⁻¹ -1403.23 m s⁻¹ 0.2663 m s⁻¹ 0.198563 m s⁻¹ 940.253 m s⁻¹ -600.701 m s⁻¹ -1039.45 m s⁻¹ -1567.83 m s⁻¹ -9.59547 m s⁻¹ 0.660183 m s⁻¹ 0.459871 m s⁻¹ 344.404 m s⁻¹ 241.106 m s⁻¹ 150.618 m s⁻¹ -1498.66 m s⁻¹ -1043.98 m s⁻¹ 0.975485 m s⁻¹ 0.117091 m s⁻¹ -2350.84 m s⁻¹ -1049.62 m s⁻¹ 517.911 m s⁻¹ 727.556 m s⁻¹ -1440.38 m s⁻¹ 0.762756 m s⁻¹ 0.0765941 m s⁻¹ 1444.87 m s⁻¹ -264.519 m s⁻¹ -1044.43 m s⁻¹ 608.438 m s⁻¹ -1093.79 m s⁻¹ 0.484282 m s⁻¹ 0.274533 m s⁻¹ 778.131 m s⁻¹ 625.476 m s⁻¹ 331.268 m s⁻¹ -438.879 m s⁻¹ 393.874 m s⁻¹ 0.305781 m s⁻¹ 0.165897 m s⁻¹ 269.81 m s⁻¹ 436.029 m s⁻¹ -2178.13 m s⁻¹ -972.365 m s⁻¹ 34.1768 m s⁻¹ 0.370966 m s⁻¹ 0.0336445 m s⁻¹ -454.505 m s⁻¹ -1477.05 m s⁻¹ 1048.72 m s⁻¹ 842.635 m s⁻¹ -970.589 m s⁻¹ 0.142977 m s⁻¹ 0.459108 m s⁻¹ 580.788 m s⁻¹ -2201.46 m s⁻¹ -1862.64 m s⁻¹ -885.331 m s⁻¹ -658.453 m s⁻¹ 0.357363 m s⁻¹ 0.282407 m s⁻¹ -1192.77 m s⁻¹ 1020.91 m s⁻¹ 334.605 m s⁻¹ -892.718 m s⁻¹ 213.223 m s⁻¹ 0.805413 m s⁻¹ 0.439469 m s⁻¹ 307.255 m s⁻¹ -1208.88 m s⁻¹ -1137.83 m s⁻¹ 787.486 m s⁻¹ 1893.14 m s⁻¹ 0.249205 m s⁻¹ 0.393297 m s⁻¹ 2256.44 m s⁻¹ -380.859 m s⁻¹ -601.359 m s⁻¹ 1517.48 m s⁻¹ -1265.31 m s⁻¹ 0.388709 m s⁻¹ 0.303767 m s⁻¹ -282.792 m s⁻¹ 10.4678 m s⁻¹ 388.6 m s⁻¹ 520.606 m s⁻¹ 1000.6 m s⁻¹ 0.747662 m s⁻¹ 0.565991 m s⁻¹ -42.5849 m s⁻¹ 2128.23 m s⁻¹ 3.97641 m s⁻¹ -740.586 m s⁻¹ -721.475 m s⁻¹ 0.964792 m s⁻¹ 0.740287 m s⁻¹

Wouldn't it make more sense to show the unit in the type with something like

32×7 QuantityArray(::Matrix{Float64}, ::Quantity{Float64, Dimensions{m s-1)):

MilesCranmer commented 2 months ago

Just for reference the type print of

QuantityArray(::Matrix{Float64}, ::Quantity{Float64, Dimensions{DynamicQuantities.FixedRational{Int32, 25200}}})

is the signature of the recommended constructor for QuantityArrays:

julia> A = QuantityArray(randn(32, 5), u"km/s");

This display choice is similar to how array views are displayed:

julia> @view x[1:5, 1:3]
5×3 view(::Matrix{Float64}, 1:5, 1:3) with eltype Float64:
...

rather than the full type signature which has some redundancies:

julia> print(typeof(@view x[1:5, 1:3]))
SubArray{Float64, 2, Matrix{Float64}, Tuple{UnitRange{Int64}, UnitRange{Int64}}, false}

Likewise the full type signature of QuantityArray is also pretty lengthy

julia> print(typeof(QuantityArray(randn(30, 50), u"m/s")))
QuantityArray{Float64, 2, Dimensions{DynamicQuantities.FixedRational{Int32, 25200}}, Quantity{Float64, Dimensions{DynamicQuantities.FixedRational{Int32, 25200}}}, Matrix{Float64}}

Anyways that's the reason for the way the type is displayed.

Now regarding the elements, I am wondering if it displays incorrectly for you? If in the Julia REPL it should be able to hide most of the elements so it doesn't wrap around. For me I think it looks okay:

Screenshot 2024-04-11 at 00 51 45

This is the same as if you had a normal Array of quantities:

Screenshot 2024-04-11 at 00 53 01

but without needing to actually store the individual units of everything.


As far as I understand the design of QuantityArrays it is not possible to concatenate two arrays of different dimensions, meaning that the unit is shared among all array values.

Right, QuantityArray is specifically for a single unit shared amongst all array elements, which should give you a speedup because the compiler knows it can avoid recalculating units for each element (without resorting to doing unit propagation in type space).

(Aside: the compiler might be able to figure this out for normal arrays IF the array of quantities was constructed in-scope. If you pass an array of quantities, the compiler has no idea whether the units are the same or not – hence the QuantityArray to "tell the compiler" this.)

However you can always cast to an array of quantities, with

julia> A = Array(quantity_array);

Then convert back with QuantityArray(A) – which will check that the units are the same, and roll it into a single stored unit.

BambOoxX commented 2 months ago

So the rendering is the same on my side, no troubles there. My question is really about not repeating redundant information during printing, which is what happens at the moment. I know this is of quite little importance, since that could save just a column or two for simple units, but if you work wink symbolic units or lengthy one the unit quickly takes most of the space.