Changeset: 5b1ce8aed1fb for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/5b1ce8aed1fb
Modified Files:
        monetdb5/modules/mal/txtsim.c
Branch: txtsim
Log Message:

Jaro Winkler similarity


diffs (truncated from 330 to 300 lines):

diff --git a/monetdb5/modules/mal/txtsim.c b/monetdb5/modules/mal/txtsim.c
--- a/monetdb5/modules/mal/txtsim.c
+++ b/monetdb5/modules/mal/txtsim.c
@@ -17,55 +17,10 @@
 #include <string.h>
 #include <limits.h>
 
-#define SoundexLen 4           /* length of a soundex code */
-#define SoundexKey "Z000"      /* default key for soundex code */
-
-/* set letter values */
-static const int Code[] = { 0, 1, 2, 3, 0, 1,
-                                                       2, 0, 0, 2, 2, 4,
-                                                       5, 5, 0, 1, 2, 6,
-                                                       2, 3, 0, 1, 0, 2, 0, 2
-};
-
 #define MIN3(X,Y,Z)   (MIN(MIN((X),(Y)),(Z)))
 #define MAX3(X,Y,Z)   (MAX(MAX((X),(Y)),(Z)))
 #define MIN4(W,X,Y,Z) (MIN(MIN(MIN(W,X),Y),Z))
 
-#define INITIAL_INT_BUFFER_LENGTH 2048
-
-#define CHECK_INT_BUFFER_LENGTH(BUFFER, BUFFER_LEN, NEXT_LEN, OP)              
\
-       do {                                                                    
                                                        \
-               if ((NEXT_LEN) > *BUFFER_LEN) {                                 
                        \
-                       size_t newlen = (((NEXT_LEN) + 1023) & ~1023); /* align 
to a multiple of 1024 bytes */ \
-                       int *newbuf = GDKmalloc(newlen);                        
                                \
-                       if (!newbuf)                                            
                                                \
-                               throw(MAL, OP, SQLSTATE(HY013) 
MAL_MALLOC_FAIL);                \
-                       GDKfree(*BUFFER);                                       
                                                \
-                       *BUFFER = newbuf;                                       
                                                \
-                       *BUFFER_LEN = newlen;                                   
                                        \
-               }                                                               
                                                                \
-       } while (0)
-
-#define RETURN_NIL_IF(b,t)                                                     
                                        \
-       if (b) {                                                                
                                                        \
-               if (ATOMextern(t)) {                                            
                                        \
-                       *(ptr*) res = (ptr) ATOMnil(t);                         
                        \
-                       if ( *(ptr *) res == NULL)                              
                                        \
-                               throw(MAL,"txtsim", SQLSTATE(HY013) 
MAL_MALLOC_FAIL);   \
-               } else {                                                        
                                                        \
-                       memcpy(res, ATOMnilptr(t), ATOMsize(t));                
                        \
-               }                                                               
                                                                \
-               return MAL_SUCCEED;                                             
                                        \
-       }
-
-/**
- * levenshtein_distance - levenshtein distance, iterative with one row
- * @res: result pointer
- * @X: Pointer to a string
- * @Y: Pointer to another string
- * @insdel_cost: cost of char insertion/deletion
- * @replace_cost: cost of char replace
- */
 static inline str
 levenshtein(int *res, const str *X, const str *Y, int insdel_cost, int 
replace_cost)
 {
@@ -131,15 +86,6 @@ damerau_putat(int *pOrigin, int col, int
        *pCell = x;
 }
 
-/**
- * damerau_levenshtein - Damerau-Levenshtein distance, iterative with full 
matrix
- * @res: result pointer
- * @S: Pointer to a string
- * @T: Pointer to another string
- * @insdel_cost: cost of char insertion/deletion
- * @replace_cost: cost of char replace
- * @transpose_cos: cost of transpose chars
- */
 static str
 damerau_levenshtein(int *res, str *S, str *T, int insdel_cost, int 
replace_cost, int transpose_cost)
 {
@@ -291,6 +237,210 @@ levenshtein_distance(Client cntxt, MalBl
        return levenshtein(res, X, Y, insdel_cost, replace_cost);;
 }
 
+#define JARO_WINKLER_SCALING_FACTOR 0.1
+#define JARO_WINKLER_PREFIX_LEN 4
+
+typedef struct {
+       size_t matches;      /* accumulator for number of matches for this item 
*/
+       BUN o;               /* position in the BAT */
+       str val;             /* string value */
+       int *cp_seq;         /* string as array of Unicode codepoints */
+       size_t len;          /* string length in characters (multi-byte 
characters count as 1)*/
+       size_t cp_seq_len;       /* string length in bytes*/
+       uint64_t abm;        /* 64bit alphabet bitmap */
+       size_t abm_popcount; /* hamming weight of abm */
+} str_item;
+
+static inline
+size_t _popcount64(uint64_t x) {
+       x = (x & 0x5555555555555555ULL) + ((x >> 1) & 0x5555555555555555ULL);
+       x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL);
+       x = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x >> 4) & 0x0F0F0F0F0F0F0F0FULL);
+       return (x * 0x0101010101010101ULL) >> 56;
+}
+
+static inline
+size_t popcount64(uint64_t x) {
+       return _popcount64(x);
+       /* __builtin_popcountll is the gcc builtin
+        * It is fast as long as the hardware
+        * support (-mpopcnt) is NOT activated
+        */
+       // return __builtin_popcountll(x);
+}
+
+/* static int */
+/* str_item_cmp(const void * a, const void * b) { */
+/*     return strcmp((const char *)((str_item *)a)->val, */
+/*                               (const char *)((str_item *)b)->val); */
+/* } */
+
+/* static int */
+/* str_item_lenrev_cmp(const void * a, const void * b) { */
+/*     return ((int)((str_item *)b)->len - (int)((str_item *)a)->len); */
+/* } */
+
+static str
+str_2_codepointseq(str_item *s) {
+       str p = s->val;
+       unsigned int i;
+       int c;
+
+       s->cp_seq = GDKmalloc(s->len * sizeof(int));
+       if (s->cp_seq == NULL)
+               throw(MAL, "str_2_byteseq", SQLSTATE(HY013) MAL_MALLOC_FAIL);
+
+       for (i = 0; i < s->len; i++) {
+               UTF8_GETCHAR(c, (p));
+               if (c == 0)
+                       break;
+               s->cp_seq[i] = c;
+       }
+       return MAL_SUCCEED;
+illegal:
+       throw(MAL, "str_2_byteseq", SQLSTATE(42000) "Illegal Unicode code 
point");
+}
+
+/* static void */
+/* str_alphabet_bitmap(str_item *s) { */
+/*     size_t i; */
+
+/*     s->abm = 0ULL; */
+/*     for (i=0; i < s->len; i++) { */
+/*             s->abm |= 1ULL << (s->cp_seq[i] % 64); */
+/*     } */
+/*     s->abm_popcount = popcount64(s->abm); */
+/* } */
+
+static inline double
+jaro_winkler_lp(const str_item *a, const str_item *b) {
+       unsigned int i, l;
+
+       /* calculate common string prefix up to prefixlen chars */
+       l = 0;
+       for (i = 0; i < MIN3(a->len, b->len, JARO_WINKLER_PREFIX_LEN); i++)
+               l += (a->cp_seq[i] == b->cp_seq[i]);
+
+       return (double)l * JARO_WINKLER_SCALING_FACTOR;
+}
+
+static inline double
+jaro_winkler(const str_item *x, const str_item *y, double lp, int *x_flags, 
int *y_flags) {
+       int xlen = x->len, ylen = y->len;
+       int range = MAX(0, MAX(xlen, ylen) / 2 - 1);
+       int *x1 = x->cp_seq, *s2 = y->cp_seq;
+       int m=0, t=0;
+       int i, j, l;
+       double dw;
+
+       if (!xlen || !ylen)
+               return 0.0;
+
+       for (i = 0; i < xlen; i++)
+               x_flags[i] = 0;
+
+       for (i = 0; i < ylen; i++)
+               y_flags[i] = 0;
+
+       /* calculate matching characters */
+       for (i = 0; i < ylen; i++) {
+               for (j = MAX(i - range, 0), l = MIN(i + range + 1, xlen); j < 
l; j++) {
+                       if (s2[i] == x1[j] && !x_flags[j]) {
+                               x_flags[j] = 1;
+                               y_flags[i] = 1;
+                               m++;
+                               break;
+                       }
+               }
+       }
+
+       if (!m) {
+               return 0.0;
+       }
+
+       /* calculate character transpositions */
+       l = 0;
+       for (i = 0; i < ylen; i++) {
+               if (y_flags[i] == 1) {
+                       for (j = l; j < xlen; j++) {
+                               if (x_flags[j] == 1) {
+                                       l = j + 1;
+                                       break;
+                               }
+                       }
+                       if (s2[i] != x1[j])
+                               t++;
+               }
+       }
+       t /= 2;
+
+       /* Jaro similarity */
+       dw = (((double)m / xlen) + ((double)m / ylen) + ((double)(m - t) / m)) 
/ 3.0;
+
+       /* calculate common string prefix up to prefixlen chars */
+       if (lp == -1)
+               lp = jaro_winkler_lp(x, y);
+
+       /* Jaro-Winkler similarity */
+       dw = dw + (lp * (1 - dw));
+
+       return dw;
+}
+
+static str
+jaro_winkler_similarity(double *ret, const str *x, const str *y) {
+       int *x_flags=NULL, *y_flags=NULL;
+       str_item xi, yi;
+       str msg = MAL_SUCCEED;
+
+       xi.val = *x;
+       xi.len = UTF8_strlen(*x);
+       if ((msg = str_2_codepointseq(&xi)) != MAL_SUCCEED)
+               goto bailout;
+
+       yi.val = *y;
+       yi.len = UTF8_strlen(*y);
+       if ((msg = str_2_codepointseq(&yi)) != MAL_SUCCEED)
+               goto bailout;
+
+       x_flags = GDKmalloc(xi.len * sizeof(int));
+       y_flags = GDKmalloc(yi.len * sizeof(int));
+
+       if (x_flags == NULL || y_flags == NULL)
+               goto bailout;
+
+       *ret = jaro_winkler(&xi, &yi, -1, x_flags, y_flags);
+
+bailout:
+       if (x_flags) GDKfree(x_flags);
+       if (y_flags) GDKfree(y_flags);
+       GDKfree(xi.cp_seq);
+       GDKfree(yi.cp_seq);
+       return msg;
+}
+
+#define SoundexLen 4           /* length of a soundex code */
+#define SoundexKey "Z000"      /* default key for soundex code */
+
+/* set letter values */
+static const int Code[] = { 0, 1, 2, 3, 0, 1,
+                                                       2, 0, 0, 2, 2, 4,
+                                                       5, 5, 0, 1, 2, 6,
+                                                       2, 3, 0, 1, 0, 2, 0, 2
+};
+
+#define RETURN_NIL_IF(b,t)                                                     
                                        \
+       if (b) {                                                                
                                                        \
+               if (ATOMextern(t)) {                                            
                                        \
+                       *(ptr*) res = (ptr) ATOMnil(t);                         
                        \
+                       if ( *(ptr *) res == NULL)                              
                                        \
+                               throw(MAL,"txtsim", SQLSTATE(HY013) 
MAL_MALLOC_FAIL);   \
+               } else {                                                        
                                                        \
+                       memcpy(res, ATOMnilptr(t), ATOMsize(t));                
                        \
+               }                                                               
                                                                \
+               return MAL_SUCCEED;                                             
                                        \
+       }
+
 static inline char
 SCode(unsigned char c)
 {
@@ -640,6 +790,20 @@ str_2_qgrams(bat *ret, str *val)
 }
 
 /* DEPRECATED (see DEPRECATED_END) */
+#define INITIAL_INT_BUFFER_LENGTH 2048
+#define CHECK_INT_BUFFER_LENGTH(BUFFER, BUFFER_LEN, NEXT_LEN, OP)              
\
+       do {                                                                    
                                                        \
+               if ((NEXT_LEN) > *BUFFER_LEN) {                                 
                        \
+                       size_t newlen = (((NEXT_LEN) + 1023) & ~1023); /* align 
to a multiple of 1024 bytes */ \
+                       int *newbuf = GDKmalloc(newlen);                        
                                \
+                       if (!newbuf)                                            
                                                \
+                               throw(MAL, OP, SQLSTATE(HY013) 
MAL_MALLOC_FAIL);                \
+                       GDKfree(*BUFFER);                                       
                                                \
+                       *BUFFER = newbuf;                                       
                                                \
_______________________________________________
checkin-list mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to