Message Boards Message Boards

Operations with Equal

Sometimes when we have some equations we wish to add a constant to both sides or multiply by a factor.

If you naively try:

eq1 = a == b;
eq2 = c == d;

eq1 + 1 (* Out: 1 + (a == b) *)
eq1 + eq2 (* Out: (a == b) + (c == d) *)
2*eq1 (* Out: 2*(a == b) *)

To augment the functionality of Equal, we need to change built-in functions (not a smart idea).

To do so we'll use the following lines of code:

SetEqual = (Unprotect@Equal;
(* HoldPattern is needed to avoid missinterpretation of the pattern *)
Equal /: HoldPattern@Plus[Equal[a_, b_], c : Except[Equal[__], _]] := a + c == b + c;
Equal /: HoldPattern@Plus[Equal[a_, b_], Equal[c_, d_]] := a + c == b + d;
Equal /: HoldPattern@Times[Equal[a_, b_], c : Except[Equal[__], _]] := a*c == b*c;
Equal /: HoldPattern@Power[Equal[a_, b_], c : Except[0, _]] := a^c == b^c;
HoldPattern@Equal[L : List[a_, b__], c_] := (# == c & /@ L);
Protect@Equal;)
(* Undo built-in modifications *)
ClearEqual := (Unprotect@Equal; Clear@Equal; Protect@Equal;)
(* Make Simplify not change sides *)
SimplifySides[Equal[a_, b_]] := Simplify@a == Simplify@b
SimplifySides[L_List, args___] := SimplifySides[#, args] & /@ L

With this (after calling SetEqual) we have:

eq1 + 1 (* Out: 1 + a == 1 + b *)
eq1 + eq2 (* Out: a + c == b + d *)
2*eq1 (* Out: 2*a == 2*b *)

And after we can just call ClearEqual to avoid any mishaps.

With this we can complete the square:

enter image description here

*Where the use of HoldForm is needed otherwise Simplify is not smart enough. Also, List is needed because of the Plus or Minus square roots of the equation.

POSTED BY: Thales Fernandes
18 Replies
Posted 6 years ago

I hope you noticed the new in 11.3 functions AddSides, MultiplySides, etc. These handle equations, inequalities, piecewise expressions, and more.

There are arguments for and against thread. I would say the main argument against is that the moment you get to multiplcation and division you need to incoroprate assumptions and/or conditions, and suddenly it is now a mathematical rather than structural operation. These new functions allow you to control this behavior (with carefully considered defaults, of course).

POSTED BY: Itai Seggev

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[]
POSTED BY: Vitaliy Kaurov

What happens when you call Solve[] with the modified equal? I never understood why I couldn't operate on equations in mathematica as nature intended :(

POSTED BY: Kay Herbert

I would say: Why D and Series do support Equal as an argument is more of a question, than why equal does not work with Plus.

It's not that Mathematica allows nonsense as True^2 or Cos[True], it just simple remains "unevaluated."

That's not how I would formulate it: It does allow for it and it remains unevaluated (i.e. it does not spawn an error like many other languages!).

Simplify[True^2 == True^2]

Equal just checks if the lhs and rhs are the same, nothing more. So it still does not interpret it here as a boolean or whatever.

But the best practice is, when solving manually, stop when things evaluate to True :).

I agree but if you have some automated script that takes user-input (an equation). and you want to work on it reliably it is tricky, because that would involve constantly checking if it is evaluated to True or False or unevaluated.

POSTED BY: Sander Huisman

How about this:

Format[inactiveEqual[a_, b___]] := Inactive[Equal][a, b];
inactiveEqual /: (f_)[inactiveEqual[a___]] /; 
   MemberQ[Attributes[f], Listable] := Map[f, inactiveEqual[a]];
inactiveEqual /: inactiveEqual[a___] + b : Except[_inactiveEqual] := 
  Map[# + b &, inactiveEqual[a]];
inactiveEqual /: inactiveEqual[a___]*b : Except[_inactiveEqual] := 
  Map[#*b &, inactiveEqual[a]];
inactiveEqual /: 
 inactiveEqual[a___] + inactiveEqual[b___] /; 
  Length[{a}] == Length[{b}] := Apply[inactiveEqual, {a} + {b}]

You can do manipulations with automatic threading and nice formating:

inactiveEqual[a, b]
% // Exp
% + inactiveEqual[x, y]
% + 1
2 %
1 - %
POSTED BY: Gianluca Gorni

I think they never will: you should see equations as things that are either True, False, or Unevaluated. For the unevaluated case discussed above it makes sense to add '1' to each side, or to square both sides. However, for the True and False case, this does not make any sense. What is True + 1?

One could argue that Series[False, {x,0,1}] and D[True, x] doesn't make sense since False or True are not numbers. Hence they cannot be "constant" or anything whatsoever unless you have a very loose definition to account this.

It's not that Mathematica allows nonsense as True^2 or Cos[True], it just simple remains "unevaluated."

Other nonsense examples are Simplify[True^2 == True^2] which evaluates to True. Sometimes Mathematica knows True is boolean and sometimes it just thinks it is a Symbol.

So, there are many instances where True is treated as a constant, even though it is not a number (any Symbol is a constant in the view of D).

But the best practice is, when solving manually, stop when things evaluate to True :).

POSTED BY: Thales Fernandes

If an equation becomes True after some transformation, I stop there and have no reason to add 1. Or we could operate on Inactive[Equal], which would avoid the trap.

POSTED BY: Gianluca Gorni

I don't understand yet why D does work to be honestÂ… seems, at first sight, 'off'

POSTED BY: Sander Huisman

I think they never will: you should see equations as things that are either True, False, or Unevaluated. For the unevaluated case discussed above it makes sense to add '1' to each side, or to square both sides. However for the True and False case, this does not make any sense. What is True + 1?

Currently all the operations you can do on equations are designed such that one can symbolically manipulate it (expand, simplify, et cetera) and then fill in the numbers, or the other way around, it gives the same result (the operations commute). This breaks for cases where you add '1' and fill in the numbers (or adding equations, or squaring them, or ...):

eq = a == c
Thread[eq + 1, Equal] /. {a -> 1, c -> 1}
Thread[(eq /. {a -> 1, c -> 1}) + 1, Equal]

The addition of 1 to an equation and filling in the values are two operations that don't commute.

Of course you can make exceptions; if it is True/False then it does not make sense to 'add 1' or add up two equations that are True/Faslse. The problem with that, however, is that this goes very much against the symbolic nature of the Wolfram Language where one can add pictures to strings, and numbers to graphs and whateverÂ…

What WOULD be useful if there would be some e.q. wrapper or function(ality) that facilitates these operations. But changing the current definitions would be messy and more likely break the symbolic nature of Wolfram Language.

POSTED BY: Sander Huisman

You can only do one operand at the time using the above code, so you would need nesting, e.g.:

Thread[eq + Thread[-eq2, Equal], Equal]

first 'minus' then 'add' or first multiply then addÂ…

POSTED BY: Sander Huisman

I totally agree. It would also be nice to have (a>b)+1 be automatically evaluated to a+1>b+1. And so on. I only wonder if Wolfram is afraid of breaking some old code by allowing automatic threading over equations.

POSTED BY: Gianluca Gorni

You can 'edit' equations using Thread:

There are countless ways of parsing it. Thread is a very nice one (I'll be using it sometimes)! But I really don't like those large equations.

But I really don't get it. You can differentiate an Equal equation (you can't integrate though...), you can use Series with it, and possible others functions can operate on it, but you can't add a constant to both sides...

POSTED BY: Thales Fernandes

The Thread method fails in more complicated cases:

eq = a == c + b
eq2 = g == h + k
Thread[eq - eq2, Equal]
Thread[2 eq + 1, Equal]
POSTED BY: Gianluca Gorni

You can 'edit' equations using Thread:

eq = a == c + b
eq2 = g == h + k
Thread[eq + 3, Equal]
Thread[eq*2, Equal]
Thread[eq^2, Equal]
Thread[Log[eq], Equal]
Thread[eq + eq2, Equal]

It is a bit cumbersome, but it nicely extends to 'equations' of larger size: a == b+c == d+e+f

POSTED BY: Sander Huisman

We have been waiting decades already for a natural way of transforming equations in Mathematica.

Yeup, I'm always solving equations by hand, so the trick of SetEqual and ClearEqual works, since I never call Solve while solving by hand (it would be an oxymoron).

I used to have a function wrapper loike yourself, but, it gets messy...

POSTED BY: Thales Fernandes

"Oh, behave!"

If you get the reference. ;)

POSTED BY: Thales Fernandes

We have been waiting decades already for a natural way of transforming equations in Mathematica. We can work around the problem without unprotecting Equal. Introduce this operator:

mapToSidesOfEquations[action_] := % /. eq_Equal :> Map[action, eq]

Then you can transform your equation in simple steps within a single cell:

a x^2 + b x + c == 0
(# - c)/a & // mapToSidesOfEquations
Expand // mapToSidesOfEquations
# + (b/(2 a))^2 & // mapToSidesOfEquations
Expand // mapToSidesOfEquations
% /. b -> HoldForm[b/(2 a)] 2 a
Factor // mapToSidesOfEquations
% // ReleaseHold
Simplify // mapToSidesOfEquations
% /. u_^2 == v_/z_^2 :> (u == Sqrt[v]/z) \[Or] (u == -Sqrt[v]/z)
# - b/(2 a) & // mapToSidesOfEquations
Together // mapToSidesOfEquations

It is less convenient to type.

POSTED BY: Gianluca Gorni

Unprotect@Equal --- you like to live dangerously ;-)

POSTED BY: Kapio Letto
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract