Converting solutions back to equations (inverse of ToRules)

If I solve an equation (or a system of equations), I get output like

{{a -> 5, b -> 4}, {a -> 2, b -> 3}}


I would like to convert this a statment, like

(a == 5 && b == 4) || (a == 2 && b == 3)


Currently, I'm doing this with the somewhat janky piece of code

solution // Apply[Equal, #, {2}] & // MapApply[And] // Apply[Or]


But this feels very unstable. Is there a simpler or more stable way to perform this conversion?

• What about ... /. Rule->Equal ? Commented Jul 14, 2022 at 20:01
• @UlrichNeumann that's a great way to deal with the first step, very self-explanatory. Commented Jul 14, 2022 at 20:28
• How is it unstable? Commented Jul 14, 2022 at 21:55
• I'm blindly replacing the head of all terms at a given level, it'd be very easy for something to go wrong. For instance, if I performed these operations in the reverse order: solution // Apply[Or] // MapApply[And] // Apply[Equal, #, {2}] & this works, but only if there's at least two solutions and at least two rules in each solution, otherwise it doesn't error out but produces completely incorrect output. I'm trying to rewrite it in a way where I can be more sure it isn't doing something wrong. Commented Jul 14, 2022 at 22:14
• So the form to be transformed is always a list of a list of rules, never just a list of rules? All other forms should remain unaltered? Commented Jul 14, 2022 at 22:21

First you may change the rules into equations: HoldPattern[ x1_ -> x2_] -> x1 == x2

Then you may transform the nested lists into Or and And: {{x1__}, {x2__}} :> Or[ And[x1], And[x2]]

{{a -> 5, b -> 4}, {a -> 2, b -> 3}} /.
HoldPattern[ x1_ -> x2_] -> x1 == x2  /. {{x1__}, {x2__}} :> Or[ And[x1], And[x2]]

(* (a == 5 && b == 4) || (a == 2 && b == 3) *)



Here's a modification of the OP's approach that will transform an expression only if it is a list of list of rules:

{{a -> 5, b -> 4}, {a -> 2, b -> 3}} //
Replace[{r : {__Rule} ..} :> (And @@ Equal @@@ # &) /@ Or[r]]

(*  (a == 5 && b == 4) || (a == 2 && b == 3)  *)


If we use ReplaceAll instead of Replace, then it would potentially transform a subexpression that matched the pattern.

To get something that would transform an expression consisting of a single solution {a -> 5, b -> 4} or a list of solutions, then the following modification would work:

{a -> 5, b -> 4} //
Replace[
s_List :> (Replace[s,
r : {__Rule} :> And @@ Equal @@@ r, {0, 1}] //
Replace[{sys__And} :> Or[sys]])
]

(*  a == 5 && b == 4  *)

{{a -> 5, b -> 4}, {a -> 2, b -> 3}} //
Replace[
s_List :> (Replace[s,
r : {__Rule} :> And @@ Equal @@@ r, {0, 1}] //
Replace[{sys__And} :> Or[sys]])
]

(*  (a == 5 && b == 4) || (a == 2 && b == 3)  *)


Again, we use Replace instead of ReplaceAll to make sure the expression has the correct form of a solution.

Here's another way:

{{a -> 5, b -> 4}, {a -> 2, b -> 3}} // Replace[
sol : {{__Rule} ..} :>
Apply[Or, Apply[And, Apply[Equal, sol, {2}], {1}], {0}]
]

(*  (a == 5 && b == 4) || (a == 2 && b == 3)  *)


The nested Apply is really a Fold operation, but the explicit nesting is shorter than the following Fold:

{{a -> 5, b -> 4}, {a -> 2, b -> 3}} // Replace[
sol : {{__Rule} ..} :> Fold[
Function[{s, f}, Apply[First@f, s, {Last@f}]],
sol,
Transpose[{{Equal, And, Or}, Range[2, 0, -1]}]]
]

(*  (a == 5 && b == 4) || (a == 2 && b == 3)  *)


Any of the Replace methods can be packaged as a function instead of an anonymous operator. For example:

toEquations[sol : {{__Rule} ..}] :=
Apply[Or, Apply[And, Apply[Equal, sol, {2}], {1}], {0}];

• To handle the {{}} and {} outputs from Solve[], add a rule for each: expr // Replace[{ {{}} -> True, {} -> False, {r : {__Rule} ..} :>... }] and so forth. Commented Jul 14, 2022 at 23:19

As a one-liner

Map[# /. Rule -> Equal /. List -> And &, expr] /. List -> Or
(*(a == 5 && b == 4) || (a == 2 && b == 3)*)