root/vtcross/trunk/src/include/vtcross/cbr.h @ 518

Revision 518, 11.4 KB (checked in by bhilburn, 15 years ago)

Added Doxygen documentation to headers in the include/vtcross directory.

RevLine 
[411]1/*
2 Copyright 2009 Virginia Polytechnic Institute and State University 
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7 
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
[518]17/*! This header contains the declaration and a full implementation of
18 * the CBR class - the default CROSS case-based reasoner, which can be used as a
19 * backend for cognitive engines.
[468]20 */
[98]21
[468]22
[471]23
[161]24#ifndef CBR_H
25#define CBR_H
26
[468]27#include <cstdlib>
28#include <cstring>
29#include <cstdio>
30#include <stdint.h>
31#include <string>
[469]32
[468]33#include <sqlite3.h>
[98]34
[468]35#include "vtcross/common.h"
36#include "vtcross/debug.h"
37#include "vtcross/error.h"
[98]38
[468]39using namespace std;
[98]40
41
[518]42#define DATABASENAME "cross_cbr"
[98]43
44
[471]45/* This is an internal debugging function used by some sqlite3 function calls.
[518]46 *  It is not used otherwise in the CROSS codebase. */
[471]47int32_t
[518]48callback(void *notUsed, int32_t argc, char **argv, char **azColName)
[471]49{
50    for(size_t i = 0; i < argc; i++) {
51        LOG("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
52    }
53    LOG("\n");
54
55    return 0;
56}
57
58
[518]59/*! \brief Case-Based Reasoner class declaration.
60 *
61 * The CBR class is designed to used as either as-is, or as a parent class.  All
[471]62 * functions are declared virtual, and internal members are 'protected' rather
63 * than private. If you require functionality in a CBR not specifically provided
64 * by this class, include this header in your source file, create a new class
65 * that derives from CBR, and implement your desired functionality over the
66 * original virtual functions as necessary.
67 */
[468]68class CBR
69{
70    public:
[518]71        /*! \brief Constructors for the CBR class.
72         *
73         * Note that the default constructor
[471]74         * must be defined inline here so that super-calls from child classes
75         * don't fail (i.e. we cannot rely on the compiler-provided constructor. */
[470]76        CBR(){};
[472]77        CBR(string _filename, string _tablename, string _cols[], uint32_t _len);
78        CBR(string _filename, string _tablename, string _cols[], \
79                string _primcols[], uint32_t _len, uint32_t _primlen);
[98]80
[518]81        /*! \brief Destructors for the CBR class.
82         *
83         * Destructor for the CBR class. Note that this destructor will be
[468]84         * called automatically by any derived classes, and so child classes
85         * should not repeat the freeing actions performed in this function. */
86        virtual ~CBR();
[98]87
[518]88        /*! \brief Open/Create a sqlite database for the CBR.
89         *
90         * This function opens the CROSS database, or if it has not been
[471]91         * created yet, creates it. */
[468]92        virtual int32_t OpenDatabase();
93
[518]94        /*! \brief  Execute a sqlite command.
95         *
96         * Construct and execute a sqlite3 command and pass the return code back. */
[468]97        virtual int32_t ExecuteCommand();
98
[518]99        /*! \brief Search the sqlite3 database.
100         *
101         * Execute a sqlite3 search command and store the results in the passed
[471]102         * retvals argument. */
[468]103        virtual int32_t ExecuteSearchCommand(float *_retvals);
104
[518]105        /*! \brief Print the CROSS sqlite database. */
[468]106        virtual void Print();
107
[518]108        /*! \brief Search the CBR database.
109         *
110         * Search the CROSS database for specific fields and store the results
[471]111         * in the passed retvals argument. */
[472]112        virtual int32_t Search(string _names[], int32_t *_ops, float *_vals, \
[468]113                uint32_t _n, float *_retvals);
[472]114        virtual int32_t SearchSum(string _name, float *_retvals);
115        virtual int32_t SearchRand(string _names[], int32_t *_ops, float *_vals, uint32_t _n, \
[469]116            float *_retvals);
[468]117
[518]118        /*! \brief Update an entry in the CBR database.  */
[472]119        virtual int32_t Update(string _where[], string _set[], float *_wherevals, \
[468]120                float *_setvals, uint32_t _wherelen, uint32_t _setlen);
121
[518]122        /*! \brief Add a row to the CROSS sqlite3 database. */
[472]123        virtual int32_t AddRow(string _cols[], float *_vals, uint32_t _len);
[468]124
125    protected:
[472]126        string filename;
127        string tablename;
128        string command;
129
[468]130        sqlite3 *db;
131        uint32_t numColumns;
132};
133
134
[472]135CBR::CBR(string _filename, string _tablename, string _cols[], uint32_t _len)
[468]136{
[472]137    /* Store database properties. */
138    filename = _filename;
139    tablename = _tablename;
140    numColumns = _len;
[468]141
[472]142    /* Create the database (or open it if it already exists). */
[468]143    OpenDatabase();
144
[472]145    /* Generate the command that will create the initial table within the
[518]146     * CROSS database. */
[472]147    command = "CREATE TABLE " + tablename + "(";
148    for(size_t i = 0; i < numColumns; i++) {
[496]149        command += _cols[i] + " FLOAT";
[468]150
[472]151        /* If this column is not the last entry, add a comma to the command in
152         * preperation for the next entry. */
153        if(i != numColumns - 1)
154            command += ", ";
[468]155    }
[472]156    command += ");";
[468]157
[472]158    /* Execute the generated command. At this point, the database is ready for
159     * use. */
[468]160    ExecuteCommand();
161}
162
[472]163
164CBR::CBR(string _filename, string _tablename, string _cols[], \
165        string _primcols[], uint32_t _len, uint32_t _primlen)
[469]166{
[472]167    /* Store database properties. */
168    filename = _filename;
169    tablename = _tablename;
170    numColumns = _len;
[468]171
[472]172    /* Create the database (or open it if it already exists). */
[469]173    OpenDatabase();
174
[472]175    /* Generate the command that will create the initial table within the
[518]176     * CROSS database with primary keys. */
[472]177    command = "CREATE TABLE " + tablename + "(";
178    for(size_t i = 0; i < numColumns; i++) {
[497]179        command += _cols[i] + " FLOAT, ";
[472]180    }
[469]181
[472]182    command += "PRIMARY KEY (";
183    for(size_t j = 0; j < _primlen; j++) {
184        command += _primcols[j];
[469]185
[472]186        /* If this column is not the last entry, add a comma to the command in
187         * preperation for the next entry. */
188        if(j != _primlen - 1)
189            command += ", ";
[469]190    }
[472]191    command += "));";
[469]192
[472]193    /* Execute the generated command. At this point, the database is ready for
194     * use. */
[469]195    ExecuteCommand();
196}
197
198
[468]199CBR::~CBR()
200
[472]201    /* Generate the sqlite command to delete a table and all of its contents,
202     * and then execute it. */
203    command = "drop table " + tablename;
[468]204    ExecuteCommand();
205
[472]206    /* Tell sqlite to clean up the database. */
207    command = "vacuum";
[468]208    ExecuteCommand();
209}
210
211
[471]212int32_t
213CBR::OpenDatabase()
214{
[472]215    int32_t rc = sqlite3_open(filename.c_str(), &db);
[471]216    if(rc) {
217        WARNING("Can't open database: %s\n", sqlite3_errmsg(db));
218        sqlite3_close(db);
219        exit(1);
220    }
221
222    return rc;
223}
224
225
226int32_t
227CBR::ExecuteCommand()
228{
229    char *zErrMsg = 0;
230
[472]231    int32_t rc = sqlite3_exec(db, command.c_str(), callback, 0, &zErrMsg);
232    if(rc != SQLITE_OK) {
233        WARNING("SQL error: %s: %s\n", zErrMsg, command.c_str());
[471]234        sqlite3_free(zErrMsg);
235    }
236
237    return rc;
238}
239
240
241int32_t
242CBR::ExecuteSearchCommand(float *_retvals)
243{
[472]244    sqlite3_stmt *pStatement;
[471]245
[472]246    int32_t rc = sqlite3_prepare_v2(db, command.c_str(), -1, &pStatement, NULL);
247    if(rc == SQLITE_OK) {
248        if(sqlite3_step(pStatement) == SQLITE_ROW) {
249            for(size_t i = 0; i < numColumns; ++i) {
[471]250                _retvals[i] = sqlite3_column_double(pStatement, i);
[472]251            }
[471]252        } else {
[472]253                    LOG("CBR:: No matched results returning default.\n");
254                        rc = 31337;
[471]255                }
256    } else {
[472]257                WARNING("CBR:: Error executing SQL statement. rc = %i\n%s\n", rc, command.c_str());
[471]258    }
259
260    sqlite3_finalize(pStatement);
261   
262    return rc;
263}
[472]264
265
[468]266void
267CBR::Print()
268{
[472]269    /* Generate the sqlite command to print the database, which is effectively a
270     * 'select all elements' command, and then execute it. */
271    command = "select " + tablename + ".* from " + tablename + ";";
[468]272
273    ExecuteCommand();
[472]274    LOG("database %s, table %s:\n", filename.c_str(), tablename.c_str());
[468]275}
276
[472]277
[468]278int32_t
[472]279CBR::Search(string _names[], int32_t *_ops, float *_vals, uint32_t _n, \
280        float *_retvals)
[468]281{   
282    char str_buffer[64];
[472]283    const string ops_str[] = {"==", "!=", ">", ">=", "<", "<="};
284
285    command = "select " + tablename + ".* from " + tablename + " where ";
286
287    for(size_t i = 0; i < _n; i++) {
288        /* Make sure that the passed ops value is valid. */
289        if((_ops[i] < 0) || (_ops[i] > 5)) {
290            ERROR(1, "Error: cbr_search(), invalid ops id : %d\n", _ops[i]);
[468]291        }
292
[476]293        command += _names[i] + ops_str[_ops[i]];
[468]294
295
[472]296        sprintf(str_buffer, "%E", _vals[i]);
297        command += string(str_buffer);
298
299        if(i < _n - 1)
300            command += " AND ";
[468]301        else
[472]302            command += " order by utility desc;";
[507]303       
[468]304    }
[509]305    //LOG("CBR::Search - command: %s\n", command.c_str());
[468]306
[472]307    return ExecuteSearchCommand(_retvals);
[468]308}
309
[472]310
[469]311int32_t
[472]312CBR::SearchSum(string _name, float *_retvals)
[469]313{   
[472]314    command = "select SUM(" + tablename + "." + _name + ") from " + tablename + ";";
[468]315
[472]316    return ExecuteSearchCommand(_retvals);
[469]317}
318
319
320int32_t
[472]321CBR::SearchRand(string _names[], int32_t *_ops, float *_vals, uint32_t _n, \
322        float *_retvals)
[469]323{   
324    char str_buffer[64];
[472]325    const char *ops_str[] = {"==", "!=", ">", ">=", "<", "<="};
326
327    command = "select " + tablename + ".* from " + tablename + " where ";
328
329    for(size_t i = 0; i < _n; i++) {
330        /* Make sure that the passed ops value is valid. */
331        if((_ops[i] < 0) || (_ops[i] > 5)) {
332            ERROR(1, "Error: cbr_search(), invalid ops id : %d\n", _ops[i]);
[469]333        }
334
[476]335        command += _names[i] + ops_str[_ops[i]];
[472]336
[469]337        sprintf(str_buffer, "%E", _vals[i]);
[472]338        command += str_buffer;
[469]339
[472]340        if(i < _n - 1)
341            command += " AND ";
[469]342        else
[472]343            command += " order by RAND();";
[469]344    }
345
[472]346    return ExecuteSearchCommand(_retvals);
[469]347}
348
349
[468]350int32_t
[472]351CBR::Update(string _where[], string _set[], float *_wherevals, float *_setvals,
352                uint32_t _wherelen, uint32_t _setlen)
[468]353{
[472]354    char str_buffer[64];
355
356    /* Generate the command to update the table. */
357    command = "UPDATE " + tablename + " SET ";
358
359    for(size_t i = 0; i < _setlen; i++) {
360        command += _set[i] + " = ";
361        sprintf(str_buffer, "%f", _setvals[i]);
362        command += string(str_buffer) + "  ";
363
364        if(i != _setlen - 1)
365            command += ", ";
[468]366    }
[472]367    command += " WHERE ";
[468]368
[472]369    for(size_t j = 0; j < _wherelen; j++) {
370        command += _where[j] + " = ";
371        sprintf(str_buffer, "%f", _wherevals[j]);
372        command += string(str_buffer) + "  ";
373
374        if(j != _wherelen - 1)
375            command += "AND ";
[468]376    }
[472]377    command += ";";
[468]378   
[472]379    return ExecuteCommand();
[468]380}
381
[472]382
[468]383int32_t
[472]384CBR::AddRow(string _cols[], float *_vals, uint32_t _len)
[468]385{
[472]386    char str_buffer[64];
387
388    command = "insert into " + tablename + " (";
389
390    for(size_t i = 0; i < _len; i++) {
391        command += _cols[i];
392
393        if(i != numColumns - 1)
394            command += ", ";
[468]395    }
[472]396    command += ") values(";
[468]397
[472]398    for(size_t j = 0; j < _len; j++) {
399        // TODO I have no idea what the below question is about.
[469]400        // ???? how to fill the values if numColumns != _len
[468]401        // assume = in the following
[472]402        sprintf(str_buffer, "%f", _vals[j]);
403        command += str_buffer;
404
405        if(j != numColumns - 1)
406            command += ", ";
[468]407    }
[472]408    command += ");";
[468]409   
[472]410    return ExecuteCommand();
[468]411}
412
[161]413#endif
Note: See TracBrowser for help on using the browser.