I was pointed to a tiny package for this written by Roman Maeder: Automatic Threading of Equations. Quoting the description:
Automatic Threading of Equations
this little utility was originally developer for DMUG,
the German Mathematica User Group (http://www.mathematica.ch/).
It answers a frequently asked question about the manipulation of
equations. Mathematica does not "thread" arithmetic and other functions
over equations, so the following naive approach to solving equations
by rearranging terms does not work:
In[1]:= 1 + x == 2
In[2]:= %-1
Out[2]= -1 + (1 + x == 2)
To make this work, the symbol Equal (the head of an equation) should
behave like List with respect to listable functions. Just as
{a,b}+1 turns into {a+1, b+1}, one may want (a==b)+1 to turn into
a+1 == b+1. This can be achieved explicitly with Thread:
In[3]:= Thread[(a==b) + 1, Equal]
Out[3]= 1 + a == 1 + b
The automatic transformation of f[a, b, c, ...] into Thread[f[a, b, c, ...]]
should happen whenever f has the attribute Listable and at least one
of the arguments a, b, ... has head Equal. This definition is essentially
what is needed:
Equal/: lhs:f_Symbol?listableQ[___, _Equal, ___] :=
Thread[ Unevaluated[lhs], Equal ]
listableQ[f_] := MemberQ[Attributes[f], Listable]
The use of "Unevaluated" prevents an infinite recursion. Together with
the necessary framework, the definition is in the little package
EqualThread.m.
Now, you can solve equation as you did in school:
read the package:
In[1]:= Needs["EqualThread`"]
the equation, to be solved for x:
In[7]:= a == b Log[2 x]
divide by b:
In[8]:= %/b
a
Out[8]= - == Log[2 x]
b
exponentiate:
In[9]:= Exp[%]
a/b
Out[9]= E == 2 x
divide by 2:
In[10]:= %/2
a/b
E
Out[10]= ---- == x
2
You can also add equations, etc.:
In[11]:= (a==b) + (c==d)
Out[11]= a + c == b + d
~ Roman M=E4der
CODE
(* :Title: make equations behave like lists *)
(* :Author: Roman E. Maeder *)
(* :Summary:
make listable functions thread over equations as they do over lists.
Allows for easy manipulation of equations.
*)
(* :Context: EqualThread` *)
(* :Package Version: 1.1 *)
(* :Copyright: Copyright 1997, Roman E. Maeder.
Permission is granted to use and distribute this file for any purpose
except for inclusion in commercial software or program collections.
This copyright notice must remain intact.
*)
(* :History:
Version 1.1 for mathgroup and MathSource, January 1998.
Version 1.0 for DMUG (German Mathematica User Group), January 1998.
*)
(* :Keywords:
Equal, equation solving, threading
*)
(* :Warning: Adds definitions to the built-in symbol System`Equal.
*)
(* :Mathematica Version:3.0 *)
BeginPackage["EqualThread`"]
(* no exports *)
Begin["`Private`"]
listableQ[f_] := MemberQ[Attributes[f], Listable]
protected = Unprotect[Equal]
Equal/: lhs:f_Symbol?listableQ[___, _Equal, ___] :=
Thread[ Unevaluated[lhs], Equal ]
Protect[Evaluate[protected]]
End[]
EndPackage[]