Correct behavior of matrix matching with separate index sets in new system

This behavior was largely correct, but was not commutative if the size of
the matrices (rows or columns) was smaller than a following match.
master
Mike Gerwitz 2021-06-16 13:01:15 -04:00
parent e90ebd226c
commit 6f2b4090cd
2 changed files with 502 additions and 5 deletions

View File

@ -27,10 +27,100 @@
<import package="../../base" />
Note that many of these classifications may match on similar values to try
to thwart potential optimizations, present or future, but these approaches
may need further adjustment to thwart future optimizations (or a way to
explicitly inhibit them).
to thwart potential optimizations,
present or future,
but these approaches
may need further adjustment to thwart future optimizations (or a way to
explicitly inhibit them).
These tests are also written a bit lazily,
given the difficulties in matching comprehensively;
that ought to be fixed in the future.
<const name="MAT3X3" desc="3x3 Matrix, Ones">
<set desc="Row 0">
<item value="1" desc="0,0" />
<item value="1" desc="0,1" />
<item value="1" desc="0,2" />
</set>
<set desc="Row 1">
<item value="1" desc="1,0" />
<item value="1" desc="1,1" />
<item value="1" desc="1,2" />
</set>
<set desc="Row 2">
<item value="1" desc="2,0" />
<item value="1" desc="2,1" />
<item value="1" desc="2,2" />
</set>
</const>
<const name="MAT3X3Z" desc="3x3 Matrix, Zeroes">
<set desc="Row 0">
<item value="0" desc="0,0" />
<item value="0" desc="0,1" />
<item value="0" desc="0,2" />
</set>
<set desc="Row 1">
<item value="0" desc="1,0" />
<item value="0" desc="1,1" />
<item value="0" desc="1,2" />
</set>
<set desc="Row 2">
<item value="0" desc="2,0" />
<item value="0" desc="2,1" />
<item value="0" desc="2,2" />
</set>
</const>
<const name="MAT3X3OOZ" desc="3x3 Matrix, Columns 1, 1, 0">
<set desc="Row 0">
<item value="1" desc="0,0" />
<item value="1" desc="0,1" />
<item value="0" desc="0,2" />
</set>
<set desc="Row 1">
<item value="1" desc="1,0" />
<item value="1" desc="1,1" />
<item value="0" desc="1,2" />
</set>
<set desc="Row 2">
<item value="1" desc="2,0" />
<item value="1" desc="2,1" />
<item value="0" desc="2,2" />
</set>
</const>
<const name="MAT3X1" desc="3x2 Matrix, Ones">
<set desc="Row 0">
<item value="1" desc="0,0" />
</set>
<set desc="Row 1">
<item value="1" desc="1,0" />
</set>
<set desc="Row 2">
<item value="1" desc="2,0" />
</set>
</const>
<const name="MAT3X1Z" desc="3x2 Matrix, Zeroes">
<set desc="Row 0">
<item value="0" desc="0,0" />
</set>
<set desc="Row 1">
<item value="0" desc="1,0" />
</set>
<set desc="Row 2">
<item value="0" desc="2,0" />
</set>
</const>
<const name="MAT1X3Z" desc="1x3 Matrix, Zeroes">
<set desc="Row 0">
<item value="0" desc="0,0" />
<item value="0" desc="0,1" />
<item value="0" desc="0,2" />
</set>
</const>
<template name="_class-tests_" desc="Classification system tests">
<param name="@system@" desc="SUT (lowercase)" />
@ -243,6 +333,391 @@
</if>
</t:describe>
</t:describe>
<t:describe name="with matrix predicates">
<t:it desc="yields TRUE for all-true element-wise conjunction">
<t:given-classify-scalar>
<match on="MAT3X3Z" value="FALSE" />
<match on="MAT3X3" value="TRUE" />
</t:given-classify-scalar>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields FALSE for some-true element-wise conjunction">
<t:given-classify-scalar>
<match on="MAT3X3Z" value="TRUE" />
<match on="MAT3X3" value="TRUE" />
</t:given-classify-scalar>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="yields TRUE for some-true element-wise disjunction">
<t:given-classify-scalar>
<any>
<match on="MAT3X3Z" value="ZERO" />
<match on="MAT3X3" value="ZERO" />
</any>
</t:given-classify-scalar>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields FALSE for all-false element-wise disjunction">
<t:given-classify-scalar>
<any>
<match on="MAT3X3Z" value="TRUE" />
<match on="MAT3X3" value="FALSE" />
</any>
</t:given-classify-scalar>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:describe name="of different column lengths">
Certain behavior is the same between the old and the new system---%
in particular,
when the match of lower length is first.
<classify as="mat-len-mismatch-first-conj-{@system@}"
yields="matLenMismatchFirstConj{@systemuc@}"
desc="Multi matrix length mismatch when first match">
<!-- fallthrough for undefined (note that this is
intentionally matching on FALSE to test against an
implicit 0 in place of undefined) -->
<match on="MAT3X1Z" value="FALSE" />
<!-- first two columns ones, last column zero -->
<match on="MAT3X3OOZ" value="TRUE" />
</classify>
<t:it desc="always yields FALSE when first match (TRUE)">
<t:given>
<c:value-of name="matLenMismatchFirstConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="always yields FALSE when first match (FALSE)">
<t:given>
<c:value-of name="matLenMismatchFirstConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<if name="@system@" eq="legacy">
<t:describe name="with legacy classification system">
The legacy system is frightenly problematic when the matrix of
lesser column length appears after the first match---%
the commutative properites of the system are lost,
and the value from the previous match falls through!
<classify as="mat-len-mismatch-conj-{@system@}"
yields="matLenMismatchConj{@systemuc@}"
desc="Multi matrix length mismatch (legacy)">
<!-- first two columns ones, last column zero -->
<match on="MAT3X3OOZ" value="TRUE" />
<!-- fallthrough for undefined (note that this is
intentionally matching on FALSE to test against an
implicit 0 in place of undefined) -->
<match on="MAT3X1Z" value="FALSE" />
</classify>
<!-- which means that it's not cummutatitve! -->
<t:it desc="causes values from previous match to fall through
into undefined (TRUE)">
<t:given>
<c:value-of name="matLenMismatchConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="causes values from previous match to fall through
into undefined (FALSE)">
<t:given>
<c:value-of name="matLenMismatchConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
</if>
<if name="@system@" eq="new">
<t:describe name="with new classification system">
<classify as="mat-len-mismatch-conj-{@system@}"
yields="matLenMismatchConj{@systemuc@}"
desc="Multi matrix length mismatch (new)">
<!-- first two columns ones, last column zero -->
<match on="MAT3X3OOZ" value="TRUE" />
<!-- must not fall through like legacy (must always be
FALSE; note that this is intentionally matching on
FALSE to test against an implicit 0 in place of
undefined) -->
<match on="MAT3X1Z" value="FALSE" />
</classify>
<!-- which means that it's not cummutatitve! -->
<t:it desc="is FALSE regardless of previous match or current
value (TRUE)">
<t:given>
<c:value-of name="matLenMismatchConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="is FALSE regardless of previous match or current
value (FALSE)">
<t:given>
<c:value-of name="matLenMismatchConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
</if>
</t:describe>
<t:describe name="of different row lengths">
<if name="@system@" eq="legacy">
<t:describe name="with legacy classification system">
The legacy classification system does something terrible when
the second match is the shorter---%
it discards the indexes entirely!
<classify as="mat-len-mismatch-rows-conj-{@system@}"
yields="matLenMismatchRowsConj{@systemuc@}"
desc="Multi matrix row mismatch (legacy)">
<!-- we _should_ have a 3x3 result matrix -->
<match on="MAT3X3OOZ" value="TRUE" />
<!-- but instead we get [[1, 1, 1], [0], [0]] because of
this match being second! -->
<match on="MAT1X3Z" value="FALSE" />
</classify>
<classify as="mat-len-mismatch-rows-first-conj-{@system@}"
yields="matLenMismatchRowsFirstConj{@systemuc@}"
desc="Multi matrix row mismatch first match (legacy)">
<match on="MAT1X3Z" value="TRUE" />
<match on="MAT3X3OOZ" value="TRUE" />
</classify>
<!-- note that this is testing buggy behavior; the new system
corrects it -->
<t:it desc="replaces all inner vectors of other rows">
<t:given>
<c:length-of>
<c:value-of name="matLenMismatchRowsConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</c:length-of>
</t:given>
<t:expect>
<!-- were it not for this bug, it should be 3 -->
<t:match-result value="#1" />
</t:expect>
</t:it>
<!-- note that this is testing buggy behavior; the new system
corrects it -->
<t:it desc="considers only defined rows' values when smaller
is first">
<t:given>
<c:value-of name="matLenMismatchRowsFirstConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#0" />
</c:index>
</c:value-of>
</t:given>
<!-- we get [[0, 0, 0], [1, 1, 0], [1, 1, 0]] -->
<!-- ^ -->
<t:expect>
<t:match-result value="#1" />
</t:expect>
</t:it>
</t:describe>
</if>
<if name="@system@" eq="new">
<t:describe name="with new classification system">
<classify as="mat-len-mismatch-rows-conj-{@system@}"
yields="matLenMismatchRowsConj{@systemuc@}"
desc="Multi matrix row mismatch (new)">
<match on="MAT3X3OOZ" value="TRUE" />
<!-- must yield FALSE rather than matching on 0 -->
<match on="MAT1X3Z" value="FALSE" />
</classify>
<classify as="mat-len-mismatch-rows-first-conj-{@system@}"
yields="matLenMismatchRowsFirstConj{@systemuc@}"
desc="Multi matrix row mismatch first match (new)">
<match on="MAT1X3Z" value="TRUE" />
<match on="MAT3X3OOZ" value="TRUE" />
</classify>
<t:it desc="retains shape of larger matrix">
<t:given>
<c:length-of>
<c:value-of name="matLenMismatchRowsConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</c:length-of>
</t:given>
<t:expect>
<!-- unlike legacy system -->
<t:match-result value="#3" />
</t:expect>
</t:it>
<t:it desc="always yields FALSE for each undefined element (TRUE)">
<t:given>
<c:length-of>
<c:value-of name="matLenMismatchRowsConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</c:length-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="always yields FALSE for each undefined element (FALSE)">
<t:given>
<c:length-of>
<c:value-of name="matLenMismatchRowsConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</c:length-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<!-- unlike legacy -->
<t:it desc="is commutative with different row lengths">
<t:given>
<c:value-of name="matLenMismatchRowsFirstConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
</if>
</t:describe>
</t:describe>
</t:describe>
</template>

View File

@ -1885,20 +1885,42 @@
function mu(ms)
{
const longest_row = Math.max.apply(null, ms.map(function(m) {
return m.length;
}));
const longest_col = Math.max.apply(null, ms.map(function(m) {
return Math.max.apply(null, m.map(function(v) { return v.length; }));
}));
const base = new Array(longest_row).fill(
new Array(longest_col).fill(1)
);
return ms.reduce(function(final, m) {
return final.map(function(v, i) {
return vu([v, m[i]||[0]]);
});
});
}, base);
}
function me(ms)
{
const longest_row = Math.max.apply(null, ms.map(function(m) {
return m.length;
}));
const longest_col = Math.max.apply(null, ms.map(function(m) {
return Math.max.apply(null, m.map(function(v) { return v.length; }));
}));
const base = new Array(longest_row).fill(
new Array(longest_col).fill(0)
);
return ms.reduce(function(final, m) {
return final.map(function(v, i) {
return ve([v, m[i]||[0]]);
});
});
}, base);
}
function vu(vs)