Ca.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009 Kamil Dudka <xdudka00@stud.fit.vutbr.cz>
00003  *
00004  * This file is part of nucad (Non-Uniform CA Designer).
00005  *
00006  * nucad is free software: you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation, either version 3 of the License, or
00009  * any later version.
00010  *
00011  * nucad is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with nucad.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00025 #include "config.h"
00026 #include "Ca.h"
00027 #include "Color.h"
00028 
00029 #ifndef BUILDING_DOX
00030 #   include <iostream>
00031 #endif
00032 
00033 #ifndef CA_CYCLIC_NEIGHBORHOOD
00034 #   define CA_CYCLIC_NEIGHBORHOOD 0
00035 #endif
00036 
00037 std::ostream& operator<< (std::ostream &str, const CaState &ca) {
00038     for (unsigned row = 0; row < ca.size(); ++row) {
00039         for (unsigned col = 0; col < ca.size(); ++col)
00040             str << " " << ca.getCellAtPos(Pos(row,col));
00041         str << std::endl;
00042     }
00043     return str;
00044 }
00045 
00046 void writeRule(std::ostream &str, const TRule5N &rule) {
00047     static const unsigned CENTER = 1 << 0;
00048     static const unsigned TOP    = 1 << 1;
00049     static const unsigned RIGHT  = 1 << 2;
00050     static const unsigned BOTTOM = 1 << 3;
00051     static const unsigned LEFT   = 1 << 4;
00052 
00053     // top
00054     str << "  ";
00055     for (unsigned i = 0; i < RULE_WIDTH; ++i) {
00056         bool next = rule[i];
00057         if ((i & CENTER) == rule[i])
00058             continue;
00059         str << (next
00060                 ? Color(C_LIGHT_GREEN)
00061                 : Color(C_LIGHT_RED))
00062             << static_cast<bool>(i & TOP)
00063             << Color(C_NO_COLOR) << "      ";
00064     }
00065     str << std::endl;
00066 
00067     // left, self, right
00068     for (unsigned i = 0; i < RULE_WIDTH; ++i) {
00069         bool next = rule[i];
00070         if ((i & CENTER) == rule[i])
00071             continue;
00072         str << (next
00073                 ? Color(C_LIGHT_GREEN)
00074                 : Color(C_LIGHT_RED))
00075             << static_cast<bool>(i & LEFT) << " "
00076             << static_cast<bool>(i & CENTER) << " "
00077             << static_cast<bool>(i & RIGHT) << "  "
00078             << Color(C_NO_COLOR);
00079     }
00080     str << std::endl;
00081 
00082     // bottom
00083     str << "  ";
00084     for (unsigned i = 0; i < RULE_WIDTH; ++i) {
00085         bool next = rule[i];
00086         if ((i & CENTER) == rule[i])
00087             continue;
00088         str << (next
00089                 ? Color(C_LIGHT_GREEN)
00090                 : Color(C_LIGHT_RED))
00091             << static_cast<bool>(i & BOTTOM)
00092             << Color(C_NO_COLOR) << "      ";
00093     }
00094     str << std::endl;
00095 }
00096 
00097 bool applyRule(const CaState &ca, Pos pos, const TRule5N &rule) {
00098     bool state = ca.getCellAtPos(pos);
00099 
00100     // FIXME: performance impact
00101     unsigned char idx = ca.getCellAtPos(neighbor(pos, D_LEFT));
00102     idx <<= 1; idx |= ca.getCellAtPos(neighbor(pos, D_BOTTOM));
00103     idx <<= 1; idx |= ca.getCellAtPos(neighbor(pos, D_RIGHT));
00104     idx <<= 1; idx |= ca.getCellAtPos(neighbor(pos, D_TOP));
00105     idx <<= 1; idx |= state;
00106 
00107 #if DEBUG_RULE_APPLICATION
00108         std::cerr << std::endl;
00109         std::cerr << "  "
00110             << static_cast<bool>(idx & (1 << 1))
00111             << std::endl;
00112         std::cerr << static_cast<bool>(idx & (1 << 4)) << " "
00113             << static_cast<bool>(idx & (1 << 0)) << " "
00114             << static_cast<bool>(idx & (1 << 2)) << std::endl;
00115         std::cerr << "  " << static_cast<bool>(idx & (1 << 3))
00116             << std::endl << "--> " << rule[idx]
00117             << std::endl << std::endl;
00118 #endif // DEBUG_RULE_APPLICATION
00119 
00120     return rule[idx];
00121 }
00122 
00123 bool applyRules(CaState &ca, const ICaRules *rules) {
00124     // make snapshot (synchronous CA's state change)
00125     CaState last(ca);
00126 
00127     for (unsigned row = 0; row < ca.size(); ++row) {
00128         for (unsigned col = 0; col < ca.size(); ++col) {
00129             const Pos pos(row, col);
00130             TRule5N rule;
00131             rules->getRuleAtPos(pos, rule);
00132             const bool nextState = applyRule(last, pos, rule);
00133             ca.setCellAtPos(pos, nextState);
00134         }
00135     }
00136 
00137     return last != ca;
00138 }
00139 
00140 // /////////////////////////////////////////////////////////////////////////////
00141 // CaState implementation
00142 CaState::CaState(size_t size):
00143     size_(size)
00144 {
00145     assert(size);
00146     bitset_.resize(size*size, false);
00147 }
00148 
00149 size_t CaState::size() const {
00150     return size_;
00151 }
00152 
00153 bool CaState::getCellAtPos(Pos pos) const {
00154 #if CA_CYCLIC_NEIGHBORHOOD
00155     if (-1 == pos.row)
00156         pos.row += size_;
00157     else if (static_cast<TCoord>(size_) == pos.row)
00158         pos.row = 0;
00159 
00160     if (-1 == pos.col)
00161         pos.col += size_;
00162     else if (static_cast<TCoord>(size_) == pos.col)
00163         pos.col = 0;
00164 #endif
00165 
00166     if (pos.row < 0 || pos.col < 0
00167             || size_ <= static_cast<size_t> (pos.row)
00168             || size_ <= static_cast<size_t> (pos.col))
00169         return false;
00170     else
00171         return bitset_[pos.row * size_ + pos.col];
00172 }
00173 
00174 void CaState::setCellAtPos(Pos pos, bool val) {
00175 #if CA_CYCLIC_NEIGHBORHOOD
00176     if (-1 == pos.row)
00177         pos.row += size_;
00178     else if (static_cast<TCoord>(size_) == pos.row)
00179         pos.row = 0;
00180 
00181     if (-1 == pos.col)
00182         pos.col += size_;
00183     else if (static_cast<TCoord>(size_) == pos.col)
00184         pos.col = 0;
00185 #endif
00186 
00187     if (pos.row < 0 || pos.col < 0
00188             || size_ <= static_cast<size_t> (pos.row)
00189             || size_ <= static_cast<size_t> (pos.col))
00190         return;
00191     bitset_[pos.row * size_ + pos.col] = val;
00192 }
00193 
00194 bool CaState::operator== (const CaState &other) const {
00195     return size_ == other.size_
00196         && bitset_ == other.bitset_;
00197 }
00198 
00199 // /////////////////////////////////////////////////////////////////////////////
00200 // CaEvaluator implementation
00201 #ifndef BUILDING_DOX
00202 struct CaEvaluator::Private {
00203     IGate           *gate;
00204     IGateBinding    *binding;
00205     CaEvalParams    params;
00206 };
00207 #endif
00208 
00209 CaEvaluator::CaEvaluator(
00210         const IGate &gate,
00211         const IGateBinding &binding,
00212         const CaEvalParams &params):
00213     d(new Private)
00214 {
00215     d->gate = gate.clone();
00216     d->binding = binding.clone();
00217     d->params = params;
00218 }
00219 
00220 CaEvaluator::~CaEvaluator() {
00221     delete d->binding;
00222     delete d->gate;
00223     delete d;
00224 }
00225 
00226 size_t CaEvaluator::caSize() const {
00227     return d->params.caSize;
00228 }
00229 
00230 float CaEvaluator::eval(const ICaRules *ca) {
00231     const IGate &gate = *(d->gate);
00232     const IGateBinding &binding = *(d->binding);
00233     const TBus cnt = 1 << d->gate->nInputs();
00234     const unsigned nSteps = d->params.nSteps;
00235     unsigned score = 0;
00236 
00237     // for each input value
00238     for(TBus in = 0; in < cnt; ++in) {
00239         const TBus exp = gate[in];
00240 
00241         // initialize CA
00242         CaState state(d->params.caSize);
00243         binding.setInput(state, in);
00244 
00245         unsigned match = 0;
00246         for(unsigned step = 0; step < nSteps; ++step) {
00247             // synchronous rule application
00248             bool live = applyRules(state, ca);
00249 
00250             // obtain gate's output from cell matrix
00251             TBus out = binding.getOutput(state);
00252             if (out == exp) {
00253                 ++ match;
00254             } else {
00255                 // hamming distance projection
00256                 const TBus nOut = d->gate->nOutputs();
00257                 unsigned ham = 0;
00258                 for (unsigned i = 0; i < nOut; ++i) {
00259                     TBus mask = 1 << i;
00260                     if (static_cast<bool>(out & mask) != static_cast<bool>(exp & mask))
00261                         ++ ham;
00262                 }
00263                 match *= nOut - ham;
00264                 match /= nOut;
00265             }
00266             if (!live) {
00267                 // nothing has changed in last step, we are on a good way
00268                 if (out == exp)
00269                     match = nSteps << 1;
00270                 else
00271                     match <<= 1;
00272 
00273                 // nothing has changed in the last step, there is absolutely
00274                 // no chance anything can change in the next step
00275                 // let's stop it
00276                 break;
00277             }
00278         }
00279 
00280         // include partial score (per input) to the sum
00281         score += match;
00282     }
00283 
00284     // normalize score to range <0, 1>
00285     return static_cast<float>(score)/(cnt*(nSteps << 1));
00286 }
00287 
00288 bool CaEvaluator::cntSteps(const ICaRules *ca, unsigned &min, unsigned &max) {
00289     const IGate &gate = *(d->gate);
00290     const IGateBinding &binding = *(d->binding);
00291     const TBus cnt = 1 << d->gate->nInputs();
00292     const unsigned maxSteps = d->params.nSteps;
00293     min = maxSteps;
00294     max = 0;
00295 
00296     // for each input value
00297     for(TBus in = 0; in < cnt; ++in) {
00298         unsigned step;
00299 
00300         // initialize CA
00301         CaState state(d->params.caSize);
00302         binding.setInput(state, in);
00303 
00304         // simulate and count steps
00305         bool live = true;
00306         for(step = 0; live && step < maxSteps; ++step) {
00307             live = applyRules(state, ca);
00308             TBus out = binding.getOutput(state);
00309             TBus exp = gate[in];
00310             if (!live && out != exp)
00311                 return false;
00312         }
00313 
00314         if (live && step == maxSteps)
00315             // CA didn't stop in the limit defined by params.nSteps
00316             return false;
00317 
00318         // update min/max
00319         if (step < min)
00320             min = step;
00321         if (max < step)
00322             max = step;
00323     }
00324 
00325     // CA stopped in the limit for all possible inputs, it seems working
00326     return true;
00327 }
00328 
00329 namespace {
00330     void writeCaState(
00331             std::ostream &str,
00332             const CaState &state,                       // current state
00333             const CaState &inputs,                      // inputs mask
00334             const CaState &outputs,                     // output mask
00335             const CaState &desiredOutputs,              // desired output vals
00336             const CaState &lastState,                   // last state
00337             bool preferInput = false)                   // true to show inputs
00338     {
00339         const size_t size = state.size();
00340         assert(size == inputs.size());
00341         assert(size == outputs.size());
00342         assert(size == desiredOutputs.size());
00343         assert(size == lastState.size());
00344 
00345         for (unsigned row = 0; row < size; ++row) {
00346             for (unsigned col = 0; col < size; ++col) {
00347                 Pos pos(row, col);
00348 
00349                 // obtain values on current position
00350                 bool val = state.getCellAtPos(pos);
00351                 bool in = inputs.getCellAtPos(pos);
00352                 bool out = outputs.getCellAtPos(pos);
00353                 bool outOk = out && (val == desiredOutputs.getCellAtPos(pos));
00354                 bool change = val ^ lastState.getCellAtPos(pos);
00355 
00356                 str << " ";
00357 
00358                 // select color according to priority
00359                 if (change)
00360                     str << Color(C_YELLOW);
00361                 else if (preferInput && in)
00362                     str << Color(C_LIGHT_BLUE);
00363                 else if (outOk)
00364                     str << Color(C_LIGHT_GREEN);
00365                 else if (out)
00366                     str << Color(C_LIGHT_RED);
00367                 else if (in)
00368                     str << Color(C_LIGHT_GREEN);
00369 
00370                 str << val;
00371                 if (in || out || change)
00372                     str << Color(C_NO_COLOR);
00373             }
00374 
00375             // end of row
00376             str << std::endl;
00377         }
00378     }
00379 }
00380 
00381 void CaEvaluator::simulate(const ICaRules *rules, std::ostream &str, TBus in) {
00382     const IGate &gate = *(d->gate);
00383     const IGateBinding &binding = *(d->binding);
00384     const size_t caSize = d->params.caSize;
00385     const unsigned maxSteps = d->params.nSteps;
00386 
00387     // obtain input mask
00388     CaState inputs(caSize);
00389     binding.setInput(inputs, static_cast<TBus>(-1));
00390 
00391     // obtain output mask
00392     CaState outputs(caSize);
00393     binding.setOutput(outputs, static_cast<TBus>(-1));
00394 
00395     // we need it to highlight matched/mismatched output values
00396     CaState desiredOutputs(caSize);
00397     binding.setOutput(desiredOutputs, gate[in]);
00398 
00399     // initialize CA and write its initial state
00400     CaState state(caSize);
00401     binding.setInput(state, in);
00402     writeCaState(str, state, inputs, outputs, desiredOutputs, state, true);
00403     str << std::endl;
00404 
00405     // simulate CA and write its state after each step
00406     bool live = true;
00407     for(unsigned step = 0; live && step < maxSteps; ++step) {
00408         CaState last(state);
00409         live = applyRules(state, rules);
00410         str << Color(C_LIGHT_BLUE) << "--- step: " << (step + 1)
00411             << Color(C_NO_COLOR) << std::endl;
00412         writeCaState(str, state, inputs, outputs, desiredOutputs, last);
00413         str << std::endl;
00414     }
00415 }
00416 
00417 void CaEvaluator::simulate(const ICaRules *rules, std::ostream &str) {
00418     const TBus cnt = 1 << d->gate->nInputs();
00419 
00420     // for each input value
00421     for(TBus in = 0; in < cnt; ++in) {
00422         str << Color(C_LIGHT_GREEN) << "--- input: " << in
00423             << Color(C_NO_COLOR) << std::endl;
00424         this->simulate(rules, str, in);
00425     }
00426 }
00427 

Generated on Sat May 2 16:39:31 2009 for nucad by  doxygen 1.5.4