#include <SWI-Prolog.h> #include <stdio.h> #include <string.h> #include <glpk.h> #include <math.h> #include <stdbool.h> #define FUNCTOR_plus2 PL_new_functor(PL_new_atom("+"), 2) #define FUNCTOR_minus2 PL_new_functor(PL_new_atom("-"), 2) #define FUNCTOR_times2 PL_new_functor(PL_new_atom("*"), 2) #define FUNCTOR_divide2 PL_new_functor(PL_new_atom("/"), 2) #define FUNCTOR_pow2 PL_new_functor(PL_new_atom("^"), 2) #define FUNCTOR_LO PL_new_functor(PL_new_atom(">="), 2) #define FUNCTOR_UP PL_new_functor(PL_new_atom("=<"), 2) #define FUNCTOR_BD PL_new_functor(PL_new_atom("="), 2) void Type(term_t u) { char message[100] = ""; if (PL_is_variable(u)) { strcat(message, "Variable, "); } if (PL_is_ground(u)) { strcat(message, "Ground, "); } if (PL_is_atom(u)) { strcat(message, "Atom, "); } if (PL_is_integer(u)) { strcat(message, "Integer, "); } if (PL_is_string(u)) { strcat(message, "String, "); } if (PL_is_float(u)) { strcat(message, "Float, "); } if (PL_is_list(u)) { strcat(message, "List, "); } if (PL_is_dict(u)) { strcat(message, "Dict, "); } if (PL_is_pair(u)) { strcat(message, "Pair, "); } if (PL_is_atomic(u)) { strcat(message, "Atomic, "); } if (PL_is_compound(u)) { strcat(message, "Compound, "); } if (PL_is_acyclic(u)) { strcat(message, "Acyclic, "); } if (PL_is_callable(u)) { strcat(message, "Callable, "); } printf("%s\n", message); } void Show (term_t t) { functor_t functor; int arity, len, n; char* name; char *s; switch( PL_term_type(t) ) { case PL_VARIABLE: {printf("Variable"); break;} case PL_ATOM: {printf("atom"); break;} case PL_INTEGER: {printf("integer"); break;} case PL_FLOAT: { PL_get_chars(t, &s, CVT_ALL); printf("%s", s); break;} case PL_STRING:{ PL_get_string_chars(t, &s, &len); printf("\"%s\"", s); break;} case PL_TERM: { term_t a = PL_new_term_ref(); PL_get_name_arity(t, &name, &arity); printf("%s(", PL_atom_chars(name)); for(n=1; n<=arity; n++) { PL_get_arg(n, t, a); if ( n > 1 ) printf(", "); pl_display(a); } printf(")"); break; } default: PL_fail; } PL_succeed; } typedef struct node { int is_terminal; int j; struct node* edges[10]; } node; const int inf = 1e9; node* root = NULL; size_t maxVarId = 0; size_t ar_size = 0; size_t row_number = 0; double* ar; int* ia; int* ja; glp_prob *lp; size_t last_symbol = 1; char* s; size_t len; node* newNode() { node* ans = (node*)calloc(1, sizeof(node)); if (ans == NULL) { printf("There's no memory"); PL_fail; } return ans; } void init() { root = newNode(); maxVarId = 0; ar_size = 0; last_symbol = 1; row_number = 0; return; } void FreeNodes(node* x) { if (x == NULL) return; for (int i = 0; i < 10; ++i) { FreeNodes(x->edges[i]); } free(x); } int GetColId(char* nameInProlog) { // printf("A quary for \'%s\' name\n", nameInProlog); node* itr = root; for (size_t i = 1; i < strlen(nameInProlog); ++i) { if (nameInProlog[i]-'0' < 0 || nameInProlog[i]-'0' > 9) { printf("Error in nameInProlog\n"); PL_fail; } if (itr->edges[nameInProlog[i]-'0'] == NULL) { itr->edges[nameInProlog[i]-'0'] = newNode(); } itr = itr->edges[nameInProlog[i]-'0']; } if (itr->is_terminal == 0) { itr->is_terminal = 1; itr->j = ++maxVarId; // printf("Added var: %d, %s\n", itr->j, nameInProlog); // glp_set_col_bnds(lp, itr->j, GLP_LO, 0.0, 0.0); // all Vars must be >= 0 } return itr->j; } char* GetName(term_t term) { char* name_for_now; // And for ever, since Prolog do not store the variables as X/Y/Z if (PL_get_chars(term, &name_for_now, CVT_VARIABLE | CVT_WRITE | REP_UTF8)) { // printf("Bad Var: %s\n", name_for_now); return name_for_now; } else { printf("\nFailed to get variable name.\n"); PL_fail; } } void ReadVars(term_t itr, int is_obj) { if (PL_is_compound(itr)) { functor_t f; if (PL_get_functor(itr, &f)) { if (f == FUNCTOR_times2 || f == FUNCTOR_plus2 || f == FUNCTOR_minus2) { term_t arg1 = PL_new_term_ref(); term_t arg2 = PL_new_term_ref(); if (PL_get_arg(1, itr, arg1) && PL_get_arg(2, itr, arg2)) { ReadVars(arg1, is_obj); ReadVars(arg2, is_obj); return; } } else { printf("Unknown functor"); } } } if (PL_is_variable(itr)) { if (!is_obj) ++ar_size; // printf("Read: %d -> %s\n", ar_size, GetName(itr)); GetColId(GetName(itr)); } } void AddingExprToLP(term_t itr, int row_id, int negative, int is_obj) { if (PL_is_compound(itr)) { functor_t f; if (PL_get_functor(itr, &f)) { term_t arg1 = PL_new_term_ref(); term_t arg2 = PL_new_term_ref(); if (f == FUNCTOR_times2 ){ if (PL_get_arg(1, itr, arg1) && PL_get_arg(2, itr, arg2)) { if (PL_is_integer(arg1) || PL_is_float(arg1)) { int id = GetColId(GetName(arg2)); double val; if (PL_is_integer(arg1)) { int i; PL_get_integer(arg1, &i); val = (double)i; } else PL_get_float(arg1, &val); if (negative) val *= -1; if (!is_obj) { ia[last_symbol] = row_id; ja[last_symbol] = id; ar[last_symbol] = val; ++last_symbol; // printf("Added: (i: %d, j: %d, v: %lf)\n", row_id, id, val); } else { // printf("%lf x #%d", val, id); glp_set_obj_coef(lp, id, val); } } else printf("Please use const*Var notation"); } } else if (f == FUNCTOR_plus2) { if (PL_get_arg(1, itr, arg1) && PL_get_arg(2, itr, arg2)) { // printf("("); AddingExprToLP(arg1, row_id, 0, is_obj); // printf("+"); AddingExprToLP(arg2, row_id, 0, is_obj); // printf(")"); } } else if (f == FUNCTOR_minus2) { if (PL_get_arg(1, itr, arg1) && PL_get_arg(2, itr, arg2)) { // printf("("); AddingExprToLP(arg1, row_id, 0, is_obj); // printf("-"); AddingExprToLP(arg2, row_id, 1, is_obj); // printf(")"); } } else { printf("Unknown functor"); } } } else if (PL_is_variable(itr)) { int id = GetColId(GetName(itr)); if (!is_obj) { ia[last_symbol] = row_id; ja[last_symbol] = id; if (negative) ar[last_symbol] = -1; else ar[last_symbol] = 1; ++last_symbol; } else { if (negative) glp_set_obj_coef(lp, id, -1); //, printf("-1*"); else glp_set_obj_coef(lp, id, 1); //, printf("1*"); // printf("[%d]", id); } } } bool is_digit(char x) { return ('0' <= x && x <= '9'); } double GetDoubleFromS_ABS(size_t *i) { double ans = 0; while ((*i) < len && is_digit(s[(*i)])) { ans *= 10; ans += s[(*i)] - '0'; ++(*i); } if ((*i) < len && s[(*i)] == '.') { ++(*i); double pw = 1./10; while ((*i) < len && is_digit(s[(*i)])) { ans += pw * (s[(*i)] - '0'); pw *= 1./10; ++(*i); } } return ans; } /** list of integers vars Min Value (return) Status from glpk list of constrains | objective func | List of asked vars (return) | | | | | | | V V V V V V */ foreign_t pl_expr(term_t cons, term_t ints, term_t obj, term_t _mn, term_t _ans, term_t _status) { init(); glp_term_out(GLP_OFF); term_t head = PL_new_term_ref(); term_t tail = PL_new_term_ref(); lp = glp_create_prob(); glp_set_prob_name(lp, "BSP-2"); glp_set_obj_dir(lp, GLP_MIN); /// REPLACMENT //////----------------------------------------------- /// | /// V // // printf("Going through cons:\n"); term_t head_cons = PL_new_term_ref(); term_t tail_cons = PL_copy_term_ref(cons); row_number = 1; while (PL_get_list(tail_cons, head_cons, tail_cons)) { PL_get_string_chars(head_cons, &s, &len); for (int i = 0; i < len; ++i) { if (s[i] == '_') { // Find a variable! ++ar_size; int beg = i; // Mark '_' as the beginning of the Variavble name ++i; // Skip '_' while (i < len && is_digit(s[i])) { // Read the Variable name ++i; } // Creating a string that contains the Variable name char *var = (char*)malloc((i - beg + 1) * sizeof(char)); // Putting the Variable name strncpy(var, s + beg, (i - beg)); var[i - beg] = '\0'; // Add null terminator // Calling the Trie // printf("Read var: %s\n", var); GetColId(var); // Make i be the pointer to the last read symbol. --i; } else if (s[i] == ',') ++row_number; } ++row_number; } /// ^ /// | ///------------------------------------------------------ ReadVars(obj, 1); tail = PL_copy_term_ref(ints); while (PL_get_list(tail, head, tail)) { if (PL_is_variable(head)) { GetColId(GetName(head)); // Just to add vars to Trie } else { printf("Ints must be the list of variables"); PL_fail; } } if (!PL_get_nil(tail)) { printf("The list is not properly terminated.\n"); PL_fail; } // printf("Cons: %ld, Vars: %ld\n", row_number, maxVarId); row_number = (row_number == 0) ? 1 : row_number; maxVarId = (maxVarId == 0) ? 1 : maxVarId; // This needs to create a problem. // printf("Row number: %d\n Array size: %d\n, Column size: %d", row_number, ar_size, maxVarId); glp_add_rows(lp, row_number); size_t row_id = 1; glp_add_cols(lp, maxVarId); ia = (int*)malloc((100+ar_size) * sizeof(int)); ja = (int*)malloc((100+ar_size) * sizeof(int)); ar = (double*)malloc((100+ar_size) * sizeof(double)); for (int i = 1; i <= maxVarId; ++i) { // printf("Added %d\n", i); // glp_set_col_kind(lp, i, GLP_CV); glp_set_col_bnds(lp, i, GLP_FR, 0, 0); // Making variables be whatever they want } /// Replacment: --------------------------- tail_cons = PL_copy_term_ref(cons); while (PL_get_list(tail_cons, head_cons, tail_cons)) { bool negative = false; int flag = 42; double bound = 0; bool is_RHS = false; PL_get_string_chars(head_cons, &s, &len); // printf("String got is: %s\n", s); for (size_t i = 0; i < len; ++i) { // If a new term of a constrint: if (s[i] == '+' || s[i] == '-' || is_digit(s[i]) || s[i] == '_') { if (s[i] == '-') negative = true; else negative = false; if (s[i] == '+' || s[i] == '-') ++i; // skipping the sign of the number / variable // num is -1 if it is on RHS (don't forget, that it could be -_xxx) double num = (is_RHS ? -1 : 1) * (negative ? -1 : 1); if (s[i] != '_') { // Skipping space from sign till number while (i < len && !is_digit(s[i])) { ++i; } // Parsing the number num = GetDoubleFromS_ABS(&i); // Applying sign and moving to LHS num *= (negative ? -1 : 1); num *= (is_RHS ? -1 : 1); // Skipping space from number till next char while (i < len && s[i] == ' ') { ++i; } // means if we met number without further multiplied variable if (s[i] != '*') { // As number now in LHS, to return it to RHS, it's needed to multiply with (-1). num *= (-1); bound += num; --i; // pointing to the last read symbol continue; } ++i; // skipping '*' // Skipping chars till '_' while (i < len && s[i] != '_') { ++i; } } int beg = i; // Mark '_' as the beginning of the Variavble name ++i; // Skip '_' while (i < len && is_digit(s[i])) { // Read the Variable name ++i; } // Creating a string that contains the Variable name char *var = (char*)malloc((i - beg) * sizeof(char)); // Putting the Variable name strncpy(var, s + beg, (i - beg)); var[i - beg] = '\0'; // Add null terminator // Calling the Trie int id = GetColId(var); // printf("Added in matrix: i - %d, j - %d, val - %lf\n", row_id, id, num); ia[last_symbol] = row_id; ja[last_symbol] = id; ar[last_symbol] = num; ++last_symbol; // Make i be the pointer to the last read symbol. --i; } else if (s[i] == '=' || s[i] == '>') { is_RHS = true; if (s[i] == '>') flag = GLP_LO; //, printf("Added LO\n"), ++i; else if (s[i] == '=' && (i + 1 < len && s[i + 1] == '<')) flag = GLP_UP; //, printf("Added UP\n"), ++i; else flag = GLP_FX; //, printf("Added FX\n"); } else if (s[i] == ',') { // printf("Bound: %lf\n", bound); if (flag == 42) { printf("No constraint sign (in constraint no. %d) was found.", row_id); PL_fail; } glp_set_row_bnds(lp, row_id, flag, bound, bound); ++row_id; negative = false; flag = 42; bound = 0; is_RHS = false; } else if (s[i] != ' ') { printf("The constraints are incorrect entered!"); PL_fail; } } // printf("Bound: %lf\n", bound); if (flag == 42) { printf("No constraint sign (in constraint no. %d) was found.", row_id); PL_fail; } glp_set_row_bnds(lp, row_id, flag, bound, bound); ++row_id; } ///------------------------------------------------- tail = PL_copy_term_ref(ints); while (PL_get_list(tail, head, tail)) { if (PL_is_variable(head)) { int id = GetColId(GetName(head)); // printf("%s - integer!!!\n", GetName(head)); glp_set_col_kind(lp, id, GLP_IV); // Say to glpk that head must be int. } else { printf("Ints must be the list of variables"); PL_fail; } } // printf("number of integer columns - %d\n", glp_get_num_int(lp)); if (!PL_get_nil(tail)) { printf("The list is not properly terminated.\n"); PL_fail; } // printf("Function to minimize:\n"); AddingExprToLP(obj, 0, 0, 1); // printf("\n"); glp_load_matrix(lp, ar_size, ia, ja, ar); int ret = glp_simplex(lp, NULL); if (ret != 0) PL_fail; int st = glp_get_status(lp); PL_unify_integer(_status, st); // printf("!!!!Status::: %d\nVars: \n", st); /*for (int i = 1; i <= maxVarId; ++i) { printf("%d -> %g\n", i, glp_get_col_prim(lp, i)); }*/ // printf("Answer (prim): %lf\n", glp_get_obj_val(lp)); if (st == 4) { printf("LP has No feasuble solution."); fflush(stdout); PL_succeed; } if (st == 6) { printf("As solution is unbounded, all variables are bounded with [-1e9, 1e9]\n"); for (int i = 1; i <= maxVarId; ++i) glp_set_col_bnds(lp, i, GLP_DB, -inf, inf); glp_set_obj_dir(lp, GLP_MIN); glp_simplex(lp, NULL); st = glp_get_status(lp); } if (st == 5) { glp_intopt(lp, NULL); int status_mip = glp_mip_status(lp); if (status_mip == 4) { printf("LP has solution, but it cannot be optimize to mip solution"); fflush(stdout); PL_unify_integer(_status, st); PL_succeed; } double val = glp_mip_obj_val(lp); // printf("Answer is %lf\n", val); PL_unify_float(_mn, val); tail = PL_copy_term_ref(ints); term_t list = PL_new_term_ref(); term_t list_new = PL_new_term_ref(); PL_put_nil(list); while (PL_get_list(tail, head, tail)) { if (PL_is_variable(head)) { int node_val = floor(glp_mip_col_val(lp, GetColId(GetName(head)))+0.1); term_t value = PL_new_term_ref(); PL_put_integer(value, node_val); PL_cons_list(list, value, list); } else { printf("Ints must be the list of variables"); PL_fail; } } if (!PL_get_nil(tail)) { printf("The list is not properly terminated.\n"); PL_fail; } PL_put_nil(list_new); tail = PL_copy_term_ref(list); while (PL_get_list(tail, head, tail)) { PL_cons_list(list_new, head, list_new); } PL_unify(_ans, list_new); } // printf("!!!Mip status: %d\n", glp_mip_status(lp)); glp_delete_prob(lp); // free Trie; for (int i = 0; i < 10; ++i) FreeNodes(root->edges[i]); // printf("\nThanks!\n"); fflush(stdout); PL_succeed; } install_t install() { PL_register_foreign("minimize", 6, pl_expr, 0); } int main(void) { return 0; }