Skip to content

Commit a8685e1

Browse files
committed
3.20
1 parent 4acb50a commit a8685e1

2 files changed

Lines changed: 54 additions & 48 deletions

File tree

app.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ def index():
2323
daily_queue_size = int(request.form.get("daily_queue_size", 5))
2424
weight_reciprocal = float(request.form.get("weight_reciprocal", 1.0))
2525
weight_queue_penalty = float(request.form.get("weight_queue_penalty", 0.5))
26-
export_trace = request.form.get("export_trace") == "on"
27-
export_jack_jill_trace = request.form.get("export_jack_jill_trace") == "on"
26+
export_trace = request.form.get("export_trace") == "off"
27+
export_jack_jill_trace = request.form.get("export_jack_jill_trace") == "off"
2828
show_match_plots = request.form.get("show_match_plots") == "on"
2929
show_like_plots = request.form.get("show_like_plots") == "on"
3030
plot_type = request.form.get("plot_type", "Bar Chart")
@@ -206,13 +206,13 @@ def index():
206206

207207
elif plot_type == "Histogram":
208208
# Fixed bin labels for histogram plots.
209-
bin_labels = ["0", "1-2", "3-4", "5+"]
209+
bin_labels = ["0", "1-3", "4-7", "8+"]
210210
def compute_hist_counts(data):
211211
data = np.array(data)
212212
bin0 = np.sum(data == 0)
213-
bin1 = np.sum((data >= 1) & (data <= 2))
214-
bin2 = np.sum((data >= 3) & (data <= 4))
215-
bin3 = np.sum(data >= 5)
213+
bin1 = np.sum((data >= 1) & (data <= 3))
214+
bin2 = np.sum((data >= 4) & (data <= 7))
215+
bin3 = np.sum(data >= 8)
216216
return [bin0, bin1, bin2, bin3]
217217

218218
men_match_data = [x[1] for x in men_matches]
@@ -346,13 +346,13 @@ def compute_hist_counts(data):
346346
<details>
347347
<summary>Lever A (click to reveal)</summary>
348348
<label for="weight_reciprocal">Reciprocal Weight (w<sub>reciprocal</sub>):</label>
349-
<input type="number" id="weight_reciprocal" name="weight_reciprocal" value="0.0" step="0.1" min="0" max="5.0">
349+
<input type="number" id="weight_reciprocal" name="weight_reciprocal" value="0.0" step="0.1" min="0" max="3.0">
350350
</details>
351351
352352
<details>
353353
<summary>Lever B (click to reveal)</summary>
354354
<label for="weight_queue_penalty">Queue Penalty Weight (w<sub>queue</sub>):</label>
355-
<input type="number" id="weight_queue_penalty" name="weight_queue_penalty" value="0.0" step="0.1" min="0" max="2.0">
355+
<input type="number" id="weight_queue_penalty" name="weight_queue_penalty" value="0.0" step="0.01" min="0" max="1.0">
356356
</details>
357357
358358
<label>

backend.py

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,10 @@
2424
##############################################################################
2525
# 1.5) SELECT "JACK" AND "JILL" AS MIDDLE-PERFORMING PROFILES
2626
##############################################################################
27-
# For Jack, we choose the man whose average probability (from women liking men)
28-
# is closest to the overall average among men.
2927
man_avgs = prob_women_likes_men.mean(axis=0)
3028
overall_man_avg = man_avgs.mean()
3129
jack_id = (man_avgs - overall_man_avg).abs().idxmin()
3230

33-
# For Jill, we choose the woman whose average probability (from men liking women)
34-
# is closest to the overall average among women.
3531
woman_avgs = prob_men_likes_women.mean(axis=0)
3632
overall_woman_avg = woman_avgs.mean()
3733
jill_id = (woman_avgs - overall_woman_avg).abs().idxmin()
@@ -87,11 +83,13 @@ def run_dating_simulation(
8783
random.seed(random_seed)
8884

8985
# Simulation state dictionaries.
90-
# For incoming likes, store tuples: (sender, sent_day)
9186
incoming_likes = {uid: [] for uid in all_user_ids}
9287
matches = {uid: set() for uid in all_user_ids}
9388
likes_sent = {uid: set() for uid in all_user_ids}
94-
daily_logs = [] # list of DataFrames (one per day)
89+
daily_logs = []
90+
91+
# NEW: track which candidates each user has already seen, so they don't reappear
92+
already_seen = {uid: set() for uid in all_user_ids}
9593

9694
# Loop over simulation days.
9795
for day in range(1, num_days + 1):
@@ -100,51 +98,53 @@ def run_dating_simulation(
10098
random.shuffle(login_order)
10199

102100
for user in login_order:
103-
# Determine candidate pool (opposite gender, not already matched).
101+
# Candidate pool: opposite gender, not matched, not already seen
104102
if user.startswith("W"):
105-
candidate_pool = [cid for cid in all_men_ids if cid not in matches[user]]
103+
candidate_pool = [
104+
cid for cid in all_men_ids
105+
if cid not in matches[user] and cid not in already_seen[user]
106+
]
106107
get_prob = lambda cand: prob_women_likes_men.loc[user, cand]
107108
get_reciprocal = lambda cand: prob_men_likes_women.loc[cand, user]
108109
else:
109-
candidate_pool = [cid for cid in all_women_ids if cid not in matches[user]]
110+
candidate_pool = [
111+
cid for cid in all_women_ids
112+
if cid not in matches[user] and cid not in already_seen[user]
113+
]
110114
get_prob = lambda cand: prob_men_likes_women.loc[user, cand]
111115
get_reciprocal = lambda cand: prob_women_likes_men.loc[cand, user]
112116

113-
# Build a lookup from candidate -> sent_day for those who already liked user.
117+
# Build a lookup from candidate -> earliest sent_day
114118
incoming_for_user = {}
115119
for sender, sent_day in incoming_likes[user]:
116-
# If multiple incoming likes from the same candidate, take the earliest.
117120
if sender not in incoming_for_user or sent_day < incoming_for_user[sender]:
118121
incoming_for_user[sender] = sent_day
119122

120-
# Build a combined candidate list.
123+
# Build the combined candidate list
121124
candidate_info = []
122125
for cand in candidate_pool:
123126
if cand in incoming_for_user:
124-
# Candidate already liked user: use score = P_ij.
125127
score = get_prob(cand)
126-
candidate_info.append({
127-
"CandidateID": cand,
128-
"Score": score,
129-
"Source": "incoming",
130-
"SentDay": incoming_for_user[cand]
131-
})
128+
source = "incoming"
129+
sent_d = incoming_for_user[cand]
132130
else:
133-
# Fresh candidate.
134-
q = len(incoming_likes[cand]) # pending likes for candidate
135-
score = get_prob(cand) * (1/(1 + weight_queue_penalty * q)) * (get_reciprocal(cand) ** weight_reciprocal)
136-
candidate_info.append({
137-
"CandidateID": cand,
138-
"Score": score,
139-
"Source": "fresh",
140-
"SentDay": day # fresh likes are sent today
141-
})
131+
q = len(incoming_likes[cand]) # how many are pending for cand
132+
score = get_prob(cand) * (1/(1 + weight_queue_penalty * q)) \
133+
* (get_reciprocal(cand) ** weight_reciprocal)
134+
source = "fresh"
135+
sent_d = day
136+
candidate_info.append({
137+
"CandidateID": cand,
138+
"Score": score,
139+
"Source": source,
140+
"SentDay": sent_d
141+
})
142142

143-
# Sort the combined list by score (descending) and select top daily_queue_size.
143+
# Sort by descending score, pick top daily_queue_size
144144
candidate_info_sorted = sorted(candidate_info, key=lambda x: x["Score"], reverse=True)
145145
selected_candidates = candidate_info_sorted[:daily_queue_size]
146146

147-
# Process each selected candidate.
147+
# Process each selected candidate
148148
for cand_record in selected_candidates:
149149
cand = cand_record["CandidateID"]
150150
source = cand_record["Source"]
@@ -154,25 +154,27 @@ def run_dating_simulation(
154154
decision = "Pass"
155155
match_formed = False
156156

157+
# Remove the incoming like once user sees it
158+
if source == "incoming":
159+
for idx, (s, sd) in enumerate(incoming_likes[user]):
160+
if s == cand:
161+
del incoming_likes[user][idx]
162+
break
163+
164+
# Decide like or pass
157165
if roll < like_prob:
158166
decision = "Like"
159-
# If candidate has already liked user, a match is formed.
160167
if user in likes_sent[cand]:
168+
# match is formed
161169
match_formed = True
162170
matches[user].add(cand)
163171
matches[cand].add(user)
164172
else:
165173
likes_sent[user].add(cand)
166-
# For fresh candidates, add this like to candidate's incoming likes.
167174
if source == "fresh":
168175
incoming_likes[cand].append((user, day))
169-
# If the candidate came from the incoming list, remove that pending like.
170-
if source == "incoming":
171-
for idx, (s, sd) in enumerate(incoming_likes[user]):
172-
if s == cand:
173-
del incoming_likes[user][idx]
174-
break
175-
delay = day - sent_day # 0 if fresh; >0 if pending from an earlier day
176+
177+
delay = day - sent_day
176178
day_records.append({
177179
"Day": day,
178180
"UserID": user,
@@ -185,6 +187,10 @@ def run_dating_simulation(
185187
"MatchFormed": match_formed,
186188
"Delay": delay
187189
})
190+
191+
# Mark cand as seen
192+
already_seen[user].add(cand)
193+
188194
daily_logs.append(pd.DataFrame(day_records))
189195

190-
return daily_logs, matches, incoming_likes
196+
return daily_logs, matches, incoming_likes

0 commit comments

Comments
 (0)