diff --git a/bin/3.sh b/bin/3.sh new file mode 100755 index 0000000..7af9d12 --- /dev/null +++ b/bin/3.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# ========================================================================== +# PLinOpt: C++ routines handling linear, bilinear & trilinear programs +# Authors: J-G. Dumas, B. Grenet, C. Pernet, A. Sedoglavic +# ========================================================================== +# Script: finds 3 files with starting letters L, R and P +# ========================================================================== + +basename $1 | awk -v dir=`dirname $1` '{ c=substr($1, 2); print dir"/L"c,dir"/R"c,dir"/P"c }' diff --git a/bin/FDT.sh b/bin/FDT.sh index d8fcf73..e8cb214 100755 --- a/bin/FDT.sh +++ b/bin/FDT.sh @@ -1,4 +1,12 @@ #!/bin/bash +# ========================================================================== +# PLinOpt: C++ routines handling linear, bilinear & trilinear programs +# Authors: J-G. Dumas, B. Grenet, C. Pernet, A. Sedoglavic +# ========================================================================== +# ========================================================================== +# Tests: optimizer, compacter, transpozer, factorizer, sparsifier, inplacer +# ========================================================================== + tmpfile=/tmp/fdt_plinopt.$$ numopt=10 modulus=7 diff --git a/bin/TTP.sh b/bin/TTP.sh new file mode 100755 index 0000000..d21c902 --- /dev/null +++ b/bin/TTP.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# ========================================================================== +# PLinOpt: C++ routines handling linear, bilinear & trilinear programs +# Authors: J-G. Dumas, B. Grenet, C. Pernet, A. Sedoglavic +# ========================================================================== +# Tests: trilinear in-placer +# Requires: maple from maplesoft to certify results +# ========================================================================== + +MAPLEPROG=maple +MAPLEHERE=true +if ! command -v maple &> /dev/null +then + echo "maple could not be found." + MAPLEHERE=false +else + if ! echo "1234+5678;" | maple | grep 6912 &> /dev/null + then + echo "`command -v maple` is here but not running" + MAPLEHERE=false + else + echo "maple is up and running" + fi +fi +if [ "${MAPLEHERE}" != true ] ; then + MAPLEPROG=tee > /dev/null +fi + + +tmpfile=/tmp/fdt_plinopt.$$ +numopt=100 + + +if [ "$#" -ge 1 ]; then + fics=("$@") +else + fics=(data/L*sms) +fi + +BINDRS=(./ ./bin) + +for dir in ${BINDRS[@]} +do + if [ -x ${dir}/trilplacer ]; then + echo "Found executables in ${dir}" + BINDIR=${dir} + fi +done + +TRIPL=${BINDIR}/trilplacer + +for fic in ${fics[@]} +do + if [ -e ${fic} ]; then + threefics=`./bin/3.sh ${fic}` + mdims=(`fgrep -v '#' ${threefics} | egrep '( M| R)' | cut -d':' -f2 | tr '\n' ' '`) + Lm=${mdims[0]}; let Ldm=1+${Lm} + Ln=${mdims[1]}; let Ldn=2*${Ln} + Rm=${mdims[3]}; let Rdm=1+${Rm} + Rn=${mdims[4]}; let Rdn=2*${Rn} + Pm=${mdims[6]}; let Pdm=2*${Pm} + Pn=${mdims[7]}; let Pdn=1+${Pn} + echo -n "${fic} ${Lm}x${Ln} ${Rm}x${Rn} ${Pm}x${Pn} :" + (${TRIPL} -O ${numopt} `./bin/3.sh $fic` >& /dev/stdout) | ${MAPLEPROG} | egrep '(errors :=|ADD|SCA|[0-9][^#]*AXPY)' | tr '\n' '\t'| tee -a ${tmpfile} + echo | tee -a ${tmpfile} + echo -n "${fic} ${Ldm}x${Ldn} ${Rdm}x${Rdn} ${Pdm}x${Pdn} :" + (${TRIPL} -e -O ${numopt} `./bin/3.sh $fic` >& /dev/stdout) | ${MAPLEPROG} | egrep '(errors :=|ADD|SCA|[0-9][^#]*AXPY)' | tr '\n' '\t'| tee -a ${tmpfile} + echo | tee -a ${tmpfile} + fi +done + +if [ "${MAPLEHERE}" = true ] ; then + let tries=2*${#fics[@]} + success=`grep 'errors := \[0, 0, 0\]' ${tmpfile} | wc -l` + + echo -ne "\033[1;93mSUCCESS: " + if [ ${success} -ne ${tries} ]; then + echo -ne "\033[1;91m" + fi + echo -e "${success} \033[1;93m/ ${tries}\033[0m" +fi + +\rm -rf ${tmpfile} diff --git a/include/plinopt_inplace.h b/include/plinopt_inplace.h index 780a222..de41ae0 100644 --- a/include/plinopt_inplace.h +++ b/include/plinopt_inplace.h @@ -77,8 +77,8 @@ Matrix::Row::const_iterator nextindex(const size_t preci, // =============================================================== -auto isAdd { [](const char c) { return ( (c=='+') || (c=='-') ); } }; -auto isSca { [](const char c) { return ( (c=='*') || (c=='/') ); } }; +auto isAddSub { [](const char c) { return ( (c=='+') || (c=='-') ); } }; +auto isMulDiv { [](const char c) { return ( (c=='*') || (c=='/') ); } }; // =============================================================== // Definition of ADD, SCA, MUL atomic operations @@ -119,6 +119,21 @@ std::string rmnl(const std::string& str) { return s; } +// =============================================================== +// Enables checking a matrix multiplication with Maple +#ifdef INPLACE_CHECKER + // Compare program with a matrix multiplication +void CheckMatrixMultiplication(const char L, const Matrix& A, + const char H, const Matrix& B, + const char F, const Matrix& C); + + // Compare program with direct linear applications +void CheckTriLinearProgram(const char L, const Matrix& AA, + const char H, const Matrix& BB, + const char F, const Matrix& CC, + bool expanded = false); +#endif + #include "plinopt_inplace.inl" diff --git a/include/plinopt_inplace.inl b/include/plinopt_inplace.inl index 587f947..3c2a876 100644 --- a/include/plinopt_inplace.inl +++ b/include/plinopt_inplace.inl @@ -41,7 +41,7 @@ struct Atom { // print as a program (removes noops and barriers) friend std::ostream& operator<<(std::ostream& out, const Atom& p) { - const bool bsca(isSca(p._ope)); + const bool bsca(isMulDiv(p._ope)); QRat QQ; if (bsca && QQ.isOne(p._val)) return out; @@ -87,14 +87,14 @@ struct Atom { // Operation is a no-op bool isnoop() const { - return (isAdd(this->_ope) && isZero(this->_val)) - || (isSca(this->_ope) && isOne(this->_val)); + return (isAddSub(this->_ope) && isZero(this->_val)) + || (isMulDiv(this->_ope) && isOne(this->_val)); } // Combine both operations, if compatible bool cumulate(const Atom& p) { if (this->sameops(p)) { - if (isAdd(this->_ope) && isAdd(p._ope)) { + if (isAddSub(this->_ope) && isAddSub(p._ope)) { if (this->_ope==p._ope) this->_val += p._val; else @@ -104,7 +104,7 @@ struct Atom { QRat QQ; QQ.negin(this->_val); } return true; - } else if (isSca(this->_ope) && isSca(p._ope)) { + } else if (isMulDiv(this->_ope) && isMulDiv(p._ope)) { if (this->_ope==p._ope) this->_val *= p._val; else @@ -124,7 +124,7 @@ struct Atom { // Lambda Testing whether atom is '*' or '/' with a 1 -auto isScaOne {[](const Atom& p){ return (isSca(p._ope) +auto isMulDivOne {[](const Atom& p){ return (isMulDiv(p._ope) && Givaro::isOne(p._val));} }; @@ -132,11 +132,11 @@ template Tricounter complexity(const Ring& R, const AProgram_t& p) { Tricounter nops; for(const auto& iter: p) { - if (isAdd(iter._ope)) { + if (isAddSub(iter._ope)) { ++std::get<0>(nops); if (notAbsOne(R,iter._val)) ++std::get<1>(nops); } - if (isSca(iter._ope)) ++std::get<1>(nops); + if (isMulDiv(iter._ope)) ++std::get<1>(nops); if (iter._ope == ' ') ++std::get<2>(nops); } return nops; @@ -154,7 +154,7 @@ std::ostream& dprint (std::ostream& out, const AProgram_t& p) { if (iter._ope != ' ') out << iter << std::endl; else - out << "# barrier # " << iter << std::endl; + dprint(out << "# barrier # ", iter) << std::endl; return out; } @@ -286,9 +286,9 @@ bool simplify(AProgram_t& Program, const bool transposed) { && ( (next->_ope == ' ') || - (isAdd(iter->_ope) && isSca(next->_ope)) + (isAddSub(iter->_ope) && isMulDiv(next->_ope)) || - (isSca(iter->_ope) && isAdd(next->_ope)) + (isMulDiv(iter->_ope) && isAddSub(next->_ope)) ); // STOP if: previous rhs is now modified @@ -319,36 +319,58 @@ bool simplify(AProgram_t& Program, const bool transposed) { void pushvariables(AProgram_t& Program, size_t numout) { // For each variable for(size_t i=0; i_des == (long)(i)) { - found = false; + if (found) { // (findex_src == i) and (findex_ope is Add or Mul) + bool rotate(false); + if (isAddSub(findex->_ope)) { + if (findex->_des == (long)(iter->_src)) { + // f_des is modified, cannot push + found = false; continue; + } else if (iter->_src == i) { + if (findex->_des == iter->_des) { + // i_ope must be '+' + rotate=true; + } else if (isMulDiv(iter->_ope)) { + found = false; continue; + } + } + } else { + // findex_ope is MUL + if (iter->_des == i) { + // f_des is modified, cannot push + found = false; continue; + } else { + if (iter->_src == i) { + if (isMulDiv(iter->_ope)) + rotate = true; + else { + found = false; continue; + } + } + } } - // Look for the same variabe again in this subregion - if (iter->_src == i) { - if (iter->_ope != ' ') { - // previous findex must have been after an AXPY - // Now is before another AXPY, can rotate here - auto nindex(findex); ++nindex; - if (nindex != iter) { + + if (rotate) { + auto nindex(findex); ++nindex; + if (nindex != iter) { // Has to rotate // dprint(std::clog << "######## BEF ROT\n", Program) << std::endl; -// dprint(std::clog << "### ROT " << findex->_var << i << ':', *findex) -// << " --> " << *nindex << std::endl; - std::rotate(findex, nindex, iter); +// dprint(std::clog << "### ROT " << findex->_var << i << ':', *findex); +// dprint(std::clog << " | ", *nindex); +// dprint(std::clog << " --> ", *iter) << std::endl; + std::rotate(findex, nindex, iter); // dprint(std::clog << "######## AFT ROT\n", Program) << std::endl; - } } found = false; } } else { // Try the next subregion of the Program - if ( (iter->_ope != ' ') - && (iter->_des == (long)(i)) ) { found = true; findex = iter; } + if ( (iter->_ope != ' ') && (iter->_src == i) ) { + found = true; + findex = iter; + } } } @@ -357,14 +379,14 @@ void pushvariables(AProgram_t& Program, size_t numout) { auto nindex(findex); ++nindex; if (nindex != Program.end()) { // dprint(std::clog << "######## BEF ROT\n", Program) << std::endl; -// dprint(std::clog << "### ROT " << findex->_var << i << ':', *findex) +// dprint(std::clog << "### ROT " << findex->_var << i << ':', *findex); +// dprint(std::clog << " | ", *nindex) // << " --> END" << std::endl; std::rotate(findex, nindex, Program.end()); // dprint(std::clog << "######## AFT ROT\n", Program) << std::endl; } } } - } // =============================================================== @@ -452,7 +474,7 @@ Tricounter LinearAlgorithm(AProgram_t& Program, const Matrix& A, } // Removing noops - Program.erase(std::remove_if(Program.begin(), Program.end(), isScaOne), + Program.erase(std::remove_if(Program.begin(), Program.end(), isMulDivOne), Program.end()); @@ -559,7 +581,7 @@ Tricounter TransposedDoubleAlgorithm(AProgram_t& Program, const Matrix& T, // dprint(std::clog << "Program:\n", Program) << std::endl; // Removing noops - Program.erase(std::remove_if(Program.begin(), Program.end(), isScaOne), + Program.erase(std::remove_if(Program.begin(), Program.end(), isMulDivOne), Program.end()); bool simp; do { @@ -921,7 +943,7 @@ Tricounter SearchTriLinearAlgorithm(std::ostream& out, // Setup auxilliary variables void InitializeVariable(const char L, const size_t m, const char M) { for(size_t h=0; h'; } std::clog << ">;" << std::endl; + + std::clog << "errors:=map(M->nops(select(x->x<>0,convert(M,list))),[" + << L << "Input," << H << "Input," << F << "Output]);" + << std::endl; } // Produce code for the application of a matrix to a variable @@ -996,36 +1023,47 @@ std::ostream& ApplyMatrix(std::ostream& out, if (!A.field().isOne(iter->second)) out << iter->second << '*'; out << I << '[' << (iter->first+1) << "])"; } - out << ';'; + out << ':'; } return out << std::endl; } - // Compare program with a matrix multiplication + // Compare program with direct linear applications void CheckTriLinearProgram(const char L, const Matrix& AA, const char H, const Matrix& BB, - const char F, const Matrix& CC) { + const char F, const Matrix& CC, bool expanded) { - CollectVariables(L, AA.coldim(), H, BB.coldim(), F, CC.rowdim()); + std::clog <<"# code-checking for " + << AA.coldim() << ':' << BB.coldim() << ' ' + << CC.rowdim() << 'x' << CC.coldim() + << " trilinear operator" << std::endl; - ApplyMatrix(std::clog, 'X', AA, 'L'); - ApplyMatrix(std::clog, 'Y', BB, 'H'); + CollectVariables(L, AA.coldim(), H, BB.coldim(), F, CC.rowdim()); - std::string zone[2] { "hig", "low" }; - for(size_t i(1); i<=AA.rowdim(); ++i) - std::clog << "T[" << i << "]:=X[" << i << "]*Y[" << i << "]*" - << zone[i & 0x1] << ";"; - std::clog << std::endl; + ApplyMatrix(std::clog, 'X', AA, 'L'); + ApplyMatrix(std::clog, 'Y', BB, 'H'); - ApplyMatrix(std::clog, 'Z', CC, 'T'); + std::string zone[2] { "hig", "low" }; + for(size_t i(1); i<=AA.rowdim(); ++i) { + std::clog << "T[" << i << "]:=X[" << i << "]*Y[" << i << ']'; + if (expanded) std::clog << '*' << zone[i & 0x1]; + std::clog << ':'; + } + std::clog << std::endl; - std::clog << "map(expand,<"; - for(size_t i(1); i<=CC.rowdim(); ++i) { - if (i>1) std::clog << '|'; - std::clog << "F[" << i << "]+Z[" << i << "]-R[" << i << ']'; - } - std::clog << ">);" << std::endl; + ApplyMatrix(std::clog, 'Z', CC, 'T'); + + std::clog << F << "Output:=map(expand,<"; + for(size_t i(1); i<=CC.rowdim(); ++i) { + if (i>1) std::clog << '|'; + std::clog << "F[" << i << "]+Z[" << i << "]-R[" << i << ']'; + } + std::clog << ">);" << std::endl; + + std::clog << "errors:=map(M->nops(select(x->x<>0,convert(M,list))),[" + << L << "Input," << H << "Input," << F << "Output]);" + << std::endl; } #endif diff --git a/src/trilplacer.cpp b/src/trilplacer.cpp index 21809b6..58bb5d8 100644 --- a/src/trilplacer.cpp +++ b/src/trilplacer.cpp @@ -47,9 +47,10 @@ void usage(char ** argv) { - std::clog << "Usage: " << argv[0] << " L.sms R.sms P.sms [-e] [-O #]\n"; + std::clog << "Usage: " << argv[0] << " L.sms R.sms P.sms [-e|-m] [-O #]\n"; - std::clog << " -e: double expands the intermediate result\n" + std::clog << " -m: check for a matrix multiplication\n" + << " -e: double expands the intermediate result\n" << " -O #: randomized search with that many loops\n"; exit(-1); } @@ -63,7 +64,7 @@ void usage(char ** argv) { int main(int argc, char ** argv) { size_t randomloops(DORANDOMSEARCH?DEFAULT_RANDOM_LOOPS:1); - bool doexpand(false); + bool doexpand(false), checkmat(false); if (argc<4) usage(argv); @@ -72,6 +73,7 @@ int main(int argc, char ** argv) { for (int i = 1; i