/* Module name: pcp.c Created by: Ljubomir Buturovic Created: 10/27/2002 Purpose: main source file for PCP (Pattern Classification Program). */ /* Copyright 2004 Ljubomir J. Buturovic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include "pau.h" #include "pcp.h" #include "cda.h" #include "ddset.h" #include "bagging.h" #include "hash.h" #include "hash_util.h" #define PCP_VERSION "2.2" int debug = 0; /* Training and test data sets. */ struct dataset *tds = (struct dataset *) 0; struct dataset *teds = (struct dataset *) 0; /* SIGINT and SIGQUIT signal handler. SIGINT is generated by Ctrl-C, SIGQUIT by Ctrl-\. The handler restores the cursor. */ static void int_handler(int signo) { cursor_on(); reset_video(); _exit(0); } /* SIGSEGV signal handler. */ static void segv_handler(int signo) { cursor_on(); reset_video(); remove(PCP_DAT); fprintf(stderr, "Segmentation fault\n"); _exit(0); } static void sig(void) { struct sigaction act; /* Catch SIGINT and SIGQUIT (the signals generated by Ctrl-C and Ctrl-\). Use sigaction(), for portability and reliability. */ act.sa_handler = int_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; #ifdef SA_RESTART act.sa_flags |= SA_RESTART; #endif sigaction(SIGINT, &act, (struct sigaction *) 0); sigaction(SIGQUIT, &act, (struct sigaction *) 0); act.sa_handler = segv_handler; sigaction(SIGSEGV, &act, (struct sigaction *) 0); } /* Remove PCP_DAT if it is empty. Return -1 in case of error and set errno. */ static int cleanup(void) { int status; FILE *fptr; struct stat buf; cursor_on(); reset_video(); fptr = fopen(PCP_DAT, "r"); if (fptr != (FILE *) 0) { status = fflush(fptr); if (status != 0) status = -1; else { status = stat(PCP_DAT, &buf); if ((status == 0) && (buf.st_size == 0)) status = unlink(PCP_DAT); } } else status = -1; return status; } static void pcp_init_menu(int *dm, int *errc, int *key, char *xname) { if (errc) { errmsg(2, *errc, xname); *errc = 0; } *dm = 1; read_keyboard(key); } /* PCP menus. */ static void p_linear_menu(int *errc) { int size; int key; int dm; int rtn; char *xname; size = 3; dm = 1; rtn = 0; *errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_LPAR, size); cursor_off(); } pcp_init_menu(&dm, errc, &key, xname); if (key == 1) *errc = p_lin_learn(&xname); else if (key == 2) pcp_ensemble(PALG_BAG_PLC, errc, &xname, debug); else if (key == 3) { if (teds) *errc = p_lin_predict(PALG_PLC, &xname); else *errc = PERR_UNDEFINED_TEDS; } else if (key == 4) p_xlearn(PALG_PLC, errc, &xname, debug); else if (key == 5) p_xlearn(PALG_BAG_PLC, errc, &xname, debug); else if (key == 6) xlearn_disp(errc, &xname, PCP_XPL); else if (key >= 7 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_F); } else if (key == 12) rtn = 1; else dm = 0; } free(xname); } static void p_quadratic_menu(int *errc) { int size; int key; int dm; int rtn; char *xname; size = 3; dm = 1; rtn = 0; *errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_QPAR, size); cursor_off(); } pcp_init_menu(&dm, errc, &key, xname); if (key == 1) *errc = p_pqc_learn(&xname); else if (key == 2) pcp_ensemble(PALG_BAG_PQC, errc, &xname, debug); else if (key == 3) { if (teds) *errc = p_pqc_predict(&xname); else *errc = PERR_UNDEFINED_TEDS; } else if (key == 4) p_xlearn(PALG_PQC, errc, &xname, debug); else if (key == 5) p_xlearn(PALG_BAG_PQC, errc, &xname, debug); else if (key == 6) xlearn_disp(errc, &xname, PCP_XPL); else if (key >= 7 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_F); } else if (key == 12) rtn = 1; else dm = 0; } free(xname); } /* TBD: postponed. */ #if 0 static void p_pairwise_menu(void) { } #endif static void p_pac_menu(int *errc) { int size; int key; int dm; int rtn; size = 2; dm = 1; rtn = 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_PAC, size); cursor_off(); } pcp_init_menu(&dm, errc, &key, (char *) 0); if (key == 1) p_linear_menu(errc); else if (key == 2) p_quadratic_menu(errc); else if (key == 12) rtn = 1; else if (key >= 3 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_B); } } } /* Linear discriminant classifier menu. */ static void p_lind_menu(int *errc) { int size; int key; int dm; int rtn; char *xname; size = 3; dm = 1; rtn = 0; *errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_LD, size); cursor_off(); } pcp_init_menu(&dm, errc, &key, xname); if (key == 1) p_lind_learn(errc, &xname, &debug); else if (key == 2) pcp_ensemble(PALG_BAG_LIN, errc, &xname, debug); else if (key == 3) { if (teds) *errc = p_lin_predict(PALG_LIN, &xname); else *errc = PERR_UNDEFINED_TEDS; } else if (key == 4) p_xlearn(PALG_LIN, errc, &xname, debug); else if (key == 5) p_xlearn(PALG_BAG_LIN, errc, &xname, debug); else if (key == 6) xlearn_disp(errc, &xname, PCP_XLD); else if (key == 12) rtn = 1; else if (key >= 7 && key <= 11) dm = 0; else dm = 0; } free(xname); } static void p_knn_menu(int *errc) { int size; int key; int dm; int rtn; char *xname; size = 3; dm = 1; rtn = 0; *errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_KNN, size); cursor_off(); } errmsg(2, *errc, xname); dm = 1; read_keyboard(&key); if (key == 1) { if (tds && teds) p_knn(errc, &xname, &debug); else *errc = PERR_UNDEFINED; } else if (key == 2) { if (tds && teds) p_knn_bagging(errc, &xname, &debug); else *errc = PERR_UNDEFINED; } else if (key == 3) p_xlearn(PALG_KNN, errc, &xname, debug); else if (key == 4) p_xlearn(PALG_BAG_KNN, errc, &xname, debug); else if (key == 5) xlearn_disp(errc, &xname, PCP_XNN); else if (key == 6) { if (tds) pcp_knn_xpar(errc, debug, &xname); else *errc = PERR_UNDEFINED_TDS; } else if (key >= 7 && key < 11) { dm = 0; errmsg(2, 0, PCP_MMSG_F); } else if (key == 12) rtn = 1; else dm = 0; } free(xname); } static void p_mlp_menu(int *errc) { int size; int key; int dm; int rtn; char *xname; size = 3; dm = 1; rtn = 0; *errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_MLP, size); cursor_off(); } errmsg(2, *errc, xname); dm = 1; read_keyboard(&key); if (key == 1) p_mlp_learn(errc, &xname, &debug); else if (key == 2) pcp_ensemble(PALG_BAG_MLP, errc, &xname, debug); else if (key == 3) { if (teds) p_mlp_predict(errc, &xname); else *errc = PERR_UNDEFINED_TEDS; } else if (key == 4) p_xlearn(PALG_MLP, errc, &xname, debug); else if (key == 5) p_xlearn(PALG_BAG_MLP, errc, &xname, debug); else if (key == 6) xlearn_disp(errc, &xname, PCP_XMP); else if (key == 7) { if (tds) pcp_mlp_xpar(errc, debug, &xname); else *errc = PERR_UNDEFINED_TDS; } else if (key >= 8 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_G); } else if (key == 12) rtn = 1; else dm = 0; } free(xname); } static void pcp_menu_xpar(int *errc) { int size; int key; int dm; int rtn; char *xname; size = 2; dm = 1; rtn = 0; *errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_XPAR, size); cursor_off(); } pcp_init_menu(&dm, errc, &key, xname); if ((key == 1) || (key == 2)) { if (tds) { if (key == 1) pcp_svm_simplex(errc, debug, &xname); else pcp_svm_grid(errc, debug, &xname); } else *errc = PERR_UNDEFINED_TDS; } else if (key >= 3 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_B); } else if (key == 12) rtn = 1; } } static void p_svm_menu(int *errc) { int size; int key; int dm; int rtn; int acode; char *xname; size = 3; dm = 1; rtn = 0; *errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_SVM, size); cursor_off(); } pcp_init_menu(&dm, errc, &key, xname); if (key == 1) { if (!tds) *errc = PERR_UNDEFINED_TDS; else if (tds->c == 1) *errc = PERR_ONE_CLASS; else p_svm_learn(errc, &xname, debug); } else if (key == 2) { if (!tds) *errc = PERR_UNDEFINED_TDS; else if (tds->c == 1) *errc = PERR_ONE_CLASS; else pcp_ensemble(PALG_SVM, errc, &xname, debug); } else if (key == 3) { if (teds) p_svm_predict(errc, &xname); else *errc = PERR_UNDEFINED_TEDS; } else if ((key == 4) || (key == 5)) { if (!tds) *errc = PERR_UNDEFINED_TDS; else if (tds->c == 1) *errc = PERR_ONE_CLASS; else { if (key == 4) acode = PALG_SVM; else acode = PALG_BAG_SVM; p_xlearn(acode, errc, &xname, debug); } } else if (key == 6) xlearn_disp(errc, &xname, PCP_XSV); else if (key == 7) pcp_menu_xpar(errc); else if (key == 8) { if (tds) p_svm_save(errc, &xname); else *errc = PERR_UNDEFINED_TDS; } else if (key == 12) rtn = 1; else if (key >= 9 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_H); } else dm = 0; } free(xname); } static void p_bayes_menu(int *errc) { int size; int key; int dm; int rtn; char *xname; size = 2; dm = 1; rtn = 0; *errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_BAYES, size); cursor_off(); } pcp_init_menu(&dm, errc, &key, xname); if ((key == 1) || (key == 2)) { if (!tds) *errc = PERR_UNDEFINED_TDS; else if (key == 1) p_bayes(errc, 0, debug); else if (key == 2) p_bayes(errc, 1, debug); } else if (key == 3) p_disp_bayes(errc, &xname); else if (key == 12) rtn = 1; else if (key >= 4 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_C); } } } /* Pattern recognition menu. */ static int pr_menu() { int size; int key; int dm; int rtn; int errc; char *xname; size = 3; dm = 1; rtn = 0; errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_PC, size); cursor_off(); } pcp_init_menu(&dm, &errc, &key, xname); if (key == 1) p_bayes_menu(&errc); else if (key == 2) p_pac_menu(&errc); /* parametric classifiers */ else if (key == 3) p_lind_menu(&errc); /* linear discriminant classifiers */ else if (key == 4) p_knn_menu(&errc); /* k-NN */ else if (key == 5) p_mlp_menu(&errc); /* MLP */ else if (key == 6) p_svm_menu(&errc); /* SVM */ else if (key == 12) rtn = 1; else if (key >= 7 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_F); } else dm = 0; } return errc; } /* Menu for principal components analysis. */ static int pca_menu(void) { int size; int key; int dm; int rtn; int menu_status; int errc; char *xname; size = 2; dm = 1; rtn = 0; menu_status = 0; xname = malloc(PCP_FLEN+1); *xname = '\0'; errc = 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_PCA, size); cursor_off(); } pcp_init_menu(&dm, &errc, &key, xname); if (key == 1) { p_cda(PDR_PCA, &errc, &xname); if (errc) menu_status = errc; } else if (key == 2) { /* Singular value decomposition. To be used when the number of vectors is less than the dimension of the input space. See source file svd.c for additional comments. */ p_svd(&errc, &xname, debug); if (errc) menu_status = errc; } else if (key == 12) rtn = 1; else if (key >= 3 && key <= 11) { dm = 0; errmsg(2, 0, "Press a..b."); } else dm = 0; } free(xname); return menu_status; } /* Feature extraction menu. */ static int fe_menu(void) { int size; int key; int dm; int rtn; int errc; int menu_status; char *xname; size = 2; dm = 1; rtn = 0; errc = 0; menu_status = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_FEXTR, size); cursor_off(); } pcp_init_menu(&dm, &errc, &key, xname); if (key == 1) { p_cda(PDR_FISHER, &errc, &xname); menu_status = errc; } else if (key == 2) menu_status = pca_menu(); else if (key == 3) { if (tds) p_emap(&errc, &xname, debug); else errc = PERR_UNDEFINED_TDS; menu_status = errc; } else if (key == 4) { if (tds && teds) p_transform(P_MAP, &errc, &xname); else errc = PERR_UNDEFINED; menu_status = errc; } else if (key == 12) rtn = 1; else if (key >= 5 && key <= 7) { dm = 0; errmsg(2, 0, PCP_MMSG_D); } else dm = 0; } free(xname); return menu_status; } /* Feature subset selection menu. */ static int fsel_menu(void) { int size; int key; int dm; int rtn; int errc; char *xname; size = 2; dm = 1; rtn = 0; errc = 0; xname = (char *) 0; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_FSEL, size); cursor_off(); } pcp_init_menu(&dm, &errc, &key, xname); if (key == 1) p_fselect(&errc, &xname, debug); else if (key == 2) p_fdisp(&errc, &xname, debug); else if (key == 3) p_f_subset(&errc, &xname, debug); else if (key == 12) rtn = 1; else if (key >= 3 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_C); } else dm = 0; } free(xname); return errc; } static int data_load_menu(void) { int size; int key; int dm; int rtn; int errc; int menu_status; int imin; char *xname; size = 3; dm = 1; rtn = 0; errc = 0; menu_status = 0; xname = malloc(PCP_FLEN+1); *xname = '\0'; while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_LOAD, size); cursor_off(); } pcp_init_menu(&dm, &errc, &key, xname); if (key == 1) { p_load(&errc, &xname); menu_status = errc; } else if (key == 3) /* Display data sets. */ ddset(); else if (key == 2) dstatus(); else if (key == 5) { if (!tds && !teds) { dm = 0; errc = PERR_UNDEFINED; menu_status = errc; } else { p_copy(&errc, &xname); if (!errc) errc = PSTS_DONE; else menu_status = errc; } } else if (key == 6) { if (!tds && !teds) { dm = 0; errc = PERR_UNDEFINED; menu_status = errc; } else if (!tds) { dm = 0; errc = PERR_UNDEFINED_TDS; menu_status = errc; } else { p_transform(P_NORMALIZE, &errc, &xname); if (!errc) errc = PSTS_DONE; else menu_status = errc; } } else if (key == 7) { if (tds) { imin = ivec_min(tds->nd, tds->c); if (imin < 2) { errc = PERR_ONE_SAMPLE; menu_status = errc; } else { errmsg(2, 0, PMSG_EIGEN_ANALYSIS); p_eigen(&errc); menu_status = errc; } } else { dm = 0; errc = PERR_UNDEFINED_TDS; menu_status = errc; } } else if (key == 8) { if (tds) { clear_screen(); covar(tds->x, tds->d, tds->nv, &errc, debug); menu_status = errc; pwait(); } else { dm = 0; errc = PERR_UNDEFINED_TDS; menu_status = errc; } } else if (key == 12) rtn = 1; else if (key >= 9 && key <= 11) { dm = 0; errmsg(2, 0, "Press a..h."); } else dm = 0; } free(xname); return menu_status; } static void usage(char *program, hash_t *status_table) { int internal_code; int exit_code; hscan_t *scan; hnode_t *node; struct pcp_stat *pcp_status; printf("%s (Pattern Classification Program) version %s\n\n", program, PCP_VERSION); printf("Usage: %s [-h] [-b batch_file] [-q] [-d]\n", program); printf(" -h display this message\n"); printf(" -b batch_file run in batch mode, reading commands from batch_file\n"); printf(" -q quiet mode (no screen output)\n"); printf(" -d enable debug mode (produces %s file)\n\n", PCP_DBG); printf("To kill the program, use SIGINT (Ctrl-C) or SIGQUIT (Ctrl-\\)\n"); if (status_table) { printf("\ninternal and exit error codes:\n"); scan = calloc(1, sizeof(hscan_t)); hash_scan_begin(scan, status_table); while ((node = hash_scan_next(scan))) { internal_code = atoi((char *) hnode_getkey(node)); pcp_status = (struct pcp_stat *) hnode_get(node); exit_code = pcp_status->exit_code; if (exit_code > PMAX_ERRNO) { printf("%6d\t%6d", internal_code, exit_code); if (pcp_status->msg) printf("\t%s\n", pcp_status->msg); else printf("\n"); } } } } /* Redirect stdin so that the commands are read from 'batch_file' instead of the keyboard. */ static int redirect_stdin(char *batch_file) { int status; FILE *fptr; status = 0; fptr = freopen(batch_file, "r", stdin); if (!fptr) { status = -1; perror(batch_file); } return status; } /* Redirect stdout so that the output is sent to /dev/null. */ static int redirect_stdout() { int status; FILE *fptr; status = 0; fptr = freopen("/dev/null", "w", stdout); if (!fptr) { status = -1; perror("/dev/null"); } return status; } /* Main program for pattern classification. Returns -1 in case of error. */ int main(int argc, char **argv) { int i; int dm; int rtn; int errc; int key; int size; int status; int menu_status; int exit_code; char *xname; hash_t *status_table; struct pcp_stat *pcp_status; sig(); status_table = create_status_table(); status = 0; menu_status = 0; errc = 0; /* Read command-line arguments. */ if (argc > 1) { i = 1; while ((i < argc) && !status) { if (!strcmp(argv[i], "-d")) { debug = 1; status = unlink(PCP_DBG); if (status == -1) { if (errno != ENOENT) errc = errno; else status = 0; } } else if (!strcmp(argv[i], "-h")) { usage(argv[0], (hash_t *) 0); status = 1; } else if (!strcmp(argv[i], "-H")) { usage(argv[0], status_table); status = 1; } else if (!strcmp(argv[i], "-b")) { if (!argv[i+1]) { usage(argv[0], (hash_t *) 0); status = -1; errc = EINVAL; } else { status = redirect_stdin(argv[i+1]); if (status == -1) errc = errno; i++; } } else if (!strcmp(argv[i], "-q")) { status = redirect_stdout(); if (status == -1) errc = errno; } else { /* Unrecognized option. */ usage(argv[0], (hash_t *) 0); status = -1; errc = EINVAL; } i++; } } if (!status) { unlink(PCP_DAT); size = 2; dm = 1; rtn = 0; errc = 0; xname = (char *) 0; init_load(&errc, &xname); while (!rtn) { if (dm) { clear_screen(); p_disp(PCP_MENU_MAIN, size); cursor_off(); } errmsg(2, errc, xname); dm = 1; status = read_keyboard(&key); if (!status) { if (key == 1) menu_status = data_load_menu(); else if (key == 2) menu_status = pr_menu(); else if (key == 3) menu_status = fe_menu(); else if (key == 4) menu_status = fsel_menu(); else if (key == 12) { rtn = 1; putc('\n', stdout); } else if (key >= 5 && key <= 11) { dm = 0; errmsg(2, 0, PCP_MMSG_D); } else dm = 0; } else { errc = errno; rtn = 1; errmsg(2, errc, xname); } } /* Cleanup. */ cleanup(); free(xname); } if (menu_status) errc = menu_status; pcp_status = hashGetStat(status_table, errc); if (pcp_status) exit_code = pcp_status->exit_code; else exit_code = 1; return exit_code; }