# \[ANN\] FlexUnits.jl 0.5 Unit Simplification and Log-Units

**URL:** https://discourse.julialang.org/t/ann-flexunits-jl-0-5-unit-simplification-and-log-units/137045
**Category:** Package Announcements
**Created:** [May 8, 2026, 5:05pm UTC](https://discourse.julialang.org/t/ann-flexunits-jl-0-5-unit-simplification-and-log-units/137045 "2026-05-08T17:05:08Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![Deduction42](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/deduction42/32/9206_2.png) [@Deduction42](https://discourse.julialang.org/u/Deduction42)
#### Post date: [May 8, 2026, 5:05pm UTC](https://discourse.julialang.org/t/ann-flexunits-jl-0-5-unit-simplification-and-log-units/137045/1 "2026-05-08T17:05:08Z")

</div>

# [FlexUnits.jl](https://github.com/Deduction42/FlexUnits.jl)

[![](https://global.discourse-cdn.com/julialang/original/3X/d/2/d24952c9535cffbd2ad05c83f74f5f375df18d50.svg)](https://deduction42.github.io/FlexUnits.jl/stable)

[![](https://global.discourse-cdn.com/julialang/original/3X/5/8/5876609cd95008f1e14ad211bd58c6898bbc45d6.svg)](https://deduction42.github.io/FlexUnits.jl/dev)

FlexUnits v0.5.0 is a major new release, aimed at supporting two features:

1. Unit simplification

2. Logarithmic units

# Unit Simplification

### What Unitful does

Unitful.jl propagates units directly. When performing a calculation, it records all the unit symbols and objects and produces a new unit type with all the symbols concatenated. This result is useful for small operations:

```julia

using Unitful

julia> r = 5u"V"/2u"A" #Ohms is better but this will do

2.5 V A^-1

```

However, for longer operations, the results can be unsightly and difficult to reason about, this is why `upreferred` exists, which gives a dimensional view

```julia

julia> p = (1.0u"kg/L" * 9.81u"m/s^2" * 25u"cm") #This is supposed to be a pressure

245.25 kg cm m L^-1 s^-2

julia> upreferred(p) #This is a bit better

2452.5 kg m^-1 s^-2

```

### What FlexUnits does by default

FlexUnits.jl and DynamicQuantities.jl, by default will give you a dimensional view of the result (i.e. `upreferred`). This gives better results for long operations:

```julia

using FlexUnits, .UnitRegistry

julia> p = (1.0u"kg/L" * 9.81u"m/s^2" * 25u"cm")

2452.5 kg/(m s²)

```

But the results are a lot harder to understand for some units (electrical units are particularly challenging)

```julia

julia> r = 5u"V"/2u"A" #Electrical units are opaque

2.5 (m² kg)/(s³ A²)

```

### FlexUnits `simplify`

FlexUnits now has a `simplify` function that performs a greedy search algorithm to minimize the number of symbols displayed. This approach is more likely yield understandable results than both alternatives (unit-propagation and dimensional-view).

```julia

julia> p = (1.0u"kg/L" * 9.81u"m/s^2" * 25u"cm") |> simplify #This is exactly what I wanted

2452.5 Pa

julia r = 5u"V"/2u"A" |> simplify #And so is this

2.5 Ω

```

Moreover, if you don’t like typing `|> simplify` all the time, you can set the `display_simplified_units` setting

```julia

display_simplified_units(true) #Show simplified results

julia> p = (1.0u"kg/L" * 9.81u"m/s^2" * 25u"cm")

2452.5 Pa

julia> r = 5u"V"/2u"A"

2.5 Ω

```

You can also change the units that the simplification algorithm uses in its search process. You can only have one unit per dimension, so if your unit clashes with an existing unit in that dimension, your unit will replace it.

```julia

set_preferred_unit(u"psi") #Simplified pressures are now in PSI

julia> p = (1.0u"kg/L" * 9.81u"m/s^2" * 25u"cm")

0.35570491213617295 psi

```

### Gotchas with `display_simplified_units(true)`

Simplified units are not displayed by default mainly because _ **WARNING** _, `display_simplified_units(true)` _does not actually simplify the result_. It only displays the result you would get if you did simplify it. Setting simplified units by default could give you unexpected answers for `ustrip` if your preferred units are not SI.

```julia

#Recall p was shown in PSI, but it's actually still in kg/(m s²)

julia ustrip(p)

2452.5

```

Setting `display_simplified_units(true)` can also produce errors if you’re displaying quantities and units that don’t subscribe to the default `Dimensions` object (or its static derivatives).

### FlexUnits `simplify` performance

The greedy algorithm used by `simplify` does require significant resources to run, and is easily 10x more expensive than Unitful’s unit tracking. However, unlike Unitful’s tracking, `simplify` does not run automatically during calculations (neither at compile nor run time). Even when `display_simplified_units(true)` is called, the `simplify` algorithm only runs when displaying results; thus the performance hit isn’t very noticeable as `display` shouldn’t be called in tight loops and already has significant overhead anyway.

# Logarithmic Units, the Simple Way

Handling logarithmic units such as decibels comes with some level of controversy. The question is whether a quantity such as `1 dB(W)` is a logarithm of a power quantity or merely the _logarithmic representation_ of a power quantity.

1. If `1 dB(W)` is a logarithmic representation of a power quantity: `1 dB(W) + 1 dB(W) = 4.0103 dB(W)`

2. If `1 dB(W)` is a an actual logarithm of a power quantity: `1 dB(W) + 1 dB = 2 dB(W²)`

FlexUnits _consistently adopts the philosophy of the second camp_, where a decibel represents the _logarithm of a quantity_ and supplies algebraic tools to manipulate logarithms of quantities (referred to as a `LogQuant`). Taking this consistent approach makes logarithmic units simpler in FlexUnits than Unitful, as the latter can take either philosophy depending on whether your decibel level is unitless or has units.

If you truly believe that `1 dB(W) + 1 dB(W) = 4.0103 dB(W)`, fear not! FlexUnits has you covered with the [semiring](https://en.wikipedia.org/wiki/Log_semiring) notational convention with \oplus and \ominus symbols that apply the following operations

1. x ⊕ y = log(exp(x) + exp(y))

2. x ⊖ y = log(exp(x) - exp(y))

Since `exp` of a logarithmic quantity is a linear quantity, ⊕ ⊖ simply apply addition/subtraction to the linear quantities and convert the result back to log-space.

### Producing logarithmic quantities

One way to produce a `LogQuant` is by taking a log of a quantity.

```julia

julia> q = log(2u"W")

log(2.0 (m² kg)/s³)

```

Another way is to multiply a number by a logarithmic unit. For example, `dB` is a `LogScale` object that can be imported to construct a logarithmic unit

```julia

import FlexUnits.dB

julia> q = 30dB(u"W")

log(1000.0000000000016 (m² kg)/s³)

```

As you can see here, `30 dB(W)` is equivalent to `1000 W` but it displayed as its logarithm. This helps reinforce how operations are performed based on logarithmic identities. While their logarithms are displayed (to emphasize this algebra), the actual numerical value stored is the logarithmic form

```julia

julia> ustrip(log(2u"W"))

0.6931471805599453

```

### Operations on logarithmic quantities

The algebraic rules `LogQuant` are centered around logarithmic identities.

```julia

julia> log(4u"m") + log(4u"s") # log(x) + log(y) = log(x*y)

log(15.999999999999998 (m s))

julia> log(4u"m") - log(4u"s") # log(x) - log(y) = log(x/y)

log(1.0 m/s)

julia> 2log(4u"m") # nlog(x) = log(x^n)

log(15.999999999999998 m²)

```

We also make use of the ⊕ and ⊖ operators that, in this context, commonly refers to adding/subtracting linearized values and transforming back to log space. It’s not exported by default as this symbol could be used by other packages to mean something else.

```julia

import FlexUnits: ⊕, ⊖

julia> log(8u"m") ⊕ log(4u"m") #Observe that the linear addition happened

log(12.0 m)

julia> log(8u"m") ⊖ log(4u"m") #Observe that linear subtraction happened

log(3.9999999999999982 m)

```

Logarithmic quantities can be converted back to regular quantities using `quantity`, `linquant`, `ubase` or `exp`

```julia

julia> (linquant(log(4u"m")), quantity(log(4u"m")), exp(log(4u"m")), ubase(log(4u"m")))

(4.0 m, 4.0 m, 4.0 m, 4.0 m)

```

### Simplification

Simplification on logarithmic units produces decibels by default

```julia

display_simplified_units(true)

julia> log(1u"W") + log(1u"W")

0.0 dB(W²)

julia> 1dB(u"W") + 1dB(u"W")

2.0 dB(W²)

```

You can change this setting to Nepers if you prefer

```julia

import FlexUnits.Np

set_preferred_logscale(Np)

julia> 1dB(u"W") + 1dB(u"W")

0.4605170185988092 Np(W²)

julia> 1Np(u"W") + 1Np(u"W")

2.0 Np(W²)

```

### Registering logarithmic units

The default unit registry can only register affine units, which is also usually sufficient for working logarithmic units _unless you need to parse strings to produce logarithmic units_. In such cases, you will need to register logarithmic units with the `LogUnitRegistry` instead. This registry can hold both affine and logarithmic units, but `uparse` can introduce performance issues because the output is a `Union`. _ **WARNING, because multiplying `uparse` outputs can produce a Quantity or a LogQuant, based on the string value, it’s recommended that you use explicit constructors like `quantity` or `ubase` to always produce linear quantities, or `logquant` or `logubase` always produce logarithmic units.** _

```julia

using FlexUnits, .LogUnitRegistry

import FlexUnits.dB

register_unit("dB_V" => dB(u"V"))

julia> 10uparse("dB_V")

log(10.000000000000002 (m² kg)/(s³ A))

julia> 10uparse("V")

10.0 (m² kg)/(s³ A)

julia> ubase(10, uparse("dB_V"))

10.000000000000002 (m² kg)/(s³ A)

julia> logubase(10, uparse("V"))

log(10.000000000000002 (m² kg)/(s³ A))

julia> 10u"dB_V"

log(10.000000000000002 (m² kg)/(s³ A))

```

---

<div class="post-metadata">

### Author: ![Deduction42](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/deduction42/32/9206_2.png) [@Deduction42](https://discourse.julialang.org/u/Deduction42)
#### Post date: [May 12, 2026, 2:32pm UTC](https://discourse.julialang.org/t/ann-flexunits-jl-0-5-unit-simplification-and-log-units/137045/2 "2026-05-12T14:32:21Z")

</div>

While this hasn’t been part of any announcement, dispatch pattern tools such as the `@D_str` macro make it easier to define static dimensions of units for automatic validation and speed increases where these dimensions are known beforehand. For example, consider a “sensor” object:

```julia
mutable struct Sensor{T<:Real, D<:AbstractDimLike} <: AbstractSensor{T,D}
    value :: Quantity{T, D}
    tolerance :: Quantity{T, D}
    units :: Units{D, AffineTransform{Float64}}
end

```

You can make a generic sensor by specifying the `D` parameter to be a `Dimensions{FixRat32}`; this can hold quantities of any dimension while still being concrete (so containers with this object don’t have dynamic dispatch).

```julia-auto
julia> const GenericSensor = Sensor{Float64, Dimensions{FixRat32}}
GenericSensor (alias for Sensor{Float64, Dimensions{FixRat32}})

julia> isconcretetype(GenericSensor)
true

```

This object doesn’t validate units, and it possible to construct inconsistent objects where units, value and tolerance all have different dimensions. Using the same pattern though, we could create a sensor object with static dimensions. This enforces unit consistency with the dimensions specified by `D"Pa"`

```julia-auto
julia> D"Pa" #gives a pressure dimension type
StaticDims{kg/(m s²)}

julia> D"psi" #gives the same result
StaticDims{kg/(m s²)}

julia> const PressureSensor = Sensor{Float64, D"psi"}
PressureSensor (alias for Sensor{Float64, StaticDims{kg/(m s²)}})

julia> ps = PressureSensor(5u"kg", 3u"Pa", u"Pa")
ERROR: ConversionError: Cannot convert unit 'kg' to target unit 'kg/(m s²)' due to a dimension mismatch of '(m s²)'

```

A more complicated example of this is found in the documentation [here](https://deduction42.github.io/FlexUnits.jl/stable/dispatch/), and a similar pattern is used for solving differential equations with units [here](https://deduction42.github.io/FlexUnits.jl/stable/examples/#Restricting-Units-to-Equations).
