def simple_reduction(puzzle):
  """
  simple_reduction returns a solution to <puzzle>.

  It works by reducing the number of greenhouses one by one until it has the
  lowest cost and meets the max constraint.
  """
  max, field = puzzle

  # figure out the current number of greenhouses
  greenhouses = common.ids(field)

  # we need to keep a copy of the previous field and it's cost in order
  # to return it once we've realized we've done one reduction too many
  prev_field, prev_cost = None, sys.maxint
  if len(greenhouses) <= max:
    prev_field, prev_cost = copy.deepcopy(field), common.cost(field)

  # join greenhouses until when run out of them or until max constraint
  # is met *and* cost increases from one reduction to the next
  while len(greenhouses) > 1:
    j1, j2, js = 0, 0, sys.maxint
    # try each combination of greenhouses
    for g1, g2 in itertools.combinations(greenhouses, 2):
      # find outer bounds (left, right, top and bottom) for a greenhouse made
      # up of g1 and g2
      size3, p31, p32 = common.outer_bounds([g1, g2], field)

      if size3 is not None:
        size1, p11, p12 = common.outer_bounds(g1, field)
        size2, p21, p22 = common.outer_bounds(g2, field)

        diff = size3 - size2 - size1
        if diff < js:
          j1, j2, js = g1, g2, diff

    # if we run out of combinations to try
    # we must either surrender (return None)
    # or if len(greenhouses) <= max return
    # the best solution we have.
    if j1 == 0:
      if len(greenhouses) <= max:
        return max, prev_field
      else:
        return max, None

    # join j1 and j2, remove j2 from greenhouses
    field = common.join(j1, j2, field)
    greenhouses.remove(j2)

    # decide if we should exit this loop or keep on reducing
    curr_cost = common.cost(field)
    if len(greenhouses) < max:
      if prev_cost < curr_cost:
        return max, prev_field

    prev_field, prev_cost = copy.deepcopy(field), curr_cost

  # if we end up here, we've come down to 1 greenhouse
  return max, field
Exemple #2
0
def find_template_exhaustive(test, ref):
    st = time.time()
    then = st
    d_thresh = ref.shape[0] * ref.shape[1] * 0.001
    #Each pixel was an integer from 0 to 255 before we normalized them. So minimum difference between
    #two pixels is 1/255 = 0.0039.. becomes 0.000015... if squared. And we are allowing average squared difference
    #of 0.001

    print('Acceptable cost value:', d_thresh)
    coord = (0, 0)
    d = float('inf')
    max_h, max_w = test.shape[0] - ref.shape[0], test.shape[1] - ref.shape[1]

    after_print = 0
    for i in range(0, max_h + 1):  # from 0 to test_h - ref_h
        #log---------
        if after_print > 1000:
            now = time.time()
            if now - then > 1:
                print('Scanning in row', i, '... d=', d)
                then = now
                after_print = 0
        ###logend-----

        for j in range(0, max_w + 1):  # from 0 to test_w - ref_w
            d_cur = cost(test, ref, i, j)
            if d_cur < d:
                d = d_cur
                coord = (j, i)
                if d < d_thresh:
                    return coord, False, time.time() - st
        after_print += max_w

    return coord, True, time.time() - st
Exemple #3
0
def heirarchical_recursive(test, ref, level):
    if level == 0:
        coord, _, _ = find_template_exhaustive(test/255.0, ref/255.0)
        return coord

    test2 = cv2.resize(test, None, fx=0.5, fy=0.5)
    ref2 = cv2.resize(ref, None, fx=0.5, fy=0.5)
    coord2 = heirarchical_recursive(test2, ref2, level - 1)
    coord = center = (coord2[0]*2, coord2[1]*2)
    #print(coord)
    search_area = (0, 0, test.shape[1] - ref.shape[1] - 1, test.shape[0] - ref.shape[0] - 1)  # top left and bottom right

    d = float('inf')
    for dx in [0, 1, -1]:
        for dy in [0, 1, -1]:
            j, i = center[0] + dx , center[1] + dy
            if j < search_area[0] or j > search_area[2] or i < search_area[1] or i > search_area[3]:
                continue
            # print(j, i, dwidth, dheight, d)
            d_cur = cost(test/255.0, ref/255.0, i, j)
            if d_cur < d:
                d = d_cur
                coord = (j, i)

    return coord
def solve(filename):
  """
  solve prints out solutions to each of the fields described in <filename>.
  """
  tstart = time.time()
  count, total = 0, 0
  for puzzle in common.parse_file(filename):
    pstart = time.time()
    max, field = variant_reduction(s0.join_vertically(s0.join_horizontally(s0.identify(puzzle))))

    count += 1
    total += common.cost(field)
    print "cost:", common.cost(field), "time:", time.strftime("%H:%M:%S", time.gmtime(time.time() - pstart))
    print common.format(field)

  print "%s field(s). Total cost is $%s" % (count, total)
  print time.strftime("Total time is %H:%M:%S", time.gmtime(time.time() - tstart))
def variant_reduction(puzzle):
  """
  variant_reduction flips and rotates <puzzle> to find a better solution.
  """
  max, field = puzzle
  
  # there are 8 variations to each puzzle, these 4 + 4 rotated ones
  variants = [lambda f: f, lambda f: flip_hz(f), lambda f: flip_vt(f), lambda f: flip_vt(flip_hz(f))]
  rotated_field = rotate(copy.deepcopy(field))

  best_cost, best_field = sys.maxint, None
  for vi in xrange(4):
    _, _field = simple_reduction((max, variants[vi](copy.deepcopy(field))))
    if common.cost(_field) < best_cost:
      best_cost, best_field = common.cost(_field), variants[vi](_field)

    _, _field = simple_reduction((max, variants[vi](copy.deepcopy(rotated_field))))
    if common.cost(_field) < best_cost:
      best_cost, best_field = common.cost(_field), unrotate(variants[vi](_field))

  return max, best_field
Exemple #6
0
def logsearch(test, ref, search_area):
    d_thresh = ref.shape[0] * ref.shape[1] * 0.001
    # Each pixel was an integer from 0 to 255 before we normalized them. So minimum difference between
    # two pixels is 1/255 = 0.0039.. becomes 0.000015... if squared. And we are allowing average squared difference
    # of 0.001
    print('Acceptable cost value:', d_thresh)
    #print(search_area)

    # search width = p, height = q
    p = search_area[2] - search_area[0]
    q = search_area[3] - search_area[1]
    dwidth = 2**(math.ceil(math.log2(p / 2)) - 1)
    dheight = 2**(math.ceil(math.log2(q / 2)) - 1)
    coord = center = (p // 2, q // 2)
    # print(dwidth, dheight)

    d = cost(test, ref, center[1], center[0])
    while dwidth >= 1 and dheight >= 1:
        for dx in [0, 1, -1]:
            for dy in [0, 1, -1]:

                j, i = center[0] + dx * dwidth, center[1] + dy * dheight
                if (dx == 0
                        and dy == 0) or j < search_area[0] or j > search_area[
                            2] or i < search_area[1] or i > search_area[3]:
                    continue
                #print(j, i, dwidth, dheight, d)
                d_cur = cost(test, ref, i, j)
                if d_cur < d:
                    d = d_cur
                    coord = (j, i)
                    if d < d_thresh:
                        return coord, False
        dwidth //= 2
        dheight //= 2
        center = coord

    return coord, True
def simplified_bruteForce(R, ax=None, aCap=0.20, beta=1.5, anim=False):
	"""
	a simplified algorithm. Still a little bit complex dock, but this is as simple as it gets I think.

	Does not add any edges, only removal. No stochastic elements are involved.

	anim=True creates a movie.
	"""
	R.beta=beta	
	inf = 1e15
	eps=1e-9
	lastAdded=None
	origin=R.origin
	for e in R.edges(data=True): assert e[2]['weight']>=0
	if not origin: raise Exception('need info about origin')

	#now, start for real and save away all kind of info about the paths.
	for node in R.nodes(data=True):		
		p1,p2=get_shortest_and_second(R,node)
		node[1]['shortest_path']=p1
		if len(p1)==0: #origin
			assert node[0]==origin
			node[1]['second_shortest']=[]
		else:
			node[1]['second_shortest']=p2
		for p in p1,p2: #now, update edge info for all visited edges
			for edge in R.edges_from_path_gen(p):
				d=R.get_edge_data(*edge)
				if not node in d['visited_from_node']: d['visited_from_node'].append(node) #in order to later see...

	remList=[]
	for e in R.edges(data=False): #add to remove list and calc. costs
		e_data=R.get_edge_data(*e)
		e_data['c']=cost(R, e, storeData=True)
		remList.append(e)
	remList=sortRemList(R,remList)

	while len(remList)>0: #the loop where edges are removed..

		"""
		now, choose the edge with the lowest cost.
		We have a problem since the cost saved is not necessary updated to the correct value.
		Actually, the only way to update it correctly is to scan through all edges for every removal. One could think that it would be possible to only update the ones affected by the removal, i.e. the ones connected by the shortest roads. The problem is that we also need to account for the ones that would take this road if some other arbitrary road was removed. Storing that variable would be possible but very memory inefficient. It could be tried and developed further, but would only be necessary if we add edges because the below alg. works pretty good.
		
		We use the fact that the cost from removing edges can only be bigger, i.e when removing edges it only gets worse.
		
		Thus, if the updated cost is still the smallest in the list, we know that this road is cheapest to remove.
		"""
		print "start"
		if anim: #movietime
			R.movieFlush()
		while True: #two possibilities to break out below..
			e=remList[0]
			e_data=R.get_edge_data(*e)
			e_data['c']=cost(R,e,storeData=False)
			if len(remList)==1:
				break
			#now, look for the other one..
			e2=remList[1]
			e2_data=R.get_edge_data(*e2)
			if e_data['c']<=e2_data['c']:
				break #e is our candidate, we know it's best
			e2_data['c']=cost(R, e2, storeData=False) #update, can only get worse
			for e in e,e2: #check for infinite costs, remove in that case.
				d=R.get_edge_data(*e) #this c has just been updated.
				if d['c']>=inf:
					remList.remove(e)
			remList=sortRemList(R,remList) #sort it, try again..
		if e_data['c']>=inf: break #if last in remList and infinite cost..
		remList.remove(e)
		e_data['c']=cost(R,e,storeData=True) #in order to store the new path..
		assert e_data['c']<inf
		#we are outside... will we go over the areaLimit if we remove e?
		if e_data['c']>eps and R.areaCover-go.singleRoadSegmentCoverage(e, R, remove=True)*R.Ainv<aCap:
			assert abs(R.areaCover-go.roadAreaCoverage(R))<eps #compare internal and actual.
			break #we are finished
		assert R.degree(e[0])>2 and R.degree(e[1])>2 #cost func should have given c=inf.
		print "removes edge ",e, e_data['c']
		remove_edge(e, R) #remove from R.
	if anim:
		R.movieFlush(final=True)
	R.cost=cf.totalCost(R)
	print "construction finished."
	print "road area coverage:", R.areaCover
	print "total area:", R.A
	print "total cost:", R.cost
	return R
def search(puzzle, breakpoint = 2):
  """
  search produces a solution to <puzzle>.

  >>> solve("p3.text") # doctest: +ELLIPSIS
  71
  ...
  """
  max, field = puzzle

  solution = (common.cost(field), field)

  paths = [solution, ]
  while len(paths) > 0:
    curr_cost, field = paths.pop(0)

    # Figure out the current number of greenhouses
    greenhouses = common.ids(field)

    if len(greenhouses) > 1:
      diffs = {}
      # Try each combination of greenhouses
      for g1, g2 in itertools.combinations(greenhouses, 2):
        # Find outer bounds (left, right, top and bottom) for a greenhouse made
        # up of g1 and g2
        size3, p31, p32 = common.outer_bounds([g1, g2], field)

        if size3 is not None:
          size1, p11, p12 = common.outer_bounds(g1, field)
          size2, p21, p22 = common.outer_bounds(g2, field)

          diff = size3 - size2 - size1
          if diff not in diffs.keys():
            diffs[diff] = [(g1, g2),]
          else:
            diffs[diff].append((g1, g2))

      # Find the list of joins which has the lowest diff and select the joins
      # of the most frequent greenhouse.
      if len(diffs.keys()) > 0:
        freqs = {}
        for (g1, g2) in diffs[sorted(diffs.keys())[0]]:
          if g1 not in freqs.keys():
            freqs[g1] = [(g1, g2),]
          else:
            freqs[g1].append((g1, g2))

          if g2 not in freqs.keys():
            freqs[g2] = [(g1, g2),]
          else:
            freqs[g2].append((g1, g2))

        # Perform each join in a fresh copy of field and add it to paths if
        # cost is lower than current cost, otherwise compare cost to solution
        # and either discard this path or add it as best-so-far.
        joins = freqs[sorted(freqs.keys(), key = lambda k: len(freqs[k]), reverse = True)[0]]
        if len(joins) <= breakpoint:
          (g1, g2) = joins[0]
          _, _field = s1.simple_reduction((max, common.join(g1, g2, copy.deepcopy(field))))

          cf = common.cost(_field)
          if cf < solution[0] and \
             len(common.ids(_field)) <= max:

            solution = (cf, _field)

        else:
          for (g1, g2) in joins:
            _field = common.join(g1, g2, copy.deepcopy(field))
            cf = common.cost(_field)
            if cf < curr_cost:
              paths.append((cf, _field))

              if cf < solution[0] and \
                 len(common.ids(_field)) <= max:

                solution = (cf, _field)

  return max, solution[1]
Exemple #9
0
def stochastic(R, ax=None, aCap=0.20, beta=1.5, anim=False, probListGen=None):
	"""
	a simplified algorithm. Still a little bit complex dock, but this is as simple as it gets I think.

	Does not add any edges, only removal. No stochastic elements are involved.

	anim=True creates a movie.

	probListGen can be given. That is a class of type ProbListGen that defines a specific
	distribution that is used by the stochastic parts.
	"""
	if probListGen==None: #use default, 0.5^i
		probListGen=ProbListGen(0.5,15)
	R.beta=beta	
	inf = 1e15
	eps=1e-9
	choiceMax=15 #we will not randomly choose something bigger than this.
	lastAdded=None
	origin=R.origin
	for e in R.edges(data=True): assert e[2]['weight']>=0
	if not origin: raise Exception('need info about origin')

	#now, start for real and save away all kind of info about the paths.
	for node in R.nodes(data=True):		
		p1,p2=get_shortest_and_second(R,node)
		node[1]['shortest_path']=p1
		if len(p1)==0: #origin
			assert node[0]==origin
			node[1]['second_shortest']=[]
		else:
			node[1]['second_shortest']=p2
		for p in p1,p2: #now, update edge info for all visited edges
			for edge in R.edges_from_path_gen(p):
				d=R.get_edge_data(*edge)
				if not node in d['visited_from_node']:
					d['visited_from_node'].append(node) #in order to later see...

	remList=[]
	for e in R.edges(data=False): #add to remove list and calc. costs
		e_data=R.get_edge_data(*e)
		e_data['c']=cost(R, e, storeData=True)
		remList.append(e)
	remList=sortRemList(R,remList)
	choices=[0]*15 #for statistics areound the stochastics.
	while len(remList)>0: #the loop where edges are removed..

		"""
		We have a problem since the cost saved is not necessary updated to the correct value.
		Actually, the only way to update it correctly is to scan through all edges for every removal. One could think that it would be possible to only update the ones affected by the removal, i.e. the ones connected by the shortest roads. The problem is that we also need to account for the ones that would take this road if some other arbitrary road was removed. Storing that variable would be possible but very memory inefficient. It could be tried and developed further, but would only be necessary if we add edges because the below alg. works pretty good.
		
		We use the fact that the cost from removing edges can only be bigger, i.e when removing edges it only gets worse.
		
		Thus, if the updated cost is still the smallest in the list, we know that this road is cheapest to remove.
		"""
		print "start"
		probList=probListGen.getList(N=len(remList))
		if anim: #it's showtime..
			R.movieFlush()
		r=random.uniform(0,1)
		choice=choiceMax
		for i,p in enumerate(probList): #time to make the choice..
			if r<p:
				choice=i #usually 0.. 50% prob of that.
				break
		updated=[] #store edges that we have updated the cost for..
		done=False
		while not done: #two possibilities to break out below..
			done=True
			for i in range(choice+2): #loop over all necessary edges
				try:
					e=remList[i]
				except IndexError: #remList is too short.. we are done here...
					done=True
					break #breaks out of for-loop, not while loop
				e_data=R.get_edge_data(*e)
				if not e in updated: #saves us some calculations
					done=False #have to iterate once more.
					e_data['c']=cost(R,e,storeData=False)
					updated.append(e)
					if e_data['c']>=inf:
						remList.remove(e)
					break #otherwise we "jump over" one in the list. new for loop
				if e_data['c']>=inf:
					remList.remove(e)
				remList=sortRemList(R,remList) #sort it..
			remList=sortRemList(R,remList) #last time.. may be a double sort but who cares?
		#sorting remList and updating cost procedure is now done.
		if len(remList)==0: break #we are done
		if choice>=len(remList): #may happen if edges have been removed due to inf. cost
			choice=int(floor(random.uniform(0,len(remList)))) #take a random one..
			print "choice:", choice
		e=remList[choice]
		e_data=R.get_edge_data(*e)
		remList.remove(e)
		e_data['c']=cost(R,e,storeData=True) #in order to store the new path..
		assert e_data['c']<inf
		#following lines just to check, not used... remove later..
		if __debug__ and choice != -1 and len(remList)>choice+1: 
			e2=remList[choice+1]
			e2_data=R.get_edge_data(*e2)
			assert e_data['c']<=e2_data['c']

		#we are outside... will we go over the areaLimit if we remove e?
		if e_data['c']>eps and R.areaCover-go.singleRoadSegmentCoverage(e, R, remove=True)*R.Ainv<aCap:
			assert abs(R.areaCover-go.roadAreaCoverage(R))<eps #compare internal and actual.
			break #we are finished
		assert R.degree(e[0])>2 and R.degree(e[1])>2 #cost func should have given c=inf.
		print "removes edge ",e, e_data['c']
		remove_edge(e, R) #remove from R.
		choices[choice]+=1
	if anim:
		R.movieFlush(final=True)
	print "construction finished."
	print "road area coverage:", R.areaCover
	print "total area:", R.A
	print "choices:", choices
	R.cost=cf.totalCost(R)
	print "total cost:", R.cost
	return R