3
$\begingroup$

I've read lots of examples here on how to set one matrix Equal to another, but how do you nest And and Or around several different equalities for values a matrix of variables can take on? Using what I've found so far, I've been able to cobble something together that works, but surely someone knows a better way?

The idea: Variables V can take on the values in either A or B or C.

Minimum Working Example:

Apply[
  And, 
  Apply[
    Or, 
    MapThread[
       Equal,
       {Transpose[
          Table[v[i], {c, 3}, {i, 10}]], 
          Table[{-1, 0, 1}, {i, 10}]},
       2], 
    {1}]]

Good output:

(v[1] == -1 || v[1] == 0 || v[1] == 1) && (v[2] == -1 || v[2] == 0 || v[2] == 1) &&
(v[3] == -1 || v[3] == 0 || v[3] == 1) && (v[4] == -1 || v[4] == 0 || v[4] == 1) &&
(v[5] == -1 || v[5] == 0 || v[5] == 1) && (v[6] == -1 || v[6] == 0 || v[6] == 1) &&
(v[7] == -1 || v[7] == 0 || v[7] == 1) && (v[8] == -1 || v[8] == 0 || v[8] == 1) &&
(v[9] == -1 || v[9] == 0 || v[9] == 1) && (v[10] == -1 || v[10] == 0 || v[10] == 1)

Ideally the answer would be something of the form

And[Or[V == {A, B, C}]]

instead of the heavy use of Table like I have, so we can see what's happening and adapt it for other problems, and often the possible values won't have such a regular pattern as these do (A, B, & C may have irregular patterns and even repetitions in order to fill out the matrix to the right shape).

Conceivably there's a way to do something like this, but I couldn't get this to work either (for use in Reduce or FindInstance):

Eqns && V ∈ {-1, 0, 1}
$\endgroup$
4
  • 1
    $\begingroup$ Would something like And @@ Map[Element[#, {-1, 0, 1}] &, Array[v, 10]] work? $\endgroup$
    – march
    Commented Mar 13, 2016 at 18:01
  • 1
    $\begingroup$ Alternatively, And @@ Map[Or @@ Thread[# == {-1, 0, 1}] &, Array[v, 10]] $\endgroup$
    – march
    Commented Mar 13, 2016 at 18:02
  • $\begingroup$ Wow you're fast. You got these comments and an answer up before I finished typing the edit at the bottom. Your first comment looks like what I tried (and added in my edit), but I didn't think to ... um ... Map Element down the list. (Because Mathematica itself gives things like {a,b} \elem Integers. It will take me a good long time to sort through what all your suggestions are doing. I pick up all kinds of programming languages in a flash, but I find Mathematica incomprehensible, and the documentation paltry. I'll have to look up and remember Array, that's much shorter. $\endgroup$ Commented Mar 13, 2016 at 18:15
  • $\begingroup$ I like the documentation and find it useful, but it often requires going through Basic Examples, Scope, and Properties & Relations. I think the documentation very much has a learn-by-example style. Anyway, Outer is particularly useful in your case, where you have two lists, and every element in one list gets "matched" to every element in the other list somehow. Thread and MapThread or complementary, but I still sometimes have to think a moment to figure out which one to use. Mapping is my go-to for basically everything, so Mapping Element came naturally. $\endgroup$
    – march
    Commented Mar 13, 2016 at 18:21

2 Answers 2

2
$\begingroup$

2D array solution for multiple target arrays for OP's ideal answer (And[Or[V == {A, B, C}]]).

This is demonstrated with Indexed as it make it easier to see the effect of the solution.

First set up the 2D arrays.

index = Table[{r, c}, {r, 2}, {c, 2}];
\[ScriptV] = Map[Indexed[v, #] &, index, {2}];
\[ScriptA] = Map[Indexed[a, #] &, index, {2}];
\[ScriptB] = Map[Indexed[b, #] &, index, {2}];
\[ScriptC] = Map[Indexed[c, #] &, index, {2}];

MatrixForm /@ {\[ScriptV], \[ScriptA], \[ScriptB], \[ScriptC]}

enter image description here

arrayContainsAny builds the inequality.

arrayContainsAny[m_, arrayList_] :=
 And @@ Flatten@MapThread[
    Or,
    First@ListCorrelate[m, #, {1, -1}, Nothing, Equal, List] & /@ arrayList,
    3]

Call it with the 2D arrays.

eqs = arrayContainsAny[\[ScriptV], {\[ScriptA], \[ScriptB], \[ScriptC]}]

enter image description here

If values are assigned to a, b, and c

SeedRandom[123];
{a, b, c} = RandomInteger[{1, 10}, {3, 2, 2}];

Then Indexed will kick in and eqs will update.

enter image description here

However, you don't need to use Indexed for the arrayList arrays parameter. You can use the arrays directly. I just used them there to demonstrate.

Hope this helps.

$\endgroup$
2
  • $\begingroup$ It's just what I'm looking for, but I really have no idea what it's doing. Especially not First Prefix ListCorrelate. I understand First, but the help file for Prefix is virtually nonexistent, and ListCorrelate just makes no sense. There is no meaning of the word kernel that makes sense here, not mathematical kernel, not software kernel, not kernel of corn, other than to think it's just the name of an argument they made up why not. I presume that if I wanted to creates something like (v1≠a1&&v1≠a2)&&..., I'd replace Equal with NotEqual and replace Or with And? $\endgroup$ Commented Mar 21, 2016 at 19:19
  • $\begingroup$ @TravisBemrose Yes, you can swap out And, Or, and Equal to make another inequality. Better yet, pass them in as parameters for additional flexibility. For ListCorrelate have a look at the basic examples in the documentation. Prefix is a basically shorthand for function square-brackets (e.g. compare Range[3] and Range@3). $\endgroup$
    – Edmund
    Commented Mar 21, 2016 at 20:32
4
$\begingroup$

Here are two options.

And @@ Map[Or @@ Thread[# == Range[-1, 1]] &, Array[v, 10]]

enter image description here

Alternatively, using Outer:

Apply[And, Or @@@ Outer[Equal, Array[v, 10], Range[-1, 1]]]

Also of possible interest, in the situation where you are using Solve, Reduce and friends:

And @@ Map[-1 <= # <= 1 && # ∈ Integers &, Array[v, 10]]

Alternatively, since Solve, Reduce and friends take a domain restriction as an optional third argument, leave out the Integers:

conds = And @@ Map[-1 <= # <= 1 &, Array[v, 10]]

and do something like

Solve[eqn && conds, Array[v, 10], Integers]
$\endgroup$
3
  • $\begingroup$ I get this error FindInstance::elemc: "Unable to resolve the domain or region membership condition v[1]\[Element]{-1,0,1}." when trying And @@ Map[# ∈ {-1, 0, 1} &, Array[v, 10]] inside FindInstance. I made sure to replace the character in case there was a copy issue. I'm sure if there's a productive way to get results down this route, but nested And / Or are working. $\endgroup$ Commented Mar 13, 2016 at 18:34
  • 1
    $\begingroup$ The thing I always forget about Element is that you can't use it with Lists. Regions and Domains have their own separate implementation (for example, 1 \[Element] {1, 2, 3} doesn't resolve but 1 \[Element] Integers evaluates to True). Therefore, I recommend using the original formulation. $\endgroup$
    – march
    Commented Mar 13, 2016 at 18:39
  • $\begingroup$ I have posted an alternative. $\endgroup$
    – march
    Commented Mar 13, 2016 at 18:44

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.