Kinghsy / Approxiamate-Logic-Synthesis

An inovative approach to cut down chip size by approximating the original circuit.
GNU General Public License v3.0
4 stars 1 forks source link

关于Approx.部分的重构 #1

Open tripack45 opened 7 years ago

tripack45 commented 7 years ago

经过考虑,我感觉Approx部分最大的问题是“全局状态”和本地状态的不清晰。

目前代码从形式上看,表现为函数传入和传出参数相当复杂,我考虑了一下比较根本的原因应该是,函数访问一些全局状态的时候,不应该自己传入参数,而是应该设计某种全局状态。

所以首先,算法本身应该设计成 Functor. 然后把全局状态作为成员函数,然后用构造函数初始化全局状态。比如求一个数组最小值并且返回它的最小下标的函数应该设计成

struct Min {
    int* array;
    int  length;
    Min(int* array, int length);
    int operator() ();
}

还有就是,KMap这样的类不应该把算法作为它的成员函数。KMap类是一种容器,比如你可以要求它返回某一行,或者返回某两行的差值。然后算法利用这些接口进行操作,获得结果。

初步想法是,我来重写KMap这里的东西。

tripack45 commented 7 years ago
//
// Created by king on 17-4-10.
//

#ifndef VE490_BOOL_FUNCTION_H
#define VE490_BOOL_FUNCTION_H

#include <vector>

#include "kmap.h"
#include "../../circuit_profile/sim_profile.h"

/* We need to first clarify the concept of a "Boolean Function"
 * This involves clearifying 2 things
 *
 * 1. Internally, what is the REPRESENTATION of a "BooleanFunction"
 * 2. Externally, what should be an operation of a "BooleanFunction"
 *
 * When handling both, the following question are need to be taken
 * into consideration
 * 1. Performance. How often does the structure be used? How will it
 *      be used? Will it be constantly copied over? Is it better to
 *      make it smaller in size, or it could be a very large class.
 *      What REPRESENTATION would make the common accessing methods
 *      quicker
 *
 * 2. Making Sense. Operations, states should be intuitive.
 *
 * I would argue that in my imagination a Boolean function is a
 * such a construct: It maps boolean inputs into a boolean space,
 * and it is named, namely you name the arguments.
 *
 * I guess two concepts are needed to be differentiated upon. The
 * boolean function that you see in mathematics,
 * i.e. input-truthTable-output, is not the same "BooleanFunction"
 * that you printed out into the blifFunction. The thing that you
 * print into the file, it is a tree-like structure, It doesn't
 * care what its final truthtable is, just simply the tree-like
 * structure. However the actual BooleanFunction, the thing that
 * you perform operations on, cares a lot.
 *
 * In light of this observation. My suggestion would be giving them
 * a separate treatment. Please see the final sections for details.
 *
 * Since we are naming the inputs so the class would be pretty heavy
 * weight. It has a significant cost of contantly copying those node
 * names. So I would like to avoid copying it too much.
 *
 * This actually gives us two things:
 * 1. First since we are not copying it very often we could keep more
 *    information than needed (even redundant ones) as long as it
 *    makes accessing easier
 * 2. Better not to implement all those XORs ANDs in this class (since
 *    the value semantic doesn't go well with the fact that this class
 *    is heavy in weight). Which implies we probably need something
 *    that is more lightweight, and easier, and faster to perform the
 *    xors, ands, count_ones, sort of operations.
 */

class BoolFunction {

private:

    int inputSize;
    std::vector<int > truthTable; // consist of 0/1
    /* A TruthTable is probably something worth encapsulating.
     * TruthTables are often operated on.
     * */

    std::vector<std::string > portName;

    FocusedSimulationResult simRes;
    int divideMode; // e.g. divideMode = XOR_IGNORE
    /* Above two are examples of being not intuitive.
     * Because two functions are equivalent as long as
     * they have the same truth table and same input
     * ordering (which corresponds to your portName)
     * It doesn't make sense that "Simulation result" is
     * a decisive of a function. The same argument also
     * applies to divideMode. These information probably
     * needed to be kept outside the class.
     * It makes even less sense that by doing so you will
     * be COPYING the same simulation information in
     * multiple boolean functions, which does not make sense
     * results into multiple objects. */

public:

    /* You could probably use a structure called "partition" to replace
     * these two arguments. */
    Kmap buildKmap(std::vector<std::string >, std::vector<std::string >);
    BoolFunction combineWith(const BoolFunction& a, const int& opera);

    /* I'm not sure how this will be used so I make no comment (except this) */
    bool divideAble();

    /* If your class only contains STL containers, you actually
     * don't need write your own copy constructors / assignment
     * operators. Due to the "rvalue reference" and "move semantic"
     * It is not even suggested to write so (if you just use STL
     * containers as your attributes. If you insist to do so make
     * sure you write all 5 functions, and in correct form.
     * */
    void operator= (const BoolFunction& initFun);
    // Return value should be BooleanFunction&
    // Assignments return left values.

    /* I don't understand. How come a Boolean function has "Multiple"
     * truthtables? How does it make sense that "I want the third
     * truthTable". How do you represent a truthTable by a single
     * integer? */
    int operator[] (const int& i); // return one of its truthtable

    /* I actually won't put this here. Since your function has
     * named arugments. What will be the new name of arguments if
     * you XOR it with some function else? */
    int operator^ (const BoolFunction& initFun);

    /* Equivalence operator lookds fine */
    bool operator== (const BoolFunction& initFun);

    /* Constructors should not be written in this manner
     * Constructors must gurantee that as long as it suceeds.
     * Now if I use BoolFunction() to construct the object
     * the object could be in a invalid state. Meaning how do
     * you define the "default" value for a boolean function?
     * How many input ports? What are their default names?
     * What is its default truthtable? And more fundamentally why
     * do you need a default BooleanFunction? The idea is, if the
     * answer to any of above questions are false (non-exists).
     * then you cannot allow this object to be default-constructable,
     * which means the first function cannot exists. If in any
     * situation you find to self needing it (while the default
     * construction doesn't make sense), you probably are
     * making a mistake somewhere. You either confused some idea
     * or you are writing wrong logic. */
    BoolFunction();
    BoolFunction(std::vector<int > truthTable, std::vector<std::string >, FocusedSimulationResult simRes);
    // Again simulation results are probably not needed.
    ~BoolFunction();

    /* You can't critisize print functions, can you? */
    void displayName();
    void displayWhole();

};

// ==============================================
// ============= My modifications ===============
// ==============================================

#endif //VE490_BOOL_FUNCTION_H
tripack45 commented 7 years ago
#ifndef VE490_ALGORITHM_HEADER_H
#define VE490_ALGORITHM_HEADER_H

// A notion of "Algorithm"
// An algorithm is essentially a "function"
// A function has parameter (constructor aruguments)
// An list of arguments (operator()'s arguments)
// A return value (operator()'s return value)

class AlgorithmDecompose {
private:
    // Private Type

    // Intermedia values, etc.
    // Actually you only need to declare them here

public:
    // Public Typs

    // This is the return value type
    // A struct is fine. No need for private attributes etc.
    // it's just data, that's it, and you n
    struct Result {
        // What is the return of a decomposition?
        BooleanFunction approxFunction;
        // The function after approximation
        BlifBuilder blifBuilder;
        // Information related to build a boolean function
    };

private:
    // Private Methods & Algorithm global states

    // Private methods are helpers.
    // Algorithm global states is the "shared information"
    // between different stages of algorithm execution

public:
    // Public Methods

    // These 2 are only
    AlgorithmDecompose();
    Result operator()(const BooleanFunction& fun,
                      const SimulationResult& simData);
    // These two should be the only 2 methods exposed
    // Since for a "function" we only require it is callable.

    // Mark copy/move methods as "delete"
    // This means we don't NEED them
    // Any attempt to invoke them triggers a COMPILE ERROR
    AlgorithmDecompose(const AlgorithmDecompose&) = delete;
    AlgorithmDecompose(AlgorithmDecompose&&) = delete;
    AlgorithmDecompose& operator=(const AlgorithmDecompose&) = delete;
    AlgorithmDecompose& operator=(AlgorithmDecompose&&) = delete;

};

class BooleanFunction {

};

class KMap {

};

#endif //VE490_HEADER_H
tripack45 commented 7 years ago
BooleanFunction { inputSize, inputName, TruthTable}

(BooleanFunction, DecompositionInfo) AlgorithmDecomposition()(BooleanFunction, SimulationResults)
tripack45 commented 7 years ago
TruthTable {}
BooleanFunction  {inputSize, inputName, TruthTable}
KMap {}
DecompositionInfo { Map< (nodeName, nodeName ) -> (nodeName, Method)> }
AlgorithmDecompose {}
tripack45 commented 7 years ago
typedef std::string nodeName;
class DecompositionInfo
{
    struct connection
    {
        nodeName n1;
        nodeName n2;
        TruthTable method;
    } std::map<nodeName, connection> data;
    nodeName outputNodeName;

    friend DecompositionInfo
    combineDecompositionInfo(const DecompositionInfo & d1,
                             const DecompositionInfo & d2,
                             const TruthTable& table, 
                             const nodeName& newOutput);

  public:
    DecompositionInfo(nodeName name, bool flip=false);
    nodeName outputNodeName();
    void exportBlif(const std::string& filename);
    std::set<nodeName> outputName();
}

combineDecompostitionInfo(const DecompositionInfo & d1,
                          const DecompostitionInfo & d2,
                          const TruthTable& table
                          const nodeName& newOutput);
tripack45 commented 7 years ago

https://theboostcpplibraries.com/boost.dynamicbitset

Kinghsy commented 7 years ago

typedef boost::dynamic_bitset<> dyBitset;

class TruthTable {
    size_t inputSize;

  public:
    TruthTable cofactor(size_t input, bool = true);
    friend combineTruthTable(TruthTable t1, TruthTable t2,
                             dyBitset t1Mask, dyBitset t2Mask);

    int operator[](size_t term);
    int operator[](dyBitseet term);

    byBItset diff(TruthTable table);

    KMap getKMap(std::vector<int> node1,
                 std::vector<int> node2);
    TruthTable project(std::map<int, int> condition,
                       std::vector<int> onList);
    size_t nInputs();
}

class BooleanFunction {
private:
    int inputSize;
    TruthTable truthTab;
    std::vector<std::string> portName;
    std::string outPortName;
public:
    BooleanFucntion(const int &inputSize,
                    const TruthTable& truthTab,
                    const std::vector<std::string>& portName,
                    const std::string& outPortName);
    ~BooleanFunction();

    bool opeator == (const BooleanFunction &initBF);
    int operator^(const BooleanFunction &initBF);

    int getInputSize();
    int getVal(const dyBitset& term);
    int getVal(const size_t term);

    std::string getPortName(const int& i);
    std::string getOutPortName();
}
Kinghsy commented 7 years ago

class Kmap {

private:

    int width, height;
    std::vector<TruthTable > kmap;
    std::vector<std::string > widthName;
    std::vector<std::string > heightName;

public:

    struct BestApprox {
         BooleanFunction leftFunc;
         BooleanFunction rightFunc;
         int errorCount;
         combineMethod method;
    }

    Kmap(const BooleanFunction& BF, std::vector<std::string > widthName, std::vector<std::string > heightName);
    ~Kmap();

    bool operator== (const Kmap& initKmap);
    int operator^ (const Kmap& initKmap);
    TruthTable operator[] (const int& i);

    BestApprox divide();

    int getWidth();
    int getHeight();
    std::string getWidthName(const int& i);
    std::string getHeightName(const int& j);

}
Kinghsy commented 7 years ago

class AlgorithmDecompose {

public:
    std::deque<BooleanFunction> BooleanFunctionPool;
   // typedef DecompositionInfo ResultType;
   struct ResultType {
         size_t errorCount;
         DecompositionInfo deInfo;
         //BooleanFunctionPool bfPool;
    }

private:

    ResultType bestDecomp;
    void searchPrcoe(BooleanFunctionPool bfPool, DecompositioInfo deInfo);

public:

    ResultType operate(const BooleanFunction& initBF,
                                      const SimulationResult& simData);

    AlgorithmDecompose();
    ~AlgorithmDecompose();

}
Kinghsy commented 7 years ago
void searchProce(....) {
     if select one that can decompose for bfPool == true
        then -->x
        else { compare deInfo with Result; }
     for all x's decomposition {
          decompose x;
          BooleanFunctionPool newBFPool = bfPool + x's decomposed functions - x.
          DecompositionInfo newDecompInfo = deInfo + x's decomposition;
          searchProce(newBFPool, newDecompInfo);
     }
}
tripack45 commented 7 years ago
void searchProce(....) {
     if select one that can decompose for bfPool == true
        then -->x
        else { compare deInfo with Result; }
     for (decomposition in x's decomposition) {
          set<InputName> leftNode = decomposition.leftInput;
          set<InputName> rightNode = decomposition.rightInput;
          (LeftBooleanFun, RightBooleanFun, CombineMethod) = Decompose x;
          bool lrelavent = LeftBooleanFun.isRelevant();
          bool rrelavent = RightBooleanFun.isRelavent();
          if (lrelavant && rrelavent) {
                 (LDecomposeInfo, LApproximatedFunction)  = searchProce(LeftBooleanFun);
                 (RDecomposeInfo, RApproximatedFunction)  = searchProce(RightBooleanFun);
                 return ( combine(LDecomposeInfo, RDecomposeInfo, CombineMethod),
                              FunctionCombine(LApp., RApp,. Mask...));
          }
     }
}
Kinghsy commented 7 years ago

目前提交了一部分,你看到了的话可以看一下。 然后TruthTable 和 DecompositionInfo 那里可能得你维护了。 我已經把AlgorithmDecomposition写掉了,应该没错才对。 然后Kmap和BooleanFunction,感觉依赖你的TrtuthTable,暂时不好下手。 然后说这样几个问题。

  1. DecompositionInfo.combine(),这个function我已经修改过了,事实上我觉得把两个DecompInfo合并起来和TuthTable没有什么关系。
  2. 然后我允许了connection中,inputsNode只有一个或者没有。如果没有这个combineMethod就得是All_0s或者All_1s,就是说有一个Output,一直是0 或者1, 然后如果只有一个input,就说明之间存在转译层。具体的你可以尝试看一下我写的AlgorithmDecomposition。
  3. combineMethod要尽快确定下来,我现在为了让程序能够不变红(显示错误)随手写了个int,具体内容在const.h中,这部分你来决定吧,应为我觉得这部分你的需求要比我大很多。
  4. 这里统一一下,所有的访问都是从0这个位置开始吧,别从1开始,统一一下。不然我怕接口到时候写着写着乱了。
  5. 所有越界的访问都返回空,这个是为了我的一些特殊判断就不用写出来了。。其实assert也行。只是如果你assert的话和我说一声,我得和你统一。
  6. DecompInfo里面加了一个方法,叫做buildDecompInfo,就是根据现有的DecompInfo生成一个BooleanFunction, 这个方法在中间要用到但是忘记提出来了。
  7. 最后,我仿佛配出来的路径上还是有问题,你到时候看看。。我还是觉得我是对的但是不知道为什么报错。报的错大概是无法给target "NewApprox"指定Specific Language.你看到了注意一下。
tripack45 commented 7 years ago
/home/tripack/490_core/newApprox/src/algorithm_decompose.cpp:34:20: error: use of deleted function ‘AlgorithmDecompose::ResultType::ResultType()’
         ResultType res;
/home/tripack/490_core/newApprox/src/algorithm_decompose.h: In constructor ‘AlgorithmDecompose::AlgorithmDecompose()’:
/home/tripack/490_core/newApprox/src/algorithm_decompose.h:39:26: error: no matching function for call to ‘BooleanFunction::BooleanFunction()’
     AlgorithmDecompose() {}

这两个问题都是同一个原因引起的,就是如果你的类没有默认构造函数,那么根据显然所有蕴含了这个类的实例的结构体/类都不会有默认构造函数。然后当你尝试声明他们的实例的时候,就会发现缺少默认构造函数,导致错误。一种解决办法是在BooleanFunctionTruthTable 上加默认构造函数。但是这个不是很好。因为没有默认构造函数其实是一种保护措施,它会防止你使用未初始化的值。 一般的办法是在最高层次的,不得不默认构造的类型上加默认构造函数,也就是说默认构造函数加在 AlgorithmDecompAlgorithmDecompose::ResultType 这两个类型上面。

然后关于这个MajorRow的问题,我想到了一组理论,需要调整一下我们的算法,新的算法是:

  1. 用一组随机的值初始化一个MajorRow
  2. 对于每一行,判断是取反还是不取反更接近这个MajorRow,取更接近的情况。这之后保持MajorRow不变。
  3. 然后现在每一行该取反的都取反了,用新的值重新计算MajorRow。
  4. 重复23,直到MajorRow和每一行的取反状况都不再发生变化为止。

我会提供理论上的解释的,这里不是很写的下。我可能直接敲成Latex了。

Kinghsy commented 7 years ago

现在我这里说一下我的进度。 码完了。能编译,测试没上过。 然后kmap.divide()这个东西写的很长,很长的原因是因为 结构上其实就是这么复杂,因为本来就有四个cases要去分别算,虽然每者都是类似的但是小差别比较多,而且容易产生很麻烦的调用,想了想就全码了,没怎么做大的抽象(我怕做了变成像上次的approx那样逻辑链太长),中间用到了三个帮助的函数。

同时,我使用的是你的算法,只是一开始没有随机一个majorrow,而是把所有的行算了一个majorrow。反正我理解了一下,我觉得你的精髓在于重复23两步,我这点上没有改变。然后你的意思我大概是能理解的,直观上的感觉是有的。然后如果一定要随机的话和我讲一下我改。

那些因为构造的问题我在最上层的结构里加了基本的那种构造函数,反正是瞎构造的你懂的。然后确定一下copy constructor的存在性吧,我觉得这个东西很重要。

大概就是这些了。然后const之类的问题我好像也改得差不多了。。至少能编译了吧

[100%] Built target newApprox
Kinghsy commented 7 years ago

写在这里你能看见:

  1. 新的版本写好了,但是措辞上面和你写的有些不一样。我这里简单提一下因为我觉得你会去看如果不去看的话就跳过本段: columnPattern 就是你的 PF 但是我这里直接采用了真值表,然后rowPattern就是P. 有两个函数作为Kmap的private使用(其实我仔细想了想应该是作为friend出现比较好,但是我觉得目前没改的必要),一个是从columnPatter去算rowPattern,然后另外一个是用rowPattern去算columnPattern。最后有一个算错误数量的。大概就是这样子。最长的程序部分可能看着会觉得很长,我没有再做抽象或者说提取公共部分的原因是如果你要看我再把里面的东西提出来你就要看不懂了。然后注意一下如果要看的话,别看case1,优先看case234这三个case比较好理解一些。
  2. 我和你说的BoolFunction的问题,我想了想可能直接处理不是很方便,我可能会考虑在BoolFunction中加一个reorder()吧,主要的作用就是重新根据新的输入顺序排一下真值表。
  3. 你在FocusSimulationData.count()方法多了几个重载,我没用。没用的原因是因为bitset的高位低位的问题我不知道在你这里是怎么处理的所以就先还是用的用string去访问。如果可以的话在这里comment讲一下。

以上。我休息了。

Kinghsy commented 7 years ago

目前,把BoolFunction测掉了,然后mffc过了一遍,为了测试用。 Kmap和BoolFunction的小错误改了蛮多,目前基本上能保证除了divide部分的正确性。 其中BoolFunction是实测的,Kmap做了一边review。

有两个严重问题,一个需要你帮忙,一个需要你这里的协调:

  1. 我Kmap的testCase一直报我缺库, 我该加的都加了,但是一直报缺。你空了帮我看一下是什么问题。
  2. 一般认为,BoolFunction如果有两个input,比如顺序是x1, x2,那么我访问BoolFunction[0] <=> 访问 {x1=0, x2=0}的情况,然后访问BoolFunction[1] <=> 访问 {x1=1, x2=0}的情况(这个也是和你沟通之后的结果)。所以其实AND对应的BoolFunction的TTable对应的真值表应该在0123的位置上分别是0001。但是你在TTable那里定义的几个实例里面其实应该是恰好反过来的,因为拿string构造的情况其实是反过来的。我看了一下好像对XOR,AND,OR这样几个影响不是很大因为你应该只是写给我看得,但是我后来又拓展了很多个TTable的实例,我不知道这些个在你那里是怎么处理的,所以最好查证一下。这点上如果出了问题会很麻烦。
tripack45 commented 7 years ago

Linking的问题是 Linking是有顺序的,假设你有3个library: a.lib, b.lib, c.lib 如果 a.lib 用了 b.lib 和 c.lib 的函数, a就必须出现在b c前面.

tripack45 commented 7 years ago

关于顺序问题的最终说法: 问题的核心在于由string构造BitSet的过程中,会把String但做一个大整数的二进制表示. 比如 "1010"会当作整数 0xA 的二进制表示. 然后 Bitset的访问是,比如访问 Bitset[2]的时候,访问的是这个大整数的二进制表示的,从最小权重的位数起,第二位的,也就是读出来是1.

使用DBitset进行访问有很明确的语义. 如果你的位序是 {x1, x2, x3} 这么初始化的, 如果你的Bitset是 {b[0] = 0, b[1] = 1, b[2] = 1} 是1, 那么你就是访问 {x1 = 0, x2 = 1, x3= 1}这个case.

但是这个语义对于真值表感觉很奇怪. 最终经过考虑我想这么处理.

  1. 本来原本这个用string构造的函数设计上只是方便测试用的, 允许用户方便的用常量进行构造. 我选择不修改它的语义.
  2. 代码中使用DBitset 进行构造,不要使用string. DBitset 有已经写出来的一系列方便的函数. 应该用起来很顺手才是. 而且能少写很多循环.
  3. 访问TruthTable不要使用string, 使用Bitset.
  4. 会额外提供一个 由vector 构造bitset的函数方便测试.
  5. 去掉了所有用string直接访问TTable 和FocusedSimResult的方法