I've been playing with the new unit support in Mathematica 9. It seems very useful, but the syntax is very verbose. Instead of typing:
UnitConvert[Quantity[1, "Meters"/"Seconds"^2]*Quantity[1, "Minutes"]^2, "Kilometers"]
I would much rather type and read something like:
UnitConvert[1 m/s^2*(1 min)^2, km]
My first idea was to simply define variables for the units I'm going to use:
m = Quantity["Meters"];
km = Quantity["Kilometers"];
s = Quantity["Seconds"];
min = Quantity["Minutes"];
but unfortunately, this doesn't really work: The term 0 [any unit]
is always simplified to 0
, and following computations won't work because the units don't match. So for example UnitConvert[1 m/s^2*(1 min)^2, km]
works fine, but UnitConvert[1 m/s^2*(0 min)^2, km]
doesn't work, because the first argument to UnitConvert
evaluates to 0
Are there other ways to achieve this? For example,
- is it possible to prevent the simplification
0 * 1 Meters -> 0
- is it possible to adjust generalized input so that entering "5 s" would evaluate to
Quantity[5, "Seconds"]
, (like entering $d_x y$ evaluates toDt[y,x]
orn!
evaluates toFactorial[n]
)
Of course, I've tried the Ctrl= input form first. It's a great way to learn new syntax by example, but I don't think it's practical for day-to-day use, for a number of reasons:
I can't use notebook expressions in the freeform input. For example:
I can't use 2D input, so I can't even type e.g. $\partial _t$ or $\int _a^b$ within a freeform expression
- Which means that for a longer expression I might have to enter several freeform-input in a single line, which doesn't make it more readable.
- If I do use 2D input like $\int _a^b$, I can't enter freeform-input for a and b (EDIT: Turns out I can. I just have to enter a space before Ctrl=. Thanks @Itai Seggev)
- I've been playing with it for an hour. It hung several times and crashed once (Not reproducible) and I had to restart it once.
- This may be a bit philosophical: I'm using a programming language because I want to express an idea unambiguously. I don't want it to guess whether the symbol t means a variable for time or metric tons.
- The freeform-boxes look weird in a presentation or publication. Of course, I can convert them to input or display form easily, but (in the right context) an expression like
1920*1080 Bytes*24/s
might mean something to the reader, but2.0739999999999998*^6B*(24/1s)
doesn't, even if it's the same value.
UPDATE: Based on @Leonid's code, this is the best solution I've come up with so far:
ClearAll[withUnits];
SetAttributes[withUnits, HoldAll];
withUnits[code_] := ReleaseHold[(Hold[code] /.
{
m -> Quantity["Meters"],
s -> Quantity["Seconds"],
km -> Quantity["Kilometers"]
})
//.
{
Power[Quantity[m_, u_]^i_, j_] :> Quantity[m^(i*j), u^(i*j)],
Times[x_, Quantity[m_, u_]^(i_: 1)] :> Quantity[x*m^i, u^i]
}];
It works for the (few) examples I've tried, like
withUnits[a m/s^2 * (3s)^2] /. a -> {0, 1, 2}
but I'm not sure if the Power
/Times
replacement really covers all cases. Maybe someone can find counterexamples or improve it.
Using @Leonid's answer and this answer by rm -rf, I started a package MyUnits
that looks like this:
BeginPackage["MyUnits`"]
Unprotect/@{Quantity,Times};
Quantity/:(0|0.) Quantity[_,unit_]:=Quantity[0,unit]
Protect/@{Quantity,Times};
meter=Quantity["Meters"];
second=Quantity["Seconds"];
hertz=Quantity["Hertz"];
minute=Quantity["Minutes"];
hour=Quantity["Hours"];
byte=Quantity["Bytes"];
kilobyte=Quantity["Kilobytes"];
megabyte=Quantity["Megabytes"];
EndPackage[ ]
Using that, I get the simple input I had with the old Units
package (including command completion) and things like 0 second + 1 hour
still work.
Quantity
$\endgroup$