tzurshubi commited on
Commit
03a57d4
·
verified ·
1 Parent(s): 27d1a74

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -4
app.py CHANGED
@@ -183,6 +183,44 @@ def int_to_bin(n: int, d: int) -> str:
183
  # Even bits → X offsets, odd bits → Y offsets, decreasing magnitudes.
184
  def layout_positions(d: int, base: float = 900.0):
185
  n = 1 << d # number of nodes = 2^d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  dx, dy = [0.0] * d, [0.0] * d # per-dimension offsets
187
  for k in range(d): # dimension k
188
  tier = k // 2 # tier 0,1,2,...
@@ -311,7 +349,8 @@ def make_figure(d: int,
311
  edge_w: int,
312
  path: List[int],
313
  scale_base: float,
314
- subsel: dict | None = None):
 
315
  nodes, edges = build_hypercube(d)
316
  pts, width, height = layout_positions(d, base=scale_base)
317
  pos = {vid: (x, y) for vid, x, y in pts}
@@ -681,6 +720,16 @@ app.layout = html.Div(
681
  style={"width": "60px"},
682
  ),
683
  html.Button("Flip", id="btn_flip", n_clicks=0),
 
 
 
 
 
 
 
 
 
 
684
  ],
685
  ),
686
 
@@ -918,7 +967,8 @@ def update_path(clickData,
918
  Input("path_store", "data"),
919
  Input("mark_negations", "value"),
920
  Input("mark_distances", "value"),
921
- Input("subpath_select_store", "data"), # NEW
 
922
  )
923
  def render(d, show_labels_vals, path, mark_vals, mark_dist_vals, subsel):
924
  labels_vals = show_labels_vals or []
@@ -941,7 +991,8 @@ def render(d, show_labels_vals, path, mark_vals, mark_dist_vals, subsel):
941
  edge_w=DEFAULTS["edge_width"],
942
  path=path or [],
943
  scale_base=float(DEFAULTS["scale"]),
944
- subsel=subsel or {}, # NEW
 
945
  )
946
  return fig
947
 
@@ -1041,12 +1092,35 @@ def style_flip_subpath_button(subsel):
1041
 
1042
  if active:
1043
  # selection mode ON
1044
- return {**base, "background": "#DC2626"} # red
1045
  else:
1046
  # selection mode OFF
1047
  return {**base, "background": "#2563EB"} # blue
1048
 
1049
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1050
 
1051
  if __name__ == "__main__":
1052
  import os
 
183
  # Even bits → X offsets, odd bits → Y offsets, decreasing magnitudes.
184
  def layout_positions(d: int, base: float = 900.0):
185
  n = 1 << d # number of nodes = 2^d
186
+
187
+
188
+ if mode == "bipartite":
189
+ # Left: odd parity, Right: even parity (as requested)
190
+ odds = [v for v in range(n) if (bin(v).count("1") % 2) == 1]
191
+ evens = [v for v in range(n) if (bin(v).count("1") % 2) == 0]
192
+
193
+ # Deterministic order (by integer id)
194
+ odds.sort()
195
+ evens.sort()
196
+
197
+ col_gap = max(400.0, base * 0.9) # horizontal distance between columns
198
+ y_gap = max(12.0, base / max(1, (n // 2))) # vertical spacing
199
+
200
+ pts = []
201
+
202
+ # Put odds on the left (x=0)
203
+ for idx, v in enumerate(odds):
204
+ x = 0.0
205
+ y = idx * y_gap
206
+ pts.append((v, x, y))
207
+
208
+ # Put evens on the right (x=col_gap)
209
+ for idx, v in enumerate(evens):
210
+ x = col_gap
211
+ y = idx * y_gap
212
+ pts.append((v, x, y))
213
+
214
+ # normalize to start at (0,0) like your current function
215
+ minx = min(x for _, x, _ in pts)
216
+ miny = min(y for _, _, y in pts)
217
+ pts2 = [(vid, x - minx, y - miny) for vid, x, y in pts]
218
+
219
+ width = col_gap
220
+ height = (len(odds) - 1) * y_gap if odds else 0.0
221
+ return pts2, width, height
222
+
223
+
224
  dx, dy = [0.0] * d, [0.0] * d # per-dimension offsets
225
  for k in range(d): # dimension k
226
  tier = k // 2 # tier 0,1,2,...
 
349
  edge_w: int,
350
  path: List[int],
351
  scale_base: float,
352
+ subsel: dict | None = None,
353
+ layout_mode: str = "default"):
354
  nodes, edges = build_hypercube(d)
355
  pts, width, height = layout_positions(d, base=scale_base)
356
  pos = {vid: (x, y) for vid, x, y in pts}
 
720
  style={"width": "60px"},
721
  ),
722
  html.Button("Flip", id="btn_flip", n_clicks=0),
723
+
724
+ # Bipartite Layout Button
725
+ html.Button(
726
+ "Bipartite layout",
727
+ id="btn_bipartite_layout",
728
+ n_clicks=0,
729
+ style={"background": "#6B7280", "color": "white"},
730
+ ),
731
+ dcc.Store(id="layout_mode_store", data="default"),
732
+
733
  ],
734
  ),
735
 
 
967
  Input("path_store", "data"),
968
  Input("mark_negations", "value"),
969
  Input("mark_distances", "value"),
970
+ Input("subpath_select_store", "data"),
971
+ Input("layout_mode_store", "data"),
972
  )
973
  def render(d, show_labels_vals, path, mark_vals, mark_dist_vals, subsel):
974
  labels_vals = show_labels_vals or []
 
991
  edge_w=DEFAULTS["edge_width"],
992
  path=path or [],
993
  scale_base=float(DEFAULTS["scale"]),
994
+ subsel=subsel or {},
995
+ layout_mode=layout_mode or "default", # NEW
996
  )
997
  return fig
998
 
 
1092
 
1093
  if active:
1094
  # selection mode ON
1095
+ return {**base, "background": "#2563EB"} # strong blue for selection
1096
  else:
1097
  # selection mode OFF
1098
  return {**base, "background": "#2563EB"} # blue
1099
 
1100
 
1101
+ @app.callback(
1102
+ Output("layout_mode_store", "data"),
1103
+ Input("btn_bipartite_layout", "n_clicks"),
1104
+ State("layout_mode_store", "data"),
1105
+ prevent_initial_call=True
1106
+ )
1107
+ def toggle_layout(n, mode):
1108
+ mode = mode or "default"
1109
+ return "bipartite" if mode == "default" else "default"
1110
+
1111
+
1112
+ @app.callback(
1113
+ Output("btn_bipartite_layout", "style"),
1114
+ Input("layout_mode_store", "data"),
1115
+ )
1116
+ def style_layout_button(mode):
1117
+ base = {"color": "white", "border": "none", "padding": "6px 12px", "borderRadius": "8px", "cursor": "pointer"}
1118
+ if mode == "bipartite":
1119
+ return {**base, "background": "#059669"} # green
1120
+ return {**base, "background": "#6B7280"} # gray
1121
+
1122
+
1123
+
1124
 
1125
  if __name__ == "__main__":
1126
  import os