# -*- coding: utf-8 -*-

# Copyright (c) 2013 Ole Krause-Sparmann

# 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.

import numpy
import scipy
import unittest
import time

from nearpy import Engine
from nearpy.distances import CosineDistance

from nearpy.hashes import RandomBinaryProjections, HashPermutations, HashPermutationMapper

def example2():

    # Dimension of feature space
    DIM = 100

    # Number of data points (dont do too much because of exact search)
    POINTS = 20000

    ##########################################################

    print('Performing indexing with HashPermutations...')
    t0 = time.time()

    # Create permutations meta-hash
    permutations = HashPermutations('permut')

    # Create binary hash as child hash
    rbp_perm = RandomBinaryProjections('rbp_perm', 14)
    rbp_conf = {'num_permutation':50,'beam_size':10,'num_neighbour':100}

    # Add rbp as child hash of permutations hash
    permutations.add_child_hash(rbp_perm, rbp_conf)

    # Create engine
    engine_perm = Engine(DIM, lshashes=[permutations], distance=CosineDistance())

    # First index some random vectors
    matrix = numpy.zeros((POINTS,DIM))
    for i in range(POINTS):
        v = numpy.random.randn(DIM)
        matrix[i] = v
        engine_perm.store_vector(v)

    # Then update permuted index
    permutations.build_permuted_index()

    t1 = time.time()
    print('Indexing took %f seconds' % (t1-t0))

    # Get random query vector
    query = numpy.random.randn(DIM)

    # Do random query on engine 3
    print('\nNeighbour distances with HashPermutations:')
    print('  -> Candidate count is %d' % engine_perm.candidate_count(query))
    results = engine_perm.neighbours(query)
    dists = [x[2] for x in results]
    print(dists)

    # Real neighbours
    print('\nReal neighbour distances:')
    query = query.reshape((DIM))
    dists = CosineDistance().distance(matrix, query)
    dists = dists.reshape((-1,))
    dists = sorted(dists)
    print(dists[:10])

    ##########################################################

    print('\nPerforming indexing with HashPermutationMapper...')
    t0 = time.time()

    # Create permutations meta-hash
    permutations2 = HashPermutationMapper('permut2')

    # Create binary hash as child hash
    rbp_perm2 = RandomBinaryProjections('rbp_perm2', 14)

    # Add rbp as child hash of permutations hash
    permutations2.add_child_hash(rbp_perm2)

    # Create engine
    engine_perm2 = Engine(DIM, lshashes=[permutations2], distance=CosineDistance())

    # First index some random vectors
    matrix = numpy.zeros((POINTS,DIM))
    for i in range(POINTS):
        v = numpy.random.randn(DIM)
        matrix[i] = v
        engine_perm2.store_vector(v)

    t1 = time.time()
    print('Indexing took %f seconds' % (t1-t0))

    # Get random query vector
    query = numpy.random.randn(DIM)

    # Do random query on engine 4
    print('\nNeighbour distances with HashPermutationMapper:')
    print('  -> Candidate count is %d' % engine_perm2.candidate_count(query))
    results = engine_perm2.neighbours(query)
    dists = [x[2] for x in results]
    print(dists)

    # Real neighbours
    print('\nReal neighbour distances:')
    query = query.reshape((DIM))
    dists = CosineDistance().distance(matrix,query)
    dists = dists.reshape((-1,))
    dists = sorted(dists)
    print(dists[:10])

    ##########################################################

    print('\nPerforming indexing with multiple binary hashes...')
    t0 = time.time()

    hashes = []
    for k in range(20):
        hashes.append(RandomBinaryProjections('rbp_%d' % k, 10))

    # Create engine
    engine_rbps = Engine(DIM, lshashes=hashes, distance=CosineDistance())

    # First index some random vectors
    matrix = numpy.zeros((POINTS,DIM))
    for i in range(POINTS):
        v = numpy.random.randn(DIM)
        matrix[i] = v
        engine_rbps.store_vector(v)

    t1 = time.time()
    print('Indexing took %f seconds' % (t1-t0))

    # Get random query vector
    query = numpy.random.randn(DIM)

    # Do random query on engine 4
    print('\nNeighbour distances with multiple binary hashes:')
    print('  -> Candidate count is %d' % engine_rbps.candidate_count(query))
    results = engine_rbps.neighbours(query)
    dists = [x[2] for x in results]
    print(dists)

    # Real neighbours
    print('\nReal neighbour distances:')
    query = query.reshape((DIM))
    dists = CosineDistance().distance(matrix,query)
    dists = dists.reshape((-1,))
    dists = sorted(dists)
    print(dists[:10])

    ##########################################################

[((1626, 1637), 'time.time', 'time.time', ([], {}), '()\n', (1635, 1637), False, 'import time\n'), ((1694, 1720), 'nearpy.hashes.HashPermutations', 'HashPermutations', (['"""permut"""'], {}), "('permut')\n", (1710, 1720), False, 'from nearpy.hashes import RandomBinaryProjections, HashPermutations, HashPermutationMapper\n'), ((1776, 1815), 'nearpy.hashes.RandomBinaryProjections', 'RandomBinaryProjections', (['"""rbp_perm"""', '(14)'], {}), "('rbp_perm', 14)\n", (1799, 1815), False, 'from nearpy.hashes import RandomBinaryProjections, HashPermutations, HashPermutationMapper\n'), ((2146, 2172), 'numpy.zeros', 'numpy.zeros', (['(POINTS, DIM)'], {}), '((POINTS, DIM))\n', (2157, 2172), False, 'import numpy\n'), ((2378, 2389), 'time.time', 'time.time', ([], {}), '()\n', (2387, 2389), False, 'import time\n'), ((2481, 2504), 'numpy.random.randn', 'numpy.random.randn', (['DIM'], {}), '(DIM)\n', (2499, 2504), False, 'import numpy\n'), ((3142, 3153), 'time.time', 'time.time', ([], {}), '()\n', (3151, 3153), False, 'import time\n'), ((3211, 3243), 'nearpy.hashes.HashPermutationMapper', 'HashPermutationMapper', (['"""permut2"""'], {}), "('permut2')\n", (3232, 3243), False, 'from nearpy.hashes import RandomBinaryProjections, HashPermutations, HashPermutationMapper\n'), ((3300, 3340), 'nearpy.hashes.RandomBinaryProjections', 'RandomBinaryProjections', (['"""rbp_perm2"""', '(14)'], {}), "('rbp_perm2', 14)\n", (3323, 3340), False, 'from nearpy.hashes import RandomBinaryProjections, HashPermutations, HashPermutationMapper\n'), ((3592, 3618), 'numpy.zeros', 'numpy.zeros', (['(POINTS, DIM)'], {}), '((POINTS, DIM))\n', (3603, 3618), False, 'import numpy\n'), ((3751, 3762), 'time.time', 'time.time', ([], {}), '()\n', (3760, 3762), False, 'import time\n'), ((3854, 3877), 'numpy.random.randn', 'numpy.random.randn', (['DIM'], {}), '(DIM)\n', (3872, 3877), False, 'import numpy\n'), ((4522, 4533), 'time.time', 'time.time', ([], {}), '()\n', (4531, 4533), False, 'import time\n'), ((4787, 4813), 'numpy.zeros', 'numpy.zeros', (['(POINTS, DIM)'], {}), '((POINTS, DIM))\n', (4798, 4813), False, 'import numpy\n'), ((4945, 4956), 'time.time', 'time.time', ([], {}), '()\n', (4954, 4956), False, 'import time\n'), ((5048, 5071), 'numpy.random.randn', 'numpy.random.randn', (['DIM'], {}), '(DIM)\n', (5066, 5071), False, 'import numpy\n'), ((2212, 2235), 'numpy.random.randn', 'numpy.random.randn', (['DIM'], {}), '(DIM)\n', (2230, 2235), False, 'import numpy\n'), ((3658, 3681), 'numpy.random.randn', 'numpy.random.randn', (['DIM'], {}), '(DIM)\n', (3676, 3681), False, 'import numpy\n'), ((4853, 4876), 'numpy.random.randn', 'numpy.random.randn', (['DIM'], {}), '(DIM)\n', (4871, 4876), False, 'import numpy\n'), ((2076, 2092), 'nearpy.distances.CosineDistance', 'CosineDistance', ([], {}), '()\n', (2090, 2092), False, 'from nearpy.distances import CosineDistance\n'), ((2881, 2897), 'nearpy.distances.CosineDistance', 'CosineDistance', ([], {}), '()\n', (2895, 2897), False, 'from nearpy.distances import CosineDistance\n'), ((3522, 3538), 'nearpy.distances.CosineDistance', 'CosineDistance', ([], {}), '()\n', (3536, 3538), False, 'from nearpy.distances import CosineDistance\n'), ((4261, 4277), 'nearpy.distances.CosineDistance', 'CosineDistance', ([], {}), '()\n', (4275, 4277), False, 'from nearpy.distances import CosineDistance\n'), ((4597, 4638), 'nearpy.hashes.RandomBinaryProjections', 'RandomBinaryProjections', (["('rbp_%d' % k)", '(10)'], {}), "('rbp_%d' % k, 10)\n", (4620, 4638), False, 'from nearpy.hashes import RandomBinaryProjections, HashPermutations, HashPermutationMapper\n'), ((4717, 4733), 'nearpy.distances.CosineDistance', 'CosineDistance', ([], {}), '()\n', (4731, 4733), False, 'from nearpy.distances import CosineDistance\n'), ((5454, 5470), 'nearpy.distances.CosineDistance', 'CosineDistance', ([], {}), '()\n', (5468, 5470), False, 'from nearpy.distances import CosineDistance\n')]