3
$\begingroup$

I want to know how can I can I tell Mathematica that all symbols that appear in an object, e.g. a matrix, obey certain constrains, without having to write these conditions by hand.

More specifically, I have a bunch of functions that perform certain operations on some input matrices, and at some point Solve or Reduce is used. These matrices are symbolic and might vary (dimensions, elements etc) each time that such a function is called. How can I impose in general that any undefined symbols that appear in the matrix are to be assumed not equal to each other and taking values between 0 or 1, without having to add the constraints by hand. Here is a sample code, where func gives the output without the constraints imposed and funcG with the constraints (but added by hand one by one).

sampleInput = {{a, 1 - a, 0}, {b, 1 - a, 0}, {0, 0, c}};

func[inp_] := Module[{v, sol},
  v = Symbol["v" <> ToString[#]] & /@ 
    Range[Dimensions[sampleInput][[1]]];
  sol = Reduce[{sampleInput.sampleInput.v == 
      Total[IdentityMatrix[3]]}, v];
  Return[sol]]

funcG[inp_] := Module[{sol, v},
  v = Symbol["v" <> ToString[#]] & /@ 
    Range[Dimensions[sampleInput][[1]]];
  sol = FullSimplify[
    Reduce[{sampleInput.sampleInput.v == 
       Total[IdentityMatrix[3]]}, v, Reals], 
    Assumptions -> {a != b, b != c, a != c, 1 > a > 0, 1 > b > 0, 1 > c > 0}];
  Return[sol]]

func[sampleInput]
funcG[sampleInput]

The output is

(a == b && -1 + b != 0 && c != 0 && v2 == (-1 + b v1)/(-1 + b) && 
   v3 == 1/c^2) || (b == 1 && a == 1 && c != 0 && v1 == 1 && 
   v3 == 1/c^2) || ((-1 + a) (a - b) != 0 && c != 0 && 
   v1 == 1/((-1 + a) (a - b)) && 
   v2 == (-1 + a^2 v1 + b v1 - a b v1)/(-1 + a) && v3 == 1/c^2)

and

(-1 + a) (a - b) v1 == 1 && b v1 + (-1 + a) (-1 + a - b) v2 == 1 && 
 v3 == 1/c^2

Since I do not know in advance how many unknown variables are in the input matrix (not even the dimension of the input matrix) and I have many functions defined that may be called, I want to have Mathematica assume that when these functions are evaluated any occurring undefined symbols in the input matrix are to be treated as unequal to each other, as well as taking values between 0 and 1.

Bonus question: when I use Reduce, even with the constraints manually imposed, the output I get is not in the form v1==...&&v2==... as you can see in the example but as a set of equations. How can I tell Mathematica to return the solution in the form v1==...&&v2==... or as substitution rules, i.e. v1->...? Thanks!

EDIT: From the comments by @xzczd the constraints that all parameters are between 0 and 1 can be implemented by using Thread[1 > DeleteDuplicates@Cases[sampleInput, _Symbol, Infinity] > 0]. The constrain that all parameters are unequal by using Unequal @@ DeleteDuplicates@Cases[sampleInput, _Symbol, Infinity] > 0]

$\endgroup$
6
  • 1
    $\begingroup$ Somewhat related: mathematica.stackexchange.com/q/249328/1871 $\endgroup$
    – xzczd
    Mar 31, 2022 at 3:14
  • $\begingroup$ @xzczd Thanks. That's helpful. From the answers there I can impose the constraints that all parameters are between 0 and 1 by using Thread[1 > DeleteDuplicates@Cases[sampleInput, _Symbol, Infinity] > 0]. Not sure how to impose the constraint that they are all unequal to each other though. Any ideas? $\endgroup$
    – AG1123
    Mar 31, 2022 at 3:37
  • 1
    $\begingroup$ Just check the Details and Options section of document of Unequal and observe the output of Unequal @@ {a, b, c}. $\endgroup$
    – xzczd
    Mar 31, 2022 at 3:40
  • $\begingroup$ @xzczd Great thanks!. That completely solves my main question. Any ideas regarding the Reduce output? $\endgroup$
    – AG1123
    Mar 31, 2022 at 3:43
  • $\begingroup$ It's because FullSimplify. One possible choice is to Reduce or Solve again as the last step. $\endgroup$
    – xzczd
    Mar 31, 2022 at 3:47

1 Answer 1

5
$\begingroup$
$Version

(* "13.0.1 for Mac OS X x86 (64-bit) (January 28, 2022)" *)

Clear["Global`*"]

sampleInput = {{a, 1 - a, 0}, {b, 1 - a, 0}, {0, 0, c}};

var = Variables[Level[sampleInput, {-1}]];
cons = Join[Thread[0 < var < 1], {Unequal @@ var}]

(* {0 < a < 1, 0 < b < 1, 0 < c < 1, a != b != c} *)

funcG[inp_] := Module[
  {sol, $v, len = Length[inp]},
  $v = Array[v, len];
  sol = Assuming[cons,
    Solve[inp . inp . $v == ConstantArray[1, len], $v, Reals] //
     FullSimplify];
  sol]

funcG[sampleInput]

(* {{v[1] -> 1/((-1 + a) (a - b)), v[2] -> a/((-1 + a)^2 (a - b)), 
  v[3] -> 1/c^2}} *)
$\endgroup$
2
  • $\begingroup$ Thank you for your answer. I will wait for a bit and then accept it. Can you comment on the difference between using DeleteDuplicates@Cases[sampleInput, _Symbol, Infinity] > 0] and Variables[Level[sampleInput, {-1}]] to get the list of variables? Also, could you explain to me how $v = Array[v, len] works? Thanks! $\endgroup$
    – AG1123
    Mar 31, 2022 at 5:47
  • 1
    $\begingroup$ The code Variables[Level[sampleInput, {-1}]] is shorter. From the documentation, "Array[f, n] generates a list of length n, with elements f[i]". If f is just a symbol then it produces a vector of the indexed variable {f[1], ..., f[n]}. An indexed variable is much easier to work with than the form {f1, f2, ..., fn} $\endgroup$
    – Bob Hanlon
    Mar 31, 2022 at 6:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.