How to perform Partial-mapped crossover in python3? - python

I am new to genetic algorithms and made one the other day that recreated a target string. So I tried to make one that could make a Magic Square. It was ok until I got to the crossover part, realising I couldn't just do a single point crossover. So I attempted to perform a Partially Mapped Crossover, and I could not and still can't get it to work. I understand how the Partially Mapped Crossover works I just can't implement it into python. Since my code isn't complete yet I isolated the crossover function in a different program and changed it so the parents were a fixed list.
Can someone please correct my code or if it is completely wrong show me how to perform a Partial Mapped Crossover on 2 lists with integers 1 to 9?
Also, I am sorry and understand that my naming of variables isn't that good but I was just trying to get the program to work making constant edits.
import random
parent1 = [1,2,3,4,5,6,7,8,9]
parent2 = [5,4,6,7,2,1,3,9,8]
firstCrossPoint = random.randint(0,len(parent1)-1) #Creating parameters for random sublist
secondCrossPoint = random.randint(firstCrossPoint+1,len(parent1))
parent1MiddleCross = parent1[firstCrossPoint:secondCrossPoint]
parent2MiddleCross = parent2[firstCrossPoint:secondCrossPoint]
child1 = (parent1[:firstCrossPoint] + parent2MiddleCross + parent1[secondCrossPoint:])
child2 = (parent2[:firstCrossPoint] + parent1MiddleCross + parent2[secondCrossPoint:])
relationsWithDupes = []
for i in range(len(parent1MiddleCross)):
relationsWithDupes.append([parent2MiddleCross[i], parent1MiddleCross[i]])
relations = []
for pair in relationsWithDupes:
for i in range(len(relationsWithDupes)):
if pair[0] in relationsWithDupes[i] or pair[1] in relationsWithDupes[i]:
if pair != relationsWithDupes[i]:
if pair[0] == relationsWithDupes[i][1]:
pair[0] = relationsWithDupes[i][0]
else:
pair[1] = relationsWithDupes[i][1]
if pair not in relations and pair[::-1] not in relations:
relations.append(pair)
for i in child1[:firstCrossPoint]:
for x in relations:
if i == x[0]:
i = x[1]
for i in child1[secondCrossPoint:]:
for x in relations:
if i == x[0]:
i = x[1]
for i in child2[:firstCrossPoint]:
for x in relations:
if i == x[1]:
i = x[0]
for i in child2[secondCrossPoint:]:
for x in relations:
if i == x[1]:
i = x[0]
print(child1)
print(child2)

import numpy as np
parent1 = [1,2,3,4,5,6,7,8,9]
parent2 = [5,4,6,7,2,1,3,9,8]
firstCrossPoint = np.random.randint(0,len(parent1)-2)
secondCrossPoint = np.random.randint(firstCrossPoint+1,len(parent1)-1)
print(firstCrossPoint, secondCrossPoint)
parent1MiddleCross = parent1[firstCrossPoint:secondCrossPoint]
parent2MiddleCross = parent2[firstCrossPoint:secondCrossPoint]
temp_child1 = parent1[:firstCrossPoint] + parent2MiddleCross + parent1[secondCrossPoint:]
temp_child2 = parent2[:firstCrossPoint] + parent1MiddleCross + parent2[secondCrossPoint:]
relations = []
for i in range(len(parent1MiddleCross)):
relations.append([parent2MiddleCross[i], parent1MiddleCross[i]])
print(relations)
def recursion1 (temp_child , firstCrossPoint , secondCrossPoint , parent1MiddleCross , parent2MiddleCross) :
child = np.array([0 for i in range(len(parent1))])
for i,j in enumerate(temp_child[:firstCrossPoint]):
c=0
for x in relations:
if j == x[0]:
child[i]=x[1]
c=1
break
if c==0:
child[i]=j
j=0
for i in range(firstCrossPoint,secondCrossPoint):
child[i]=parent2MiddleCross[j]
j+=1
for i,j in enumerate(temp_child[secondCrossPoint:]):
c=0
for x in relations:
if j == x[0]:
child[i+secondCrossPoint]=x[1]
c=1
break
if c==0:
child[i+secondCrossPoint]=j
child_unique=np.unique(child)
if len(child)>len(child_unique):
child=recursion1(child,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross)
return(child)
def recursion2(temp_child,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross):
child = np.array([0 for i in range(len(parent1))])
for i,j in enumerate(temp_child[:firstCrossPoint]):
c=0
for x in relations:
if j == x[1]:
child[i]=x[0]
c=1
break
if c==0:
child[i]=j
j=0
for i in range(firstCrossPoint,secondCrossPoint):
child[i]=parent1MiddleCross[j]
j+=1
for i,j in enumerate(temp_child[secondCrossPoint:]):
c=0
for x in relations:
if j == x[1]:
child[i+secondCrossPoint]=x[0]
c=1
break
if c==0:
child[i+secondCrossPoint]=j
child_unique=np.unique(child)
if len(child)>len(child_unique):
child=recursion2(child,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross)
return(child)
child1=recursion1(temp_child1,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross)
child2=recursion2(temp_child2,firstCrossPoint,secondCrossPoint,parent1MiddleCross,parent2MiddleCross)
print(child1)
print(child2)

I just stumbled across this when looking for an implementation of PMX and it seems unnecessarily complicated? I've included below an alternative I've just coded if anyone comes across the same issue.
def PMX_crossover(parent1, parent2, seed):
'''
parent1 and parent2 are 1D np.array
'''
rng = np.random.default_rng(seed=seed)
cutoff_1, cutoff_2 = np.sort(rng.choice(np.arange(len(parent1)+1), size=2, replace=False))
def PMX_one_offspring(p1, p2):
offspring = np.zeros(len(p1), dtype=p1.dtype)
# Copy the mapping section (middle) from parent1
offspring[cutoff_1:cutoff_2] = p1[cutoff_1:cutoff_2]
# copy the rest from parent2 (provided it's not already there
for i in np.concatenate([np.arange(0,cutoff_1), np.arange(cutoff_2,len(p1))]):
candidate = p2[i]
while candidate in p1[cutoff_1:cutoff_2]: # allows for several successive mappings
print(f"Candidate {candidate} not valid in position {i}") # DEBUGONLY
candidate = p2[np.where(p1 == candidate)[0][0]]
offspring[i] = candidate
return offspring
offspring1 = PMX_one_offspring(parent1, parent2)
offspring2 = PMX_one_offspring(parent2, parent1)
return offspring1, offspring2

Related

Most frequently overlapping range - Python3.x

I'm a beginner, trying to write code listing the most frequently overlapping ranges in a list of ranges.
So, input is various ranges (#1 through #7 in the example figure; https://prntscr.com/kj80xl) and I would like to find the most common range (in the example 3,000- 4,000 in 6 out of 7 - 86 %). Actually, I would like to find top 5 most frequent.
Not all ranges overlap. Ranges are always positive and given as integers with 1 distance (standard range).
What I have now is only code comparing one sequence to another and returning the overlap, but after that I'm stuck.
def range_overlap(range_x,range_y):
x = (range_x[0], (range_x[-1])+1)
y = (range_y[0], (range_y[-1])+1)
overlap = (max(x[0],y[0]),min(x[-1],(y[-1])))
if overlap[0] <= overlap[1]:
return range(overlap[0], overlap[1])
else:
return "Out of range"
I would be very grateful for any help.
Better solution
I came up with a simpler solution (at least IMHO) so here it is:
def get_abs_min(ranges):
return min([min(r) for r in ranges])
def get_abs_max(ranges):
return max([max(r) for r in ranges])
def count_appearances(i, ranges):
return sum([1 for r in ranges if i in r])
def create_histogram(ranges):
keys = [str(i) for i in range(len(ranges) + 1)]
histogram = dict.fromkeys(keys)
results = []
min = get_abs_min(range_list)
max = get_abs_max(range_list)
for i in range(min, max):
count = str(count_appearances(i, ranges))
if histogram[count] is None:
histogram[count] = dict(start=i, end=None)
elif histogram[count]['end'] is None:
histogram[count]['end'] = i
elif histogram[count]['end'] == i - 1:
histogram[count]['end'] = i
else:
start = histogram[count]['start']
end = histogram[count]['end']
results.append((range(start, end + 1), count))
histogram[count]['start'] = i
histogram[count]['end'] = None
for count, d in histogram.items():
if d is not None and d['start'] is not None and d['end'] is not None:
results.append((range(d['start'], d['end'] + 1), count))
return results
def main(ranges, top):
appearances = create_histogram(ranges)
return sorted(appearances, key=lambda t: t[1], reverse=True)[:top]
The idea here is as simple as iterating through a superposition of all the ranges and building a histogram of appearances (e.g. the number of original ranges this current i appears in)
After that just sort and slice according to the chosen size of the results.
Just call main with the ranges and the top number you want (or None if you want to see all results).
OLDER EDITS BELOW
I (almost) agree with #Kasramvd's answer.
here is my take on it:
from collections import Counter
from itertools import combinations
def range_overlap(x, y):
common_part = list(set(x) & set(y))
if common_part:
return range(common_part[0], common_part[-1] +1)
else:
return False
def get_most_common(range_list, top_frequent):
overlaps = Counter(range_overlap(i, j) for i, j in
combinations(list_of_ranges, 2))
return [(r, i) for (r, i) in overlaps.most_common(top_frequent) if r]
you need to input the range_list and the number of top_frequent you want.
EDIT
the previous answer solved this question for all 2's combinations over the range list.
This edit is tested against your input and results with the correct answer:
from collections import Counter
from itertools import combinations
def range_overlap(*args):
sets = [set(r) for r in args]
common_part = list(set(args[0]).intersection(*sets))
if common_part:
return range(common_part[0], common_part[-1] +1)
else:
return False
def get_all_possible_combinations(range_list):
all_combos = []
for i in range(2, len(range_list)):
all_combos.append(combinations(range_list, i))
all_combos = [list(combo) for combo in all_combos]
return all_combos
def get_most_common_for_combo(combo):
return list(filter(None, [range_overlap(*option) for option in combo]))
def get_most_common(range_list, top_frequent):
all_overlaps = []
combos = get_all_possible_combinations(range_list)
for combo in combos:
all_overlaps.extend(get_most_common_for_combo(combo))
return [r for (r, i) in Counter(all_overlaps).most_common(top_frequent) if r]
And to get the results just run get_most_common(range_list, top_frequent)
Tested on my machine (ubunut 16.04 with python 3.5.2) with your input range_list and top_frequent = 5 with the results:
[range(3000, 4000), range(2500, 4000), range(1500, 4000), range(3000, 6000), range(1, 4000)]
You can first change your function to return a valid range in both cases so that you can use it in a set of comparisons. Also, since Python's range objects are not already created iterables but smart objects that only get start, stop and step attributes of a range and create the range on-demand, you can do a little change on your function as well.
def range_overlap(range_x,range_y):
rng = range(max(range_x.start, range_y.start),
min(range_x.stop, range_y.stop)+1)
if rng.start < rng.stop:
return rng.start, rng.stop
Now, if you have a set of ranges and you want to compare all the pairs you can use itertools.combinations to get all the pairs and then using range_overlap and collections.Counter you can find the number of overlapped ranges.
from collections import Counter
from itertools import combinations
overlaps = Counter(range_overlap(i,j) for i, j in
combinations(list_of_ranges, 2))

Extracting elements from frozenset

I've been trying to develop an apriori algorithm using this data. I was able to get the associations and the confidence for both the pairs and the triples but am having trouble formatting the output and extracting the correct elements.
I ran the algorithm on this test data. Its just a subset of the original dataset. Currently the output looks like this:
[[frozenset({'GRO73461'}), frozenset({'ELE17451'}), 1.0],
[frozenset({'GRO99222'}), frozenset({'ELE17451'}), 0.8125], [frozenset({'ELE17451'}), frozenset({'GRO99222'}), 0.5], [frozenset({'ELE17451'}), frozenset({'GRO73461'}), 0.38461538461538464]]
frozenset({'GRO73461', 'ELE17451'}), 0.8], [frozenset({'GRO73461'}), frozenset({'DAI22896', 'ELE17451'}), 0.8]
As you can see its kind of a mess. The list is ordered based on the confidence in descending order. I want to separate the frequent pairs from the frequent triples and arrange the output so that it looks like this:
OUTPUT A
FRO11987 FRO12685 0.4325
FRO11987 ELE11375 0.4225
FRO11987 GRO94758 0.4125
FRO11987 SNA80192 0.4025
FRO11987 FRO18919 0.4015
OUTPUT B
FRO11987 FRO12685 DAI95741 0.4325
FRO11987 ELE11375 GRO73461 0.4225
FRO11987 GRO94758 ELE26917 0.4125
FRO11987 SNA80192 ELE28189 0.4025
FRO11987 FRO18919 GRO68850 0.4015
Where the above is the top 5 frequent pairs, and top 5 frequent triples based on the confidence.
The main area I'm having trouble with is discerning between the frequent pairs and triples and then extracting the items from the frozensets such that they are in the above format.
from numpy import *
import pandas as pd
from operator import itemgetter
def loadDataSet(data=None):
return pd.read_csv(data, sep = ' ', error_bad_lines=False)
def createCandidateSet(data):
C1 = []
for transaction in data:
for item in transaction:
if not [item] in C1:
C1.append([item])
C1.sort()
return list(map(frozenset, C1))
def scanData(dataset, Ck, support):
ssCount = {}
for tID in dataset:
for candidate in Ck:
if candidate.issubset(tID):
if not candidate in ssCount:
ssCount[candidate] = 1
else:
ssCount[candidate]+=1
# numItems = float(len(dataset))
res = []
supportData ={}
for key in ssCount:
#Support is a proportion or a integer; the occurrence of the item in relation to the data set
# currSupport = ssCount[key]/numItems
currSupport = ssCount[key]
if currSupport >= support:
res.insert(0, key)
supportData[key] = currSupport
return res, supportData
def aprioriHelper(Lk, k): #creates candidate itemsets
res = []
freqItemLen = len(Lk)
for i in range(freqItemLen):
for j in range(i+1, freqItemLen):
L1 = list(Lk[i])[:k-2]
L2 = list(Lk[j])[:k-2]
L1.sort()
L2.sort()
if L1 == L2:
res.append(Lk[i] | Lk[j])
return res
def apriori(dataset, minSupport=100):
C1 = createCandidateSet(dataset)
D = list(map(set, dataset))
L1, supportData = scanData(D, C1, minSupport)
L = [L1]
k = 2
while (len(L[k-2]) > 0):
Ck = aprioriHelper(L[k-2], k)
Lk, supportK = scanData(D, Ck, minSupport) #scan dataset to get frequent items sets, now the itemsets are bigger
supportData.update(supportK)
L.append(Lk)
k+=1
return L, supportData
def generateRules(L, supportData, conf = 0.7): #support data is data on each item sets support, comes from scanData
rules = [] #takes tuples of associations, consequences, and confidence
for i in range(1, len(L)): #get itemsets with number of items >=2
for freq in L[i]:
association = [frozenset([item]) for item in freq]
if i > 1:
rulesFromConsequences(freq, association, supportData, rules, conf)
else:
calculateConfidence(freq, association, supportData, rules, conf)
return rules
def calculateConfidence(freq, association, supportData, rules, conf=0.7):
filteredAssociations = []
for consequence in association:
#confidence(I -> J) = Support(I U J)/Support(I)
confidence = supportData[freq]/supportData[freq - consequence] #calculate confidence
if confidence >= conf:
# print(freq-consequence, ' ', consequence, ' ', confidence) #print out association rule and confidence
rules.append((freq-consequence, consequence, confidence))
filteredAssociations.append(consequence)
return filteredAssociations
def rulesFromConsequences(freq, association, supportData, rules, conf=0.7):
#generate more rules when frequent itemsets become larger
a_len = len(association[0])
if (len(freq) > (a_len+1)): #try to merge into a bigger itemset that is frequent
association_p1 = aprioriHelper(association, a_len+1) #create association+1 new candidates- create bigger itemset and get more candidates for association rules
association_p1 = calculateConfidence(freq, association_p1, supportData, rules, conf)
if len(association_p1) > 1: #need to have at least two sets in order to merge
rulesFromConsequences(freq, association_p1, supportData, rules, conf) #recursively call to build bigger itemset and get more rules
def main():
dataset = [line.split() for line in open('datatest.txt')]
L, supportData = apriori(dataset, minSupport=8)
rules = generateRules(L, supportData, conf=0)
rules = sorted(rules, key = itemgetter(2), reverse=True)
triples = []
doubles = []
i = 0
while len(triples) < 5:
if i == len(rules):
break
if len(rules[i][1]) == 2:
triples.append(rules[i])
i+=1
j = 0
while len(doubles) < 5:
if j == len(rules):
break
if len(rules[j][1]) == 1:
doubles.append(rules[j])
j+=1
if __name__ == '__main__':
main()
Any advice on the issue is appreciated. If you have any questions on the code or thought process please let me know. Apologies in advance if there are any careless mistakes.
Thank you for reading

Can someone detect error in this code to implement dijkstra's algorithm using python?

I am trying to implement dijkstra's algorithm (on an undirected graph) to find the shortest path and my code is this.
Note: I am not using heap/priority queue or anything but an adjacency list, a dictionary to store weights and a bool list to avoid cycling in the loops/recursion forever. Also, the algorithm works for most test cases but fails for this particular one here: https://ideone.com/iBAT0q
Important : Graph can have multiple edges from v1 to v2 (or vice versa), you have to use the minimum weight.
import sys
sys.setrecursionlimit(10000)
def findMin(n):
for i in x[n]:
cost[n] = min(cost[n],cost[i]+w[(n,i)])
def dik(s):
for i in x[s]:
if done[i]:
findMin(i)
done[i] = False
dik(i)
return
q = int(input())
for _ in range(q):
n,e = map(int,input().split())
x = [[] for _ in range(n)]
done = [True]*n
w = {}
cost = [1000000000000000000]*n
for k in range(e):
i,j,c = map(int,input().split())
x[i-1].append(j-1)
x[j-1].append(i-1)
try: #Avoiding multiple edges
w[(i-1,j-1)] = min(c,w[(i-1,j-1)])
w[(j-1,i-1)] = w[(i-1,j-1)]
except:
try:
w[(i-1,j-1)] = min(c,w[(j-1,i-1)])
w[(j-1,i-1)] = w[(i-1,j-1)]
except:
w[(j-1,i-1)] = c
w[(i-1,j-1)] = c
src = int(input())-1
#for i in sorted(w.keys()):
# print(i,w[i])
done[src] = False
cost[src] = 0
dik(src) #First iteration assigns possible minimum to all nodes
done = [True]*n
dik(src) #Second iteration to ensure they are minimum
for val in cost:
if val == 1000000000000000000:
print(-1,end=' ')
continue
if val!=0:
print(val,end=' ')
print()
The optimum isn't always found in the second pass. If you add a third pass to your example, you get closer to the expected result and after the fourth iteration, you're there.
You could iterate until no more changes are made to the cost array:
done[src] = False
cost[src] = 0
dik(src)
while True:
ocost = list(cost) # copy for comparison
done = [True]*n
dik(src)
if cost == ocost:
break

Would a dictionary or list of tuples be more efficient for this particular task?

I wrote a script whose aim is to find the parameters best fitting a function by trial and error (a Markov Chain Monte Carlo). The viability of the parameters is judged by a chi value -- the lower the chi, the better.
Below is a section of my code as currently written:
chi1 = fun_chi(B1,G1,C11,C21,C31,C41,C01)
BGCchilist = []
count = -1
for i in range(iteration_MCMC):
count = count + 1
print(count)
B2,G2,C12,C22,C32,C42,C02 = gen_param(B1,G1,C11,C21,C31,C41,C01)
chi2 = fun_chi(B2,G2,C12,C22,C32,C42,C02)
ratio = np.exp((-chi2 + chi1) / 2)
rand = np.random.uniform(0,1)
if rand < ratio:
B1 = B2
G1 = G2
C11 = C12
C21 = C22
C31 = C32
C41 = C42
C01 = C02
chi1 = chi2
##save the data
Bsave = B1
Gsave = G1
C1save = C11
C2save = C21
C3save = C31
C4save = C41
C0save = C01
chisave = chi1
BGCchilist.append((Bsave,Gsave,C1save,C2save,C3save,C4save,C0save,chisave))
Blist = [x[0] for x in BGCchilist]
Glist = [x[1] for x in BGCchilist]
C1list = [x[2] for x in BGCchilist]
C2list = [x[3] for x in BGCchilist]
C3list = [x[4] for x in BGCchilist]
C4list = [x[5] for x in BGCchilist]
C0list = [x[6] for x in BGCchilist]
chilist = [x[7] for x in BGCchilist]
minchi = min(x[7] for x in BGCchilist)
mintuple = [x for x in BGCchilist if x[7] == minchi]
I ultimately individually graph all of those lists versus iteration via Matplotlib and then save mintuple as a text file.
A colleague took a look at this and told me that perhaps I would be better served by using a dictionary, where for every iteration I do something like a_dictionary[chisave] = (B,G,...C0) and then find mintuple by looking for the minimum value in the dictionary. However, it seems like extracting lists from the value tuples in the dictionary for graphing would be more unwieldy and take more steps.
Would the dictionary approach allow for fewer steps, greater efficiency, or is this all lists approach okay as is?
Thank you for your help.
This is making an answer out of the comments. I thought that the question about the efficiency but after closer examination, it looks like it is about how to clean up the code. If that is the confer, then the following might be useful:
For the first part, we just keep everything as a tuple, and use inlace expansion like so ...
par1 = B1,G1,C11,C21,C31,C41,C01
chi1 = fun_chi(*par1)
BGCchilist = []
for i in range(iteration_MCMC):
print (i-1)
par2 = gen_param(*par1)
chi2 = fun_chi(*par2)
ratio = np.exp((-chi2 + chi1) / 2)
rand = np.random.uniform(0,1)
if rand < ratio: par1 = par2
BGCchilist.append(list(par1) + [chi1]) ##save the data
This is a verbatim logical copy. However, I don't see chi1 being updated within the for loop. I am guessing that you want that to happen as well? Like so:
par1 = B1,G1,C11,C21,C31,C41,C01
BGCchilist = []
for i in range(iteration_MCMC):
print (i-1)
chi1 = fun_chi(*par1) # This is the change
par2 = gen_param(*par1)
chi2 = fun_chi(*par2)
if np.random.uniform(0,1) < np.exp((-chi2 + chi1) / 2): par1 = par2
BGCchilist.append(list(par1) + [chi1]) ##save the data
I am not sure about the algorithm, so cannot definitively comment on that part.
The next part is more interesting.
Blist = [x[0] for x in BGCchilist]
Glist = [x[1] for x in BGCchilist]
C1list = [x[2] for x in BGCchilist]
C2list = [x[3] for x in BGCchilist]
C3list = [x[4] for x in BGCchilist]
C0list = [x[6] for x in BGCchilist]
C4list = [x[5] for x in BGCchilist]
chilist = [x[7] for x in BGCchilist]
can be simply replaced by:
BList, GList, \
C1List, C2List, C3List \
C0List, C4List, chilist = zip(*BGCchilist)
I think this is what it does. Please check this part separately.
Finally, do you really need all the lists? Or do you just need the chiList ?
The rest should be easy I think? What you have is pretty ok. Check the key parameter in the min function. So if you dont want all the lists for some reason, you can simply skip that part and find the minimum within the for loop, or you can just do:
minChi = min( zip(*BGCchilist)[-1] )
allMinChis = filter( lambda m: m[7] == minChi, BGCchilist)
Cheeers!
N.B. I havent tested the code so there might be errors. Please go through the code and make sure that that is what you want.
A dict will be fewer steps, and be easier to read as well. Using minchi as the key:
BGCchilist = {}
# in the loop
BGCchilist[chisave] = [Bsave, Gsave, C1save,C2save,C3save,C4save,C0save,chisave]
# out of the loop
minchi = min(BGCchilist.keys())
mintuple = BGCchilist[minchi]
If you need to save each combination of parameters that result in the same chi value, change the dict to hold a list of tuples per key, instead of just a list:
from collections import defaultdict
BGCchilist = defaultdict(list)
...
BGCchilist[chisave].append([...all the saves...])
...
minchi = min(BGCchilist.keys())
mintuples = BGCchilist[minchi]
# at this point mintuples has at least one tuple of params that got
# this chi score, maybe more
Update:
Okay, so here's my understanding of your requirements:
you need to save each combination of parameters and the resulting chi score
you need to get the lowest chi score when done
you need a list of each input parameter, and the chi scores, when done
I would use a simple class for this. It might not be fewer lines, but it will be much more readable:
# lightly tested
class BGCChiData(object): # (object) not needed in Python3
def __init__(self):
self.data = defaultdict(list)
def __getitem__(self, chi):
return self.data[chi]
def __setitem__(self, chi, params):
self.data[chi].append(params)
#property
def min_chi(self):
return min(self.data.keys())
#property
def B(self):
return [tup[0] for val in self.data.values() for tup in val]
#property
def G(self):
return [tup[1] for val in self.data.values() for tup in val]
#property
def C1(self):
return [tup[2] for val in self.data.values() for tup in val]
#property
def C2(self):
return [tup[3] for val in self.data.values() for tup in val]
#property
def C3(self):
return [tup[4] for val in self.data.values() for tup in val]
#property
def C4(self):
return [tup[5] for val in self.data.values() for tup in val]
#property
def C0(self):
return [tup[6] for val in self.data.values() for tup in val]
#property
def chi(self):
return [tup[7] for val in self.data.values() for tup in val]
chi1 = fun_chi(B1,G1,C11,C21,C31,C41,C01)
BGCchi = BGCChiData()
for count in range(iteration_MCMC):
print(count)
B2,G2,C12,C22,C32,C42,C02 = gen_param(B1,G1,C11,C21,C31,C41,C01)
chi2 = fun_chi(B2,G2,C12,C22,C32,C42,C02)
ratio = np.exp((-chi2 + chi1) / 2)
rand = np.random.uniform(0,1)
if rand < ratio:
B1 = B2
G1 = G2
C11 = C12
C21 = C22
C31 = C32
C41 = C42
C01 = C02
chi1 = chi2
##save the data
BGCchi[chi1].append(B1, G1, C11, C21, C31, C41, C01)
minchi = BGCchi.min_chi
mintuple = BGCchi[minchi]
B1list = BGCchi.B
# etc. etc.

Beginner problems with references to arrays in python 3.1.1

As part of the last assignment in a beginner python programing class, I have been assigned a traveling sales man problem. I settled on a recursive function to find each permutation and the sum of the distances between the destinations, however, I am have a lot of problems with references. Arrays in different instances of the Permute and Main functions of TSP seem to be pointing to the same reference.
from math import sqrt
class TSP:
def __init__(self):
self.CartisianCoordinates = [['A',[1,1]], ['B',[2,2]], ['C',[2,1]], ['D',[1,2]], ['E',[3,3]]]
self.Array = []
self.Max = 0
self.StoredList = ['',0]
def Distance(self, i1, i2):
x1 = self.CartisianCoordinates[i1][1][0]
y1 = self.CartisianCoordinates[i1][1][1]
x2 = self.CartisianCoordinates[i2][1][0]
y2 = self.CartisianCoordinates[i2][1][1]
return sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2))
def Evaluate(self):
temparray = []
Data = []
for i in range(len(self.CartisianCoordinates)):
Data.append([])
for i1 in range(len(self.CartisianCoordinates)):
for i2 in range(len(self.CartisianCoordinates)):
if i1 != i2:
temparray.append(self.Distance(i1, i2))
else:
temparray.append('X')
Data[i1] = temparray
temparray = []
self.Array = Data
self.Max = len(Data)
def Permute(self,varray,index,vcarry,mcarry): #Problem Class
array = varray[:]
carry = vcarry[:]
for i in range(self.Max):
print ('ARRAY:', array)
print (index,i,carry,array[index][i])
if array[index][i] != 'X':
carry[0] += self.CartisianCoordinates[i][0]
carry[1] += array[index][i]
if len(carry) != self.Max:
temparray = array[:]
for j in range(self.Max):temparray[j][i] = 'X'
index = i
mcarry += self.Permute(temparray,index,carry,mcarry)
else:
return mcarry
print ('pass',mcarry)
return mcarry
def Main(self):
out = []
self.Evaluate()
for i in range(self.Max):
array = self.Array[:] #array appears to maintain the same reference after each copy, resulting in an incorrect array being passed to Permute after the first iteration.
print (self.Array[:])
for j in range(self.Max):array[j][i] = 'X'
print('I:', i, array)
out.append(self.Permute(array,i,[str(self.CartisianCoordinates[i][0]),0],[]))
return out
SalesPerson = TSP()
print(SalesPerson.Main())
It would be greatly appreciated if you could provide me with help in solving the reference problems I am having. Thank you.
Slicing a list (by using [:] as you do) does not create a deep copy -- it creates a shallow copy. That means that if the list contains references to other lists, the copy will contain the same references -- not references to new lists. Put another way, only the list itself is copied, not its elements or its elements' elements.
What you want here is a deep copy. Instead of
array = self.Array[:]
try
array = copy.deepcopy(self.Array)
for which you'll need import copy.

Categories