diff --git a/README.md b/README.md index 94dd000..65e1b8c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Neighborhood Graph and Tree for Indexing High-dimensional Data News ---- -- 01/18/2021 NGT v1.13.0 to provide the [quantized graph (NGTQG)](bin/ngtqg/README.md) is released. +- 01/15/2021 NGT v1.13.0 to provide the [quantized graph (NGTQG)](bin/ngtqg/README.md) is released. - 11/04/2019 [NGT tutorial](https://github.com/yahoojapan/NGT/wiki) has been released. - 06/26/2019 Jaccard distance is available. (v1.7.6) - 06/10/2019 PyPI NGT package v1.7.5 is now available. diff --git a/VERSION b/VERSION index 01b7568..80138e7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.13.3 +1.13.4 diff --git a/bin/ngtqg/README.md b/bin/ngtqg/README.md index 72907a8..07794bf 100644 --- a/bin/ngtqg/README.md +++ b/bin/ngtqg/README.md @@ -40,7 +40,7 @@ Quantize the objects of the specified index and build a quantized graph into the Specify the name of the directory for the existing index such as ANNG or ONNG to be quantized. The index only with L2 distance or normalized L2 distance can be quantized. You should build the ANNG or ONNG with normalized L2 distance in order to use cosine similarity for the quantized graph. **-E** *max_no_of_edges* -Specify the maximum number of edges to build a qunatized graph. Since every 64 or 32 objects that are associated with edges of each node are processed, the number should be a multple of 64 for avx512 or 32 for avx2. +Specify the maximum number of edges to build a qunatized graph. Since every 16 objects that are associated with edges of each node are processed, the number should be a multiple of 16. ### SEARCH diff --git a/lib/NGT/Command.cpp b/lib/NGT/Command.cpp index b565bfa..c595811 100644 --- a/lib/NGT/Command.cpp +++ b/lib/NGT/Command.cpp @@ -923,9 +923,11 @@ using namespace std; if (removedIDs.find(id) == removedIDs.end() && id < objSize) { std::cerr << "Not found an object in the tree. However, it might be a duplicated object. " << id << std::endl; uninsertedTreeObjectCount++; - try { - graphIndex.repository.remove(id); - } catch(...) {} + if (repair) { + try { + graphIndex.repository.remove(id); + } catch(...) {} + } } } } @@ -952,14 +954,18 @@ using namespace std; } invalidGraphObjectCount++; } - } catch (...) { + } catch(NGT::Exception &err) { if (removedIDs.find(id) == removedIDs.end() && id < objSize) { - std::cerr << "Not found an object in the graph. It should be inserted into the graph. " << id << std::endl; + std::cerr << "Not found an object in the graph. It should be inserted into the graph. " << err.what() << " ID=" << id << std::endl; uninsertedGraphObjectCount++; - try { - graphAndTreeIndex.DVPTree::removeNaively(id); - } catch(...) {} + if (repair) { + try { + graphAndTreeIndex.DVPTree::removeNaively(id); + } catch(...) {} + } } + } catch(...) { + std::cerr << "Unexpected error!" << std::endl; } } diff --git a/lib/NGT/Index.cpp b/lib/NGT/Index.cpp index f4f49d4..bc95c13 100644 --- a/lib/NGT/Index.cpp +++ b/lib/NGT/Index.cpp @@ -856,6 +856,11 @@ NGT::GraphIndex::showStatisticsOfGraph(NGT::GraphIndex &outGraph, char mode, siz #else NGT::ObjectDistance &n = (*node)[i]; #endif + if (std::isnan(n.distance)) { + stringstream msg; + msg << "Index::showStatisticsOfGraph: Fatal inner error! The graph has a node with nan distance. " << id << ":" << n.id << ":" << n.distance; + NGTThrowException(msg); + } if (n.id == 0) { std::cerr << "ngt info: Warning. id is zero." << std::endl; valid = false; @@ -978,7 +983,11 @@ NGT::GraphIndex::showStatisticsOfGraph(NGT::GraphIndex &outGraph, char mode, siz } std::sort(node.begin(), node.end()); for (size_t i = 0; i < node.size(); i++) { - assert(i == 0 || node[i - 1] <= node[i]); + if (i > 0 && node[i - 1] > node[i]) { + stringstream msg; + msg << "Index::showStatisticsOfGraph: Fatal inner error! Wrong distance order " << node[i - 1] << ":" << node[i]; + NGTThrowException(msg); + } if (i >= dcsize) { break; } diff --git a/lib/NGT/NGTQ/QuantizedGraph.h b/lib/NGT/NGTQ/QuantizedGraph.h index 30a6ae5..bf3a556 100644 --- a/lib/NGT/NGTQ/QuantizedGraph.h +++ b/lib/NGT/NGTQ/QuantizedGraph.h @@ -63,24 +63,10 @@ namespace NGTQG { return PARENT::at(id).ids; } - - size_t getNumOfPaddedUint8Objects(size_t noEdges) { - if (noEdges == 0) { - return 0; - } - return ((noEdges - 1) / (NGTQ_SIMD_BLOCK_SIZE * NGTQ_BATCH_SIZE) + 1) * (NGTQ_SIMD_BLOCK_SIZE * NGTQ_BATCH_SIZE); - } - size_t getNumOfPaddedUint4Objects(size_t noUint8Objects) { - if (noUint8Objects == 0) { - return 0; - } - return (noUint8Objects * numOfSubspaces) / 2 + 1; - } - void construct(NGT::Index &ngtindex, NGTQ::Index &quantizedIndex, size_t maxNoOfEdges) { NGTQ::InvertedIndexEntry invertedIndexObjects(numOfSubspaces); quantizedIndex.getQuantizer().extractInvertedIndexObject(invertedIndexObjects); - + NGT::GraphAndTreeIndex &index = static_cast(ngtindex.getIndex()); NGT::NeighborhoodGraph &graph = static_cast(index); @@ -89,12 +75,11 @@ namespace NGTQG { for (size_t id = 1; id < graphRepository.size(); id++) { NGT::GraphNode &node = *graphRepository.VECTOR::get(id); - size_t noEdges = node.size() < maxNoOfEdges ? node.size() : maxNoOfEdges; - (*this)[id].ids.reserve(noEdges); - size_t noObjects = getNumOfPaddedUint8Objects(noEdges); - uint8_t *objectData = new uint8_t[noObjects * numOfSubspaces](); + size_t numOfEdges = node.size() < maxNoOfEdges ? node.size() : maxNoOfEdges; + (*this)[id].ids.reserve(numOfEdges); + NGTQ::QuantizedObjectProcessingStream quantizedStream(quantizedIndex.getQuantizer(), numOfEdges); for (auto i = node.begin(); i != node.end(); i++) { - if (distance(node.begin(), i) >= static_cast(noEdges)) { + if (distance(node.begin(), i) >= static_cast(numOfEdges)) { break; } if ((*i).id == 0) { @@ -105,51 +90,35 @@ namespace NGTQG { (*this)[id].ids.push_back((*i).id); for (size_t idx = 0; idx < numOfSubspaces; idx++) { size_t dataNo = distance(node.begin(), i); - size_t blkNo = dataNo / NGTQ_SIMD_BLOCK_SIZE; - size_t oft = dataNo - blkNo * NGTQ_SIMD_BLOCK_SIZE; #if defined(NGT_SHARED_MEMORY_ALLOCATOR) abort(); #else - objectData[blkNo * (NGTQ_SIMD_BLOCK_SIZE * numOfSubspaces) + NGTQ_SIMD_BLOCK_SIZE * idx + oft] = invertedIndexObjects[(*i).id].localID[idx] - 1; + quantizedStream.arrangeQuantizedObject(dataNo, idx, invertedIndexObjects[(*i).id].localID[idx] - 1); #endif } } - { - size_t idx = 0; - uint8_t *uint4Objects = new uint8_t[getNumOfPaddedUint4Objects(noObjects)](); - for (size_t nidx = 0; nidx < noObjects; nidx += NGTQ_SIMD_BLOCK_SIZE * NGTQ_BATCH_SIZE) { - for (size_t bcnt = 0; bcnt < NGTQ_BATCH_SIZE; bcnt++) { - for (size_t lidx = 0; lidx < numOfSubspaces; lidx++) { - for (size_t bidx = 0; bidx < NGTQ_SIMD_BLOCK_SIZE; bidx++) { - if (idx % 2 == 0) { - uint4Objects[idx / 2] = objectData[idx]; - } else { - uint4Objects[idx / 2] |= (objectData[idx] << 4); - } - idx++; - } - } - } - } - delete[] objectData; - (*this)[id].objects = uint4Objects; - } + + (*this)[id].objects = quantizedStream.compressIntoUint4(); } } + void serialize(std::ofstream &os, NGT::ObjectSpace *objspace = 0) { + NGTQ::QuantizedObjectProcessingStream quantizedObjectProcessingStream(numOfSubspaces); uint64_t n = numOfSubspaces; NGT::Serializer::write(os, n); n = PARENT::size(); NGT::Serializer::write(os, n); for (auto i = PARENT::begin(); i != PARENT::end(); ++i) { NGT::Serializer::write(os, (*i).ids); - size_t noObjects = getNumOfPaddedUint4Objects(getNumOfPaddedUint8Objects((*i).ids.size())); - NGT::Serializer::write(os, static_cast((*i).objects), noObjects); + size_t streamSize = quantizedObjectProcessingStream.getUint4StreamSize((*i).ids.size()); + NGT::Serializer::write(os, static_cast((*i).objects), streamSize); } } + void deserialize(std::ifstream &is, NGT::ObjectSpace *objectspace = 0) { try { + NGTQ::QuantizedObjectProcessingStream quantizedObjectProcessingStream(numOfSubspaces); uint64_t n; NGT::Serializer::read(is, n); numOfSubspaces = n; @@ -157,10 +126,10 @@ namespace NGTQG { PARENT::resize(n); for (auto i = PARENT::begin(); i != PARENT::end(); ++i) { NGT::Serializer::read(is, (*i).ids); - size_t noObjects = getNumOfPaddedUint4Objects(getNumOfPaddedUint8Objects((*i).ids.size())); - uint8_t *objects = new uint8_t[noObjects]; - NGT::Serializer::read(is, objects, noObjects); - (*i).objects = objects; + size_t streamSize = quantizedObjectProcessingStream.getUint4StreamSize((*i).ids.size()); + uint8_t *objectStream = new uint8_t[streamSize]; + NGT::Serializer::read(is, objectStream, streamSize); + (*i).objects = objectStream; } } catch(NGT::Exception &err) { std::stringstream msg; @@ -168,16 +137,19 @@ namespace NGTQG { NGTThrowException(msg); } } + void save(const string &path) { const std::string p(path + "/grp"); std::ofstream os(p); serialize(os); } + void load(const string &path) { const std::string p(path + "/grp"); std::ifstream is(p); deserialize(is); } + size_t numOfSubspaces; }; diff --git a/lib/NGT/NGTQ/Quantizer.h b/lib/NGT/NGTQ/Quantizer.h index d2487d4..501e0a3 100644 --- a/lib/NGT/NGTQ/Quantizer.h +++ b/lib/NGT/NGTQ/Quantizer.h @@ -22,7 +22,7 @@ #define NGTQ_SIMD_BLOCK_SIZE 16 -#define NGTQ_BATCH_SIZE 2 +#define NGTQ_BATCH_SIZE 2 #define NGTQG_PREFETCH #if defined(NGT_AVX512) #define NGTQG_AVX512 @@ -368,7 +368,7 @@ class QuantizedObjectDistance { float *localDistanceLookup; #endif size_t size; - vector flag; + vector flag; }; class DistanceLookupTableUint8 { @@ -382,29 +382,18 @@ class QuantizedObjectDistance { delete[] offsets; } } - bool isValid(size_t idx) { return flag[idx]; } -#ifndef NGTQ_DISTANCE_ANGLE - void set(size_t idx, double d) { flag[idx] = true; localDistanceLookup[idx] = d; } - double getDistance(size_t idx) { return localDistanceLookup[idx]; } -#endif - void initialize(size_t s) { - size = s; -#ifdef NGTQ_DISTANCE_ANGLE - localDistanceLookup = new LocalDistanceLookup[size]; -#else + void initialize(size_t numOfSubspaces, size_t localCodebookCentroidNo) { + size_t alignedNumOfSubvectors = ((numOfSubspaces - 1) / NGTQ_BATCH_SIZE + 1) * NGTQ_BATCH_SIZE; + size = alignedNumOfSubvectors * localCodebookCentroidNo; localDistanceLookup = new uint8_t[size]; -#endif - scales = new float[size]; - offsets = new float[size]; - flag.resize(size, false); + scales = new float[alignedNumOfSubvectors]; + offsets = new float[alignedNumOfSubvectors]; } -#ifdef NGTQ_DISTANCE_ANGLE - LocalDistanceLookup *localDistanceLookup; -#else + uint8_t *localDistanceLookup; -#endif size_t size; - vector flag; + size_t aslignedNumOfSubspaces; + size_t localCodebookCentroidNo; float *scales; float *offsets; float totalOffset; @@ -759,7 +748,14 @@ class QuantizedObjectDistance { distanceLUT.scales[li] = scale; distanceLUT.totalOffset += offset; } - + if ((localCodebookNo & 0x1) != 0) { + for (size_t k = 0; k < localCodebookCentroidNo; k++) { + *cdlu++ = 0; + } + distanceLUT.offsets[localCodebookNo] = 0.0; + distanceLUT.scales[localCodebookNo] = 0.0; + } + } } @@ -802,14 +798,7 @@ class QuantizedObjectDistance { } void initialize(DistanceLookupTableUint8 &c) { -#if defined(NGTQG_AVX512) - size_t size = (localCodebookNo / 4 + 1) * 4; -#elif defined(NGTQG_AVX2) - size_t size = (localCodebookNo / 2 + 1) * 2; -#else - size_t size = localCodebookNo; -#endif - c.initialize(size * localCodebookCentroidNo); + c.initialize(localCodebookNo, localCodebookCentroidNo); } NGT::Index *globalCodebook; @@ -964,25 +953,22 @@ class QuantizedObjectDistanceFloat : public QuantizedObjectDistance { } +#if defined(NGTQG_AVX512) || defined(NGTQG_AVX2) inline void operator()(void *inv, float *distances, size_t size, DistanceLookupTableUint8 &distanceLUT) { uint8_t *localID = static_cast(inv); - -#if defined(NGTQG_AVX512) || defined(NGTQG_AVX2) float *d = distances; - auto *last = localID + localDivisionNo * size / 2; - #if defined(NGTQG_AVX512) __m512i mask512x0F = _mm512_set1_epi16(0x000f); __m512i mask512xF0 = _mm512_set1_epi16(0x00f0); - const size_t step512 = 32; - const size_t range512 = (localDivisionNo >> 2) * step512 - (step512 - 1); -#endif + constexpr size_t step512 = 32; + const size_t range512 = (localDivisionNo >> 2) * step512; +#endif const __m256i mask256x0F = _mm256_set1_epi16(0x000f); const __m256i mask256xF0 = _mm256_set1_epi16(0x00f0); - const size_t step256 = 16; - const size_t range256 = (localDivisionNo >> 1) * step256; - + constexpr size_t step256 = 16; + const size_t range256 = (((localDivisionNo - 1) >> 1) + 1) * step256; + auto *last = localID + range256 / NGTQ_SIMD_BLOCK_SIZE * size; while (localID < last) { uint8_t *lut = distanceLUT.localDistanceLookup; auto *lastgroup256 = localID + range256; @@ -1000,7 +986,7 @@ class QuantizedObjectDistanceFloat : public QuantizedObjectDistance { depu16 = _mm512_adds_epu16(depu16, _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(vtmp, 0))); depu16 = _mm512_adds_epu16(depu16, _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(vtmp, 1))); lut += (localCodebookCentroidNo - 1) * 4; - localID += 32; + localID += step512; } #else __m256i depu16l = _mm256_setzero_si256(); @@ -1014,10 +1000,15 @@ class QuantizedObjectDistanceFloat : public QuantizedObjectDistance { __m256i hi = _mm256_slli_epi16(_mm256_and_si256(packedobj, mask256xF0), 4); __m256i obj = _mm256_or_si256(lo, hi); __m256i vtmp = _mm256_shuffle_epi8(lookupTable, obj); + __attribute__((aligned(32))) uint8_t v[32]; + _mm256_storeu_si256((__m256i*)&v, vtmp); #if defined(NGTQG_AVX512) depu16 = _mm512_adds_epu16(depu16, _mm512_cvtepu8_epi16(vtmp)); #else + __attribute__((aligned(32))) uint16_t v16[16]; + _mm256_storeu_si256((__m256i*)&v16, depu16l); + _mm256_storeu_si256((__m256i*)&v16, depu16h); depu16l = _mm256_adds_epu16(depu16l, _mm256_cvtepu8_epi16(_mm256_extractf128_si256(vtmp, 0))); depu16h = _mm256_adds_epu16(depu16h, _mm256_cvtepu8_epi16(_mm256_extractf128_si256(vtmp, 1))); #endif @@ -1046,6 +1037,9 @@ class QuantizedObjectDistanceFloat : public QuantizedObjectDistance { __m256i hih = _mm256_cvtepu16_epi32(_mm256_extractf128_si256(depu16h, 1)); __m256 distancel = _mm256_cvtepi32_ps(_mm256_add_epi32(lol, hil)); __m256 distanceh = _mm256_cvtepi32_ps(_mm256_add_epi32(loh, hih)); + __attribute__((aligned(32))) float v32[8]; + _mm256_storeu_ps((float*)&v32, distancel); + _mm256_storeu_ps((float*)&v32, distanceh); __m256 scalel = _mm256_broadcastss_ps(*reinterpret_cast<__m128*>(&distanceLUT.scales[0])); __m256 scaleh = _mm256_broadcastss_ps(*reinterpret_cast<__m128*>(&distanceLUT.scales[0])); distancel = _mm256_mul_ps(distancel, scalel); @@ -1065,22 +1059,41 @@ class QuantizedObjectDistanceFloat : public QuantizedObjectDistance { #endif d += 16; } + } + #else - uint8_t *lut = distanceLUT.localDistanceLookup; - for (size_t li = 0; li < localDivisionNo; li++) { - for (size_t i = 0; i < size; i++) { - distances[i] += static_cast(*(lut + (*localID - 1))) * distanceLUT.scales[li] / 255.0 + distanceLUT.offsets[li]; - localID++; + inline void operator()(void *inv, float *distances, size_t size, DistanceLookupTableUint8 &distanceLUT) { + uint8_t *localID = static_cast(inv); + size_t alignedNumOfSubvectors = ((localDivisionNo - 1) / NGTQ_BATCH_SIZE + 1) * NGTQ_BATCH_SIZE; + size_t alignedSize = ((size - 1) / 2 + 1) * 2; + uint32_t d[NGTQ_SIMD_BLOCK_SIZE]; + size_t didx = 0; + size_t byteSize = alignedNumOfSubvectors * alignedSize / 2; + auto *last = localID + byteSize; + while (localID < last) { + uint8_t *lut = distanceLUT.localDistanceLookup; + memset(d, 0, sizeof(uint32_t) * NGTQ_SIMD_BLOCK_SIZE); + for (size_t li = 0; li < alignedNumOfSubvectors; li++) { + for (size_t i = 0; i < NGTQ_SIMD_BLOCK_SIZE; i++) { + uint8_t obj = *localID; + if (i % 2 == 0) { + obj &= 0x0f; + } else { + obj >>= 4; + localID++; + } + d[i] += *(lut + obj); + } + lut += localCodebookCentroidNo - 1; } - lut += localCodebookCentroidNo - 1; - } - - for (size_t i = 0; i < size; i++) { - distances[i] = sqrt(distances[i]); + for (size_t i = 0; i < NGTQ_SIMD_BLOCK_SIZE; i++) { + distances[didx + i] = sqrt(static_cast(d[i]) * distanceLUT.scales[0] + distanceLUT.totalOffset); + } + didx += NGTQ_SIMD_BLOCK_SIZE; } -#endif } +#endif inline double operator()(NGT::Object &object, size_t objectID, void *l) { @@ -1253,7 +1266,90 @@ class Quantizer { }; +class QuantizedObjectProcessingStream { + public: + QuantizedObjectProcessingStream(Quantizer &quantizer, size_t numOfObjects): stream(0) { + initialize(quantizer.divisionNo); + setStreamSize(numOfObjects); + stream = new uint8_t[streamSize](); + } + QuantizedObjectProcessingStream(size_t numOfSubspaces): stream(0) { + initialize(numOfSubspaces); + } + + ~QuantizedObjectProcessingStream() { + delete[] stream; + } + + void initialize(size_t divisionNo) { + alignedNumOfSubvectors = ((divisionNo - 1) / NGTQ_BATCH_SIZE + 1) * NGTQ_BATCH_SIZE; + alignedBlockSize = NGTQ_SIMD_BLOCK_SIZE * alignedNumOfSubvectors; + } + + void setStreamSize(size_t numOfObjects) { + alignedNumOfObjects = (((numOfObjects - 1) / NGTQ_SIMD_BLOCK_SIZE + 1) * NGTQ_SIMD_BLOCK_SIZE); + streamSize = alignedNumOfObjects * alignedNumOfSubvectors; + return; + } + + void arrangeQuantizedObject(size_t dataNo, size_t subvectorNo, uint8_t quantizedObject) { +#if defined(NGT_SHARED_MEMORY_ALLOCATOR) + abort(); +#else + size_t blkNo = dataNo / NGTQ_SIMD_BLOCK_SIZE; + size_t oft = dataNo - blkNo * NGTQ_SIMD_BLOCK_SIZE; + stream[blkNo * alignedBlockSize + NGTQ_SIMD_BLOCK_SIZE * subvectorNo + oft] = quantizedObject; +#endif + } + + uint8_t* compressIntoUint4() { + size_t idx = 0; + size_t uint4StreamSize = streamSize / 2; + uint8_t *uint4Objects = new uint8_t[uint4StreamSize](); + while (idx < streamSize) { + for (size_t lidx = 0; lidx < alignedNumOfSubvectors; lidx++) { + for (size_t bidx = 0; bidx < NGTQ_SIMD_BLOCK_SIZE; bidx++) { + if (idx / 2 > uint4StreamSize) { + std::stringstream msg; + msg << "Quantizer::compressIntoUint4: Fatal inner error! " << (idx / 2) << ":" << uint4StreamSize; + NGTThrowException(msg); + } + if (idx % 2 == 0) { + uint4Objects[idx / 2] = stream[idx]; + } else { + uint4Objects[idx / 2] |= (stream[idx] << 4); + } + idx++; + } + } + } + return uint4Objects; + } + + uint8_t* getStream() { + auto s = stream; + stream = 0; + return s; + } + + size_t getUint4StreamSize(size_t numOfObjects) { + setStreamSize(numOfObjects); + return streamSize / 2; + } + + size_t getStreamSize(size_t numOfObjects) { + setStreamSize(numOfObjects); + return streamSize; + } + + uint8_t *stream; + size_t alignedNumOfSubvectors; + size_t alignedBlockSize; + size_t alignedNumOfObjects; + size_t streamSize; +}; + class GenerateResidualObject { public: virtual ~GenerateResidualObject() {} @@ -1748,7 +1844,7 @@ class QuantizerInstance : public Quantizer { clustering.epsilonFrom = 0.10; clustering.epsilonTo = 0.50; clustering.epsilonStep = 0.05; - clustering.maximumIteration = 10; + clustering.maximumIteration = 20; for (size_t li = 0; li < localCodebookNo; ++li) { double diff = clustering.kmeansWithNGT(localCodebook[li], numberOfCentroids); if (diff > 0.0) { diff --git a/lib/NGT/Node.h b/lib/NGT/Node.h index d405f10..260d149 100644 --- a/lib/NGT/Node.h +++ b/lib/NGT/Node.h @@ -54,7 +54,7 @@ namespace NGT { void setNull() { id = 0; } void serialize(std::ofstream &os) { NGT::Serializer::write(os, id); } void deserialize(std::ifstream &is) { NGT::Serializer::read(is, id); } - void serializeAsText(std::ofstream &os) { NGT::Serializer::writeAsText(os, id); } + void serializeAsText(std::ofstream &os) { NGT::Serializer::writeAsText(os, id); } void deserializeAsText(std::ifstream &is) { NGT::Serializer::readAsText(is, id); } protected: NodeID id; diff --git a/lib/NGT/ObjectSpace.h b/lib/NGT/ObjectSpace.h index 084d01f..2e31f65 100644 --- a/lib/NGT/ObjectSpace.h +++ b/lib/NGT/ObjectSpace.h @@ -250,9 +250,9 @@ namespace NGT { template void normalize(T *data, size_t dim) { - double sum = 0.0; + float sum = 0.0; for (size_t i = 0; i < dim; i++) { - sum += (double)data[i] * (double)data[i]; + sum += data[i] * data[i]; } if (sum == 0.0) { std::stringstream msg; @@ -261,7 +261,7 @@ namespace NGT { } sum = sqrt(sum); for (size_t i = 0; i < dim; i++) { - data[i] = (double)data[i] / sum; + data[i] = data[i] / sum; } } uint32_t getPrefetchOffset() { return prefetchOffset; } @@ -304,6 +304,11 @@ namespace NGT { size_t byteSize = objectspace->getByteSizeOfObject(); assert(&(*this)[0] != 0); NGT::Serializer::read(is, (uint8_t*)&(*this)[0], byteSize); + if (is.eof()) { + std::stringstream msg; + msg << "ObjectSpace::BaseObject: Fatal Error! Read beyond the end of the object file. The object file is corrupted?" << byteSize; + NGTThrowException(msg); + } } void serializeAsText(std::ostream &os, ObjectSpace *objectspace = 0) { assert(objectspace != 0); diff --git a/lib/NGT/PrimitiveComparator.h b/lib/NGT/PrimitiveComparator.h index b1b3cb3..4fa7efa 100644 --- a/lib/NGT/PrimitiveComparator.h +++ b/lib/NGT/PrimitiveComparator.h @@ -226,8 +226,11 @@ namespace NGT { template inline static double compareNormalizedL2(const OBJECT_TYPE *a, const OBJECT_TYPE *b, size_t size) { double v = 2.0 - 2.0 * compareDotProduct(a, b, size); - - return sqrt(v); + if (v < 0.0) { + return 0.0; + } else { + return sqrt(v); + } } @@ -470,7 +473,6 @@ namespace NGT { #endif __attribute__((aligned(32))) float f[4]; _mm_store_ps(f, sum128); - double s = f[0] + f[1] + f[2] + f[3]; return s; }