yaz23 commited on
Commit
7bd06ba
·
1 Parent(s): 30c23ea

remove some bike_bench code

Browse files
bike_bench_internal/benchmark_models/results/models/CTGAN.pkl CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:1cbceb9dfe3ffc3be54c4a19a90412905753834a7e1f58637d1be7e906305975
3
- size 4287183
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:493ee9d845adc3fb6ed9d26eadfda1b87e45bbf1f591050d4c389af1d45e37e9
3
+ size 4081377
bike_bench_internal/benchmark_models/results/models/TVAE.pkl CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:9bdf3070e85d642be9f23f2a562215169821535f033cf72b94e9e4b2c5d188f4
3
- size 2339380
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1e7ff6161cc5f0bf1c86acee3ca056e4067b3d734c2d67b1541ac128a08f7777
3
+ size 2284778
bike_bench_internal/src/bikebench.egg-info/SOURCES.txt CHANGED
@@ -7,108 +7,34 @@ src/bikebench.egg-info/PKG-INFO
7
  src/bikebench.egg-info/SOURCES.txt
8
  src/bikebench.egg-info/dependency_links.txt
9
  src/bikebench.egg-info/top_level.txt
10
- src/bikebench/benchmark_models/__init__.py
11
- src/bikebench/benchmark_models/benchmarking_utils.py
12
- src/bikebench/benchmark_models/generative_modeling_utils.py
13
- src/bikebench/benchmark_models/libmoon/__init__.py
14
- src/bikebench/benchmark_models/libmoon/example.py
15
- src/bikebench/benchmark_models/libmoon/problem/__init__.py
16
- src/bikebench/benchmark_models/libmoon/problem/mop.py
17
- src/bikebench/benchmark_models/libmoon/problem/mtl/__init__.py
18
- src/bikebench/benchmark_models/libmoon/problem/mtl/fair_classify.py
19
- src/bikebench/benchmark_models/libmoon/problem/mtl/mnist.py
20
- src/bikebench/benchmark_models/libmoon/problem/mtl/objectives.py
21
- src/bikebench/benchmark_models/libmoon/problem/mtl/loaders/__init__.py
22
- src/bikebench/benchmark_models/libmoon/problem/mtl/loaders/adult_loader.py
23
- src/bikebench/benchmark_models/libmoon/problem/mtl/loaders/compas_loader.py
24
- src/bikebench/benchmark_models/libmoon/problem/mtl/loaders/credit_loader.py
25
- src/bikebench/benchmark_models/libmoon/problem/mtl/loaders/multimnist_loader.py
26
- src/bikebench/benchmark_models/libmoon/problem/mtl/model/__init__.py
27
- src/bikebench/benchmark_models/libmoon/problem/mtl/model/simple.py
28
- src/bikebench/benchmark_models/libmoon/problem/synthetic/__init__.py
29
- src/bikebench/benchmark_models/libmoon/problem/synthetic/dtlz.py
30
- src/bikebench/benchmark_models/libmoon/problem/synthetic/maf.py
31
- src/bikebench/benchmark_models/libmoon/problem/synthetic/re.py
32
- src/bikebench/benchmark_models/libmoon/problem/synthetic/re_original.py
33
- src/bikebench/benchmark_models/libmoon/problem/synthetic/vlmop.py
34
- src/bikebench/benchmark_models/libmoon/problem/synthetic/wfg.py
35
- src/bikebench/benchmark_models/libmoon/problem/synthetic/zdt.py
36
- src/bikebench/benchmark_models/libmoon/solver/__init__.py
37
- src/bikebench/benchmark_models/libmoon/solver/gradient/__init__.py
38
- src/bikebench/benchmark_models/libmoon/solver/gradient/base_solver.py
39
- src/bikebench/benchmark_models/libmoon/solver/gradient/core_solver.py
40
- src/bikebench/benchmark_models/libmoon/solver/gradient/epo_solver.py
41
- src/bikebench/benchmark_models/libmoon/solver/gradient/functions_evaluation.py
42
- src/bikebench/benchmark_models/libmoon/solver/gradient/functions_hv_grad_3d.py
43
- src/bikebench/benchmark_models/libmoon/solver/gradient/functions_hv_python3.py
44
- src/bikebench/benchmark_models/libmoon/solver/gradient/gradhv.py
45
- src/bikebench/benchmark_models/libmoon/solver/gradient/mgda_core.py
46
- src/bikebench/benchmark_models/libmoon/solver/gradient/mgda_solver.py
47
- src/bikebench/benchmark_models/libmoon/solver/gradient/min_norm_solvers_numpy.py
48
- src/bikebench/benchmark_models/libmoon/solver/gradient/moosvgd.py
49
- src/bikebench/benchmark_models/libmoon/solver/gradient/pmgda.py
50
- src/bikebench/benchmark_models/libmoon/solver/gradient/pmtl.py
51
- src/bikebench/benchmark_models/libmoon/solver/gradient/run/__init__.py
52
- src/bikebench/benchmark_models/libmoon/solver/gradient/run/run_grad.py
53
- src/bikebench/benchmark_models/libmoon/solver/gradient/utils/__init__.py
54
- src/bikebench/benchmark_models/libmoon/solver/gradient/utils/util.py
55
- src/bikebench/benchmark_models/libmoon/solver/mobo/__init__.py
56
- src/bikebench/benchmark_models/libmoon/solver/mobo/dirhvego.py
57
- src/bikebench/benchmark_models/libmoon/solver/mobo/mobod.py
58
- src/bikebench/benchmark_models/libmoon/solver/mobo/utils/__init__.py
59
- src/bikebench/benchmark_models/libmoon/solver/mobo/utils/termination.py
60
- src/bikebench/benchmark_models/libmoon/solver/moea/__init__.py
61
- src/bikebench/benchmark_models/libmoon/solver/moea/moead.py
62
- src/bikebench/benchmark_models/libmoon/solver/moea/moead_pfl.py
63
- src/bikebench/benchmark_models/libmoon/solver/moea/utils/__init__.py
64
- src/bikebench/benchmark_models/libmoon/solver/moea/utils/decomposition.py
65
- src/bikebench/benchmark_models/libmoon/solver/moea/utils/genetic_operator.py
66
- src/bikebench/benchmark_models/libmoon/solver/moea/utils/population.py
67
- src/bikebench/benchmark_models/libmoon/solver/moea/utils/termination.py
68
- src/bikebench/benchmark_models/libmoon/solver/moea/utils/utils_ea.py
69
- src/bikebench/benchmark_models/libmoon/solver/moea/utils/weight_vector.py
70
- src/bikebench/benchmark_models/libmoon/solver/pfl/__init__.py
71
- src/bikebench/benchmark_models/libmoon/solver/pfl/run.py
72
- src/bikebench/benchmark_models/libmoon/solver/pfl/model/__init__.py
73
- src/bikebench/benchmark_models/libmoon/solver/pfl/model/simple.py
74
- src/bikebench/benchmark_models/libmoon/solver/psl/__init__.py
75
- src/bikebench/benchmark_models/libmoon/solver/psl/run_mtl_condition.py
76
- src/bikebench/benchmark_models/libmoon/solver/psl/run_mtl_psl.py
77
- src/bikebench/benchmark_models/libmoon/solver/psl/run_simple_psl.py
78
- src/bikebench/benchmark_models/libmoon/solver/psl/util.py
79
- src/bikebench/benchmark_models/libmoon/solver/psl/model/__init__.py
80
- src/bikebench/benchmark_models/libmoon/solver/psl/model/mtl.py
81
- src/bikebench/benchmark_models/libmoon/solver/psl/model/simple.py
82
- src/bikebench/benchmark_models/libmoon/util_global/__init__.py
83
- src/bikebench/benchmark_models/libmoon/util_global/constant.py
84
- src/bikebench/benchmark_models/libmoon/util_global/scalarization.py
85
- src/bikebench/benchmark_models/libmoon/util_global/weight_factor/__init__.py
86
- src/bikebench/benchmark_models/libmoon/util_global/weight_factor/das_dennis.py
87
- src/bikebench/benchmark_models/libmoon/util_global/weight_factor/funs.py
88
- src/bikebench/benchmark_models/libmoon/visulization/__init__.py
89
- src/bikebench/benchmark_models/libmoon/visulization/util.py
90
- src/bikebench/benchmark_models/libmoon/visulization/view_res.py
91
  src/bikebench/conditioning/__init__.py
92
  src/bikebench/conditioning/conditioning.py
93
  src/bikebench/data_loading/__init__.py
94
  src/bikebench/data_loading/data_loading.py
95
  src/bikebench/data_loading/dataverse_utils.py
 
96
  src/bikebench/design_evaluation/__init__.py
97
  src/bikebench/design_evaluation/design_evaluation.py
98
- src/bikebench/design_evaluation/score_report.py
99
- src/bikebench/design_evaluation/scoring.py
100
  src/bikebench/embedding/__init__.py
101
  src/bikebench/embedding/clip_embedding_calculator.py
102
  src/bikebench/embedding/dataset_rendering_tools.py
103
  src/bikebench/embedding/embedding_predictor.py
104
- src/bikebench/embedding/execute_emb.py
105
  src/bikebench/ergonomics/__init__.py
106
  src/bikebench/ergonomics/joint_angles.py
107
  src/bikebench/prediction/__init__.py
108
  src/bikebench/prediction/aero_predictor.py
109
- src/bikebench/prediction/clip_predictor.py
110
  src/bikebench/prediction/evaluators.py
 
111
  src/bikebench/prediction/prediction_utils.py
 
 
 
112
  src/bikebench/rendering/BikeCAD_server_client.py
113
  src/bikebench/rendering/BikeCAD_server_manager.py
114
  src/bikebench/rendering/__init__.py
 
7
  src/bikebench.egg-info/SOURCES.txt
8
  src/bikebench.egg-info/dependency_links.txt
9
  src/bikebench.egg-info/top_level.txt
10
+ src/bikebench/benchmarking/__init__ .py
11
+ src/bikebench/benchmarking/benchmarker.py
12
+ src/bikebench/benchmarking/benchmarking_utils.py
13
+ src/bikebench/benchmarking/score_report.py
14
+ src/bikebench/benchmarking/scoring.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  src/bikebench/conditioning/__init__.py
16
  src/bikebench/conditioning/conditioning.py
17
  src/bikebench/data_loading/__init__.py
18
  src/bikebench/data_loading/data_loading.py
19
  src/bikebench/data_loading/dataverse_utils.py
20
+ src/bikebench/data_loading/execute_upload_missing.py
21
  src/bikebench/design_evaluation/__init__.py
22
  src/bikebench/design_evaluation/design_evaluation.py
 
 
23
  src/bikebench/embedding/__init__.py
24
  src/bikebench/embedding/clip_embedding_calculator.py
25
  src/bikebench/embedding/dataset_rendering_tools.py
26
  src/bikebench/embedding/embedding_predictor.py
 
27
  src/bikebench/ergonomics/__init__.py
28
  src/bikebench/ergonomics/joint_angles.py
29
  src/bikebench/prediction/__init__.py
30
  src/bikebench/prediction/aero_predictor.py
31
+ src/bikebench/prediction/aesthetics_predictor.py
32
  src/bikebench/prediction/evaluators.py
33
+ src/bikebench/prediction/model_definitions.py
34
  src/bikebench/prediction/prediction_utils.py
35
+ src/bikebench/prediction/structural_predictor.py
36
+ src/bikebench/prediction/usability_predictor.py
37
+ src/bikebench/prediction/validity_predictor.py
38
  src/bikebench/rendering/BikeCAD_server_client.py
39
  src/bikebench/rendering/BikeCAD_server_manager.py
40
  src/bikebench/rendering/__init__.py
bike_bench_internal/src/bikebench.egg-info/top_level.txt CHANGED
@@ -1,2 +1,3 @@
1
  bikebench
 
2
  resources
 
1
  bikebench
2
+ biked_commons
3
  resources
bike_bench_internal/src/bikebench/benchmarking/benchmarking_utils.py CHANGED
@@ -8,10 +8,23 @@ from bikebench.conditioning import conditioning
8
  from tqdm import trange, tqdm
9
  from bikebench.transformation import ordered_columns
10
 
11
- def get_test_conditions():
12
- rider_conditions = conditioning.sample_riders(100, split="test")
13
- use_case_conditions = conditioning.sample_use_case(100, split="test")
14
- image_embeddings = conditioning.sample_embedding(100, split="test")
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  rider_conditions_repeated = rider_conditions.repeat_interleave(100, dim=0)
17
  use_case_conditions_repeated = use_case_conditions.repeat_interleave(100, dim=0)
@@ -21,27 +34,37 @@ def get_test_conditions():
21
 
22
  return conditions
23
 
24
- def get_single_test_condition(idx=0, device="cpu"):
25
- rider_condition = conditioning.sample_riders(100, split="test")
26
- use_case_condition = conditioning.sample_use_case(100, split="test")
27
- image_embedding = conditioning.sample_embedding(100, split="test")
28
-
29
  rider_condition = rider_condition[idx].to(device)
30
  use_case_condition = use_case_condition[idx].to(device)
31
- image_embedding = image_embedding[idx].to(device)
 
 
 
 
 
 
 
 
32
 
33
- condition = {"Rider": rider_condition, "Use Case": use_case_condition, "Embedding": image_embedding}
34
- return condition
 
 
 
 
35
 
36
- def evaluate(result_tens, device, evaluate_as_aggregate = False):
37
  data_columns = ordered_columns.bike_bench_columns
38
- evaluations = get_standard_evaluations(device)
39
- evaluator, requirement_names, requirement_types = construct_tensor_evaluator(evaluations, data_columns, device=device)
40
 
41
  if evaluate_as_aggregate:
42
- condition = get_test_conditions()
43
- main_scorer = construct_scorer(MainScores, evaluations, data_columns, device)
44
- detailed_scorer = construct_scorer(DetailedScores, evaluations, data_columns, device)
45
 
46
  main_scores = main_scorer(result_tens, condition)
47
  detailed_scores = detailed_scorer(result_tens, condition)
@@ -50,16 +73,17 @@ def evaluate(result_tens, device, evaluate_as_aggregate = False):
50
  all_detailed_scores = []
51
  all_evaluation_scores = []
52
 
53
- main_scorer = construct_scorer(MainScores, evaluations, data_columns, device)
54
- detailed_scorer = construct_scorer(DetailedScores, evaluations, data_columns, device)
55
  for i in trange(100):
56
  result_slice = result_tens[i*100:(i+1)*100]
57
- condition = get_single_test_condition(i, device)
58
 
 
59
  evaluation_scores = evaluator(result_slice, condition)
60
 
61
- main_scores = main_scorer(result_slice.detach(), condition, preevaluated_scores = evaluation_scores) #main scores is a series
62
- detailed_scores = detailed_scorer(result_slice.detach(), condition, preevaluated_scores = evaluation_scores) #detailed scores is a series
63
 
64
  all_main_scores.append(main_scores)
65
  all_detailed_scores.append(detailed_scores)
@@ -72,6 +96,12 @@ def evaluate(result_tens, device, evaluate_as_aggregate = False):
72
 
73
  return main_scores, detailed_scores, all_evaluation_scores
74
 
 
 
 
 
 
 
75
  # def get_condition_by_idx(idx=0):
76
  # rider_condition = conditioning.sample_riders(10, split="test")
77
  # use_case_condition = conditioning.sample_use_case(10, split="test")
 
8
  from tqdm import trange, tqdm
9
  from bikebench.transformation import ordered_columns
10
 
11
+ def get_train_conditions(n, randomize=True, device="cpu", mode = "embedding"):
12
+ rider_conditions = conditioning.sample_riders(n, split="train", randomize=randomize, device=device)
13
+ use_case_conditions = conditioning.sample_use_case(n, split="train", randomize=randomize, device=device)
14
+
15
+ if mode == "text":
16
+ texts = conditioning.sample_text(n, split="train", randomize=randomize)
17
+ conditions = {"Rider": rider_conditions, "Use Case": use_case_conditions, "Text": texts}
18
+ return conditions
19
+ elif mode == "embedding":
20
+ image_embeddings = conditioning.sample_embedding(n, split="train", randomize=randomize, device=device)
21
+ conditions = {"Rider": rider_conditions, "Use Case": use_case_conditions, "Embedding": image_embeddings}
22
+ return conditions
23
+
24
+ def get_test_conditions(device="cpu"):
25
+ rider_conditions = conditioning.sample_riders(100, split="test", device=device)
26
+ use_case_conditions = conditioning.sample_use_case(100, split="test", device=device)
27
+ image_embeddings = conditioning.sample_embedding(100, split="test", device=device)
28
 
29
  rider_conditions_repeated = rider_conditions.repeat_interleave(100, dim=0)
30
  use_case_conditions_repeated = use_case_conditions.repeat_interleave(100, dim=0)
 
34
 
35
  return conditions
36
 
37
+ def get_single_test_condition(idx=0, device="cpu", mode = "embedding"):
38
+ rider_condition = conditioning.sample_riders(100, split="test", device=device)
39
+ use_case_condition = conditioning.sample_use_case(100, split="test", device=device)
 
 
40
  rider_condition = rider_condition[idx].to(device)
41
  use_case_condition = use_case_condition[idx].to(device)
42
+ if mode == "text":
43
+ text = conditioning.sample_text(100, split="test")
44
+ text = text[idx]
45
+ condition = {"Rider": rider_condition, "Use Case": use_case_condition, "Text": text}
46
+ return condition
47
+
48
+ elif mode == "embedding":
49
+ image_embedding = conditioning.sample_embedding(100, split="test", device=device)
50
+
51
 
52
+ image_embedding = image_embedding[idx].to(device)
53
+
54
+ condition = {"Rider": rider_condition, "Use Case": use_case_condition, "Embedding": image_embedding}
55
+ return condition
56
+ else:
57
+ raise ValueError("mode must be 'text' or 'embedding'")
58
 
59
+ def evaluate_designs(result_tens, evaluate_as_aggregate = False):
60
  data_columns = ordered_columns.bike_bench_columns
61
+ evaluations = get_standard_evaluations("cpu")
62
+ evaluator, requirement_names, is_objective, is_conditional = construct_tensor_evaluator(evaluations, data_columns, device="cpu")
63
 
64
  if evaluate_as_aggregate:
65
+ condition = get_test_conditions("cpu")
66
+ main_scorer = construct_scorer(MainScores, evaluations, data_columns, "cpu")
67
+ detailed_scorer = construct_scorer(DetailedScores, evaluations, data_columns, "cpu")
68
 
69
  main_scores = main_scorer(result_tens, condition)
70
  detailed_scores = detailed_scorer(result_tens, condition)
 
73
  all_detailed_scores = []
74
  all_evaluation_scores = []
75
 
76
+ main_scorer = construct_scorer(MainScores, evaluations, data_columns, "cpu")
77
+ detailed_scorer = construct_scorer(DetailedScores, evaluations, data_columns, "cpu")
78
  for i in trange(100):
79
  result_slice = result_tens[i*100:(i+1)*100]
80
+ condition = get_single_test_condition(i, "cpu")
81
 
82
+ result_slice = result_slice.detach().cpu()
83
  evaluation_scores = evaluator(result_slice, condition)
84
 
85
+ main_scores = main_scorer(result_slice, condition, preevaluated_scores = evaluation_scores) #main scores is a series
86
+ detailed_scores = detailed_scorer(result_slice, condition, preevaluated_scores = evaluation_scores) #detailed scores is a series
87
 
88
  all_main_scores.append(main_scores)
89
  all_detailed_scores.append(detailed_scores)
 
96
 
97
  return main_scores, detailed_scores, all_evaluation_scores
98
 
99
+
100
+
101
+
102
+
103
+
104
+
105
  # def get_condition_by_idx(idx=0):
106
  # rider_condition = conditioning.sample_riders(10, split="test")
107
  # use_case_condition = conditioning.sample_use_case(10, split="test")
bike_bench_internal/src/bikebench/benchmarking/scoring.py CHANGED
@@ -156,7 +156,7 @@ class AverageConstraintViolation(ScoringFunction):
156
  return self.names
157
 
158
  def evaluate(self, designs, objective_scores, constraint_scores, objective_names, constraint_names, obj_ref_point):
159
- self.names = [f"Mean Violations ↓"] #counts average number of violated constraints per design
160
  validity_boolean = constraint_scores > 0
161
  return np.mean(np.sum(validity_boolean, axis=1))
162
 
@@ -170,7 +170,7 @@ class AverageNovelty(ScoringFunction):
170
  self.reference_designs = self.scaler.transform(raw_ref)
171
 
172
  def return_names(self) -> List[str]:
173
- return ["Mean Novelty ↑"]
174
 
175
  def evaluate(self, designs, objective_scores, constraint_scores, objective_names, constraint_names, obj_ref_point):
176
  scaled_designs = self.scaler.transform(designs)
@@ -195,6 +195,8 @@ class DPPDiversity(ScoringFunction):
195
  return ["Diversity ↓ (DPP)"]
196
 
197
  def evaluate(self, designs, objective_scores, constraint_scores, objective_names, constraint_names, obj_ref_point):
 
 
198
 
199
  # Convert to numpy and scale like AverageNovelty
200
  X = np.asarray(designs, dtype=np.float64)
@@ -286,9 +288,9 @@ class MeanConstraintViolationMagnitude(ScoringFunction):
286
  return meanscores
287
 
288
  def construct_scorer(scoring_functions: List[ScoringFunction], evaluation_functions: List[EvaluationFunction], column_names: List[str], device: str = "cpu") -> callable:
289
- evaluator, requirement_names, requirement_types = construct_tensor_evaluator(evaluation_functions, column_names, device=device)
290
  requirement_names = np.array(requirement_names)
291
- isobjective = torch.tensor(requirement_types) == 1
292
  objective_names = requirement_names[isobjective]
293
  constraint_names = requirement_names[~isobjective]
294
 
 
156
  return self.names
157
 
158
  def evaluate(self, designs, objective_scores, constraint_scores, objective_names, constraint_names, obj_ref_point):
159
+ self.names = [f"Constraint Violation ↓"] #counts average number of violated constraints per design
160
  validity_boolean = constraint_scores > 0
161
  return np.mean(np.sum(validity_boolean, axis=1))
162
 
 
170
  self.reference_designs = self.scaler.transform(raw_ref)
171
 
172
  def return_names(self) -> List[str]:
173
+ return ["Novelty ↑"]
174
 
175
  def evaluate(self, designs, objective_scores, constraint_scores, objective_names, constraint_names, obj_ref_point):
176
  scaled_designs = self.scaler.transform(designs)
 
195
  return ["Diversity ↓ (DPP)"]
196
 
197
  def evaluate(self, designs, objective_scores, constraint_scores, objective_names, constraint_names, obj_ref_point):
198
+ #deduplicate designs
199
+ designs = np.unique(designs, axis=0)
200
 
201
  # Convert to numpy and scale like AverageNovelty
202
  X = np.asarray(designs, dtype=np.float64)
 
288
  return meanscores
289
 
290
  def construct_scorer(scoring_functions: List[ScoringFunction], evaluation_functions: List[EvaluationFunction], column_names: List[str], device: str = "cpu") -> callable:
291
+ evaluator, requirement_names, is_objective, is_conditional = construct_tensor_evaluator(evaluation_functions, column_names, device=device)
292
  requirement_names = np.array(requirement_names)
293
+ isobjective = torch.tensor(is_objective, dtype=bool)
294
  objective_names = requirement_names[isobjective]
295
  constraint_names = requirement_names[~isobjective]
296
 
bike_bench_internal/src/bikebench/conditioning/conditioning.py CHANGED
@@ -79,41 +79,48 @@ def _get_embed_tensor(split: str, device: torch.device = None):
79
  cpu = _ensure_embed_cpu(split)
80
  return _to_device_cached(cpu, _DEVICE_CACHE["embed"][split], device)
81
 
 
 
 
 
 
 
 
 
82
  def sample_riders(num_samples: int, split="test",
83
  randomize=False, device: torch.device = None):
 
 
 
84
  data = _get_rider_tensor(split, device)
85
  N = data.size(0)
86
- if randomize:
87
- idx = torch.randint(0, N, (num_samples,), device=data.device)
88
- else:
89
- reps = num_samples // N + 1
90
- idx = torch.arange(N, device=data.device).repeat(reps)[:num_samples]
91
  return data[idx]
92
 
93
- def sample_embedding(num_samples: int, split="test", device: torch.device = None):
 
 
 
 
94
  data = _get_embed_tensor(split, device)
95
  N = data.size(0)
96
- if split=="train":
97
- idx = torch.randint(0, N, (num_samples,), device=data.device)
98
- else:
99
- reps = num_samples // N + 1
100
- idx = torch.arange(N, device=data.device).repeat(reps)[:num_samples]
101
  return data[idx]
102
 
103
- def sample_use_case(num_samples: int, split=None, device: torch.device = None):
 
 
104
  onehot = torch.eye(3, dtype=torch.float32)
105
- if split=="train":
106
- idx = torch.randint(0, 3, (num_samples,), device=onehot.device)
107
- else:
108
- reps = num_samples // 3 + 1
109
- idx = torch.arange(3, device=onehot.device).repeat(reps)[:num_samples]
110
  return onehot[idx]
111
 
112
  def sample_text(num_samples, split="test", randomize=False):
113
  text_data = _ensure_text(split)
114
  if not text_data:
115
  return []
116
- if randomize:
117
- return np.random.choice(text_data, size=num_samples, replace=True).tolist()
118
- reps = num_samples // len(text_data) + 1
119
- return (text_data * reps)[:num_samples]
 
79
  cpu = _ensure_embed_cpu(split)
80
  return _to_device_cached(cpu, _DEVICE_CACHE["embed"][split], device)
81
 
82
+ def get_indices(N, num_samples, randomize=False):
83
+ if randomize:
84
+ idx = torch.randint(0, N, (num_samples,))
85
+ else:
86
+ reps = num_samples // N + 1
87
+ idx = torch.arange(N).repeat(reps)[:num_samples]
88
+ return idx
89
+
90
  def sample_riders(num_samples: int, split="test",
91
  randomize=False, device: torch.device = None):
92
+
93
+ if split=="test" and randomize:
94
+ print("Warning: Randomizing order of test data when benchmark expects fixed order!")
95
  data = _get_rider_tensor(split, device)
96
  N = data.size(0)
97
+ idx = get_indices(N, num_samples, randomize)
98
+ idx.to(device)
 
 
 
99
  return data[idx]
100
 
101
+ def sample_embedding(num_samples: int, split="test", randomize = False, device: torch.device = None):
102
+ #warn if split is test and randomize is true
103
+ if split=="test" and randomize:
104
+ print("Warning: Randomizing order of test data when benchmark expects fixed order!")
105
+
106
  data = _get_embed_tensor(split, device)
107
  N = data.size(0)
108
+ idx = get_indices(N, num_samples, randomize)
109
+ idx.to(device)
 
 
 
110
  return data[idx]
111
 
112
+ def sample_use_case(num_samples: int, split=None, randomize = False, device: torch.device = None):
113
+ if split=="test" and randomize:
114
+ print("Warning: Randomizing order of test data when benchmark expects fixed order!")
115
  onehot = torch.eye(3, dtype=torch.float32)
116
+ idx = get_indices(3, num_samples, randomize)
117
+ idx.to(device)
 
 
 
118
  return onehot[idx]
119
 
120
  def sample_text(num_samples, split="test", randomize=False):
121
  text_data = _ensure_text(split)
122
  if not text_data:
123
  return []
124
+ N = len(text_data)
125
+ idx = get_indices(N, num_samples, randomize)
126
+ return [text_data[i] for i in idx]
 
bike_bench_internal/src/bikebench/data_loading/data_loading.py CHANGED
@@ -11,6 +11,7 @@ import requests
11
  from tqdm import tqdm
12
 
13
  from bikebench.resource_utils import datasets_path
 
14
 
15
  # ------------------------------------------------------------------------------------
16
  # Config
@@ -238,11 +239,21 @@ def load_bike_bench_test(*, repair: bool = False, doi: str = DATAVERSE_DOI):
238
 
239
  def load_bike_bench_mixed_modality_train(*, repair: bool = False, doi: str = DATAVERSE_DOI):
240
  path = download_if_missing("Generative_Modeling_Datasets/bike_bench_mixed_modality.csv", repair=repair, doi=doi)
241
- return pd.read_csv(path, index_col=0)
 
 
 
 
 
242
 
243
  def load_bike_bench_mixed_modality_test(*, repair: bool = False, doi: str = DATAVERSE_DOI):
244
  path = download_if_missing("Generative_Modeling_Datasets/bike_bench_mixed_modality_test.csv", repair=repair, doi=doi)
245
- return pd.read_csv(path, index_col=0)
 
 
 
 
 
246
 
247
  # ---- Original_BIKED_Data (numbered helpers) ----
248
  def _find_numbered_file(base_dir: Path, n: int, exts: set[str], paren_style: bool = False) -> str:
 
11
  from tqdm import tqdm
12
 
13
  from bikebench.resource_utils import datasets_path
14
+ from bikebench.transformation.one_hot_encoding import ONE_HOT_ENCODED_BIKEBENCH_COLUMNS, BOOLEAN_COLUMNS
15
 
16
  # ------------------------------------------------------------------------------------
17
  # Config
 
239
 
240
  def load_bike_bench_mixed_modality_train(*, repair: bool = False, doi: str = DATAVERSE_DOI):
241
  path = download_if_missing("Generative_Modeling_Datasets/bike_bench_mixed_modality.csv", repair=repair, doi=doi)
242
+ df = pd.read_csv(path, index_col=0)
243
+ categorical_cols = ONE_HOT_ENCODED_BIKEBENCH_COLUMNS
244
+ boolean_cols = BOOLEAN_COLUMNS
245
+ continuous_cols = df.columns.difference(categorical_cols + boolean_cols).tolist()
246
+ df[continuous_cols] = df[continuous_cols].astype(np.float32)
247
+ return df
248
 
249
  def load_bike_bench_mixed_modality_test(*, repair: bool = False, doi: str = DATAVERSE_DOI):
250
  path = download_if_missing("Generative_Modeling_Datasets/bike_bench_mixed_modality_test.csv", repair=repair, doi=doi)
251
+ df = pd.read_csv(path, index_col=0)
252
+ categorical_cols = ONE_HOT_ENCODED_BIKEBENCH_COLUMNS
253
+ boolean_cols = BOOLEAN_COLUMNS
254
+ continuous_cols = df.columns.difference(categorical_cols + boolean_cols).tolist()
255
+ df[continuous_cols] = df[continuous_cols].astype(np.float32)
256
+ return df
257
 
258
  # ---- Original_BIKED_Data (numbered helpers) ----
259
  def _find_numbered_file(base_dir: Path, n: int, exts: set[str], paren_style: bool = False) -> str:
bike_bench_internal/src/bikebench/data_loading/dataverse_utils.py CHANGED
@@ -151,10 +151,18 @@ def _upload_new(doi: str, path: Path, directory_label: str) -> int:
151
  r.raise_for_status()
152
  return r.json()["data"]["files"][0]["dataFile"]["id"]
153
 
154
- def _replace_file(file_id: int, path: Path) -> int:
155
  url = f"{DV_API}/files/{file_id}/replace"
156
  mime = mimetypes.guess_type(str(path))[0] or "application/octet-stream"
157
- data = {"jsonData": json.dumps({"forceReplace": True})}
 
 
 
 
 
 
 
 
158
  with path.open("rb") as fh:
159
  files = {"file": (path.name, fh, mime)}
160
  r = requests.post(url, data=data, files=files, headers=_headers(), timeout=600)
@@ -189,7 +197,11 @@ def upload_directory(
189
  if full_key in remote and replace_existing:
190
  fid = remote[full_key]["id"]
191
  print(f"REPLACE {full_key} id={fid}")
192
- _replace_file(fid, abs_path)
 
 
 
 
193
  elif full_key in remote and not replace_existing:
194
  print(f"SKIP {full_key} (exists; replace_existing=False)")
195
  else:
 
151
  r.raise_for_status()
152
  return r.json()["data"]["files"][0]["dataFile"]["id"]
153
 
154
+ def _replace_file(file_id: int, path: Path, *, directory_label: Optional[str] = None, label: Optional[str] = None, force: bool = True, restrict: bool = False) -> int:
155
  url = f"{DV_API}/files/{file_id}/replace"
156
  mime = mimetypes.guess_type(str(path))[0] or "application/octet-stream"
157
+
158
+ meta = {"forceReplace": bool(force), "restrict": bool(restrict)}
159
+ if directory_label is not None:
160
+ meta["directoryLabel"] = directory_label # <-- keep file in the same folder
161
+ if label is not None:
162
+ meta["label"] = label # optional; controls display name
163
+
164
+ data = {"jsonData": json.dumps(meta)}
165
+
166
  with path.open("rb") as fh:
167
  files = {"file": (path.name, fh, mime)}
168
  r = requests.post(url, data=data, files=files, headers=_headers(), timeout=600)
 
197
  if full_key in remote and replace_existing:
198
  fid = remote[full_key]["id"]
199
  print(f"REPLACE {full_key} id={fid}")
200
+ # compute the target folder and filename for metadata:
201
+ directory = str(PurePosixPath(full_key).parent)
202
+ directory = "" if directory == "." else directory
203
+ filename = PurePosixPath(full_key).name
204
+ _replace_file(fid, abs_path, directory_label=directory, label=filename)
205
  elif full_key in remote and not replace_existing:
206
  print(f"SKIP {full_key} (exists; replace_existing=False)")
207
  else:
bike_bench_internal/src/bikebench/design_evaluation/design_evaluation.py CHANGED
@@ -39,7 +39,11 @@ class EvaluationFunction(ABC):
39
  pass
40
 
41
  @abstractmethod # 1 = objective, 0 = constraint
42
- def return_types(self) -> List[str]:
 
 
 
 
43
  pass
44
 
45
  @abstractmethod
@@ -52,7 +56,7 @@ class AeroEvaluator(EvaluationFunction):
52
  super().__init__(device, dtype)
53
  state_path = models_and_scalers_path("aero_model_weights.pt")
54
  scaler_path = models_and_scalers_path("aero_scaler.pt")
55
- state = torch.load(state_path, weights_only=True, map_location=torch.device(device))
56
  self.model = get_aero_model(dropout_on=False).to(device)
57
  self.model.load_state_dict(state)
58
  self.preprocessor = Preprocessor(scaler_path=scaler_path, preprocess_fn=aero_predictor.calculate_features, device=device)
@@ -68,7 +72,10 @@ class AeroEvaluator(EvaluationFunction):
68
  def return_names(self) -> List[str]:
69
  return ['Drag Force (N)']
70
 
71
- def return_types(self) -> List[str]:
 
 
 
72
  return [1]
73
 
74
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
@@ -92,7 +99,7 @@ class FrameValidityEvaluator(EvaluationFunction):
92
  super().__init__(device, dtype)
93
  state_path = models_and_scalers_path("validity_model_weights.pt")
94
  scaler_path = models_and_scalers_path("validity_scaler.pt")
95
- state = torch.load(state_path, weights_only=True, map_location=torch.device(device))
96
  self.model = get_validity_model(dropout_on=False).to(device)
97
  self.model.load_state_dict(state)
98
  self.preprocessor = Preprocessor(scaler_path=scaler_path, preprocess_fn=None, device=device)
@@ -105,7 +112,10 @@ class FrameValidityEvaluator(EvaluationFunction):
105
  def return_names(self) -> List[str]:
106
  return ['Predicted Frame Validity']
107
 
108
- def return_types(self) -> List[str]:
 
 
 
109
  return [0]
110
 
111
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
@@ -122,7 +132,7 @@ class StructuralEvaluator(EvaluationFunction):
122
  super().__init__(device, dtype)
123
  state_path = models_and_scalers_path("structural_model_weights.pt")
124
  scaler_path = models_and_scalers_path("structural_scaler.pt")
125
- state = torch.load(state_path, weights_only=True, map_location=torch.device(device))
126
  self.model = get_structural_model(dropout_on = False).to(device)
127
  self.model.load_state_dict(state)
128
  self.preprocessor = Preprocessor(scaler_path=scaler_path, preprocess_fn=None, device=device)
@@ -134,8 +144,11 @@ class StructuralEvaluator(EvaluationFunction):
134
  def return_names(self) -> List[str]:
135
  return ['Mass (kg)', 'Planar Compliance Score', 'Transverse Compliance Score', 'Eccentric Compliance Score', 'Planar Safety Factor', 'Eccentric Safety Factor']
136
 
137
- def return_types(self) -> List[str]:
138
- return [1,1,1,1,0,0]
 
 
 
139
 
140
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
141
  framed_tensor = self.converter(designs)
@@ -157,32 +170,32 @@ class AestheticsEvaluator(EvaluationFunction):
157
  scaler_path = models_and_scalers_path("aesthetics_scaler.pt")
158
  self.preprocessor = Preprocessor(
159
  scaler_path=scaler_path,
160
- preprocess_fn=aesthetics_predictor.remove_wall_thickness,
161
  device=device
162
  )
163
- state = torch.load(state_path, weights_only=True, map_location=torch.device(device))
164
  self.model = get_aesthetics_model(dropout_on = False).to(device)
165
  self.model.load_state_dict(state)
166
  self.model.eval()
 
167
 
168
  self.mode = mode # "Image", "Image Path", or "Text"
169
- self.embedding_model = clip_embedding_calculator.ClipEmbeddingCalculator(
170
- device=self.device,
171
- batch_size=batch_size
172
- )
173
 
174
  def variable_names(self) -> List[str]:
175
  return ordered_columns.bike_bench_columns
176
 
177
  def return_names(self) -> List[str]:
178
  # if self.mode in ["Image", "Image Path"]:
179
- # return ["Cosine Distance to Image"]
180
  if self.mode == "Text":
181
- return ["Cosine Distance to Text"]
182
  elif self.mode == "Embedding":
183
- return ["Cosine Distance to Embedding"]
184
 
185
- def return_types(self) -> List[str]:
 
 
 
186
  return [1]
187
 
188
  def evaluate(self,
@@ -219,6 +232,14 @@ class AestheticsEvaluator(EvaluationFunction):
219
  texts = list(cond)
220
  else:
221
  raise TypeError("For Text mode, conditioning must be text or list of texts")
 
 
 
 
 
 
 
 
222
  embed = self.embedding_model.embed_texts(texts)
223
  elif self.mode == "Embedding":
224
  if isinstance(cond, torch.Tensor):
@@ -266,7 +287,10 @@ class ValidationEvaluator(EvaluationFunction):
266
  def return_names(self) -> List[str]:
267
  return self.validation_names
268
 
269
- def return_types(self) -> List[str]:
 
 
 
270
  return [0] * len(self.validation_names)
271
 
272
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
@@ -299,13 +323,22 @@ class ErgonomicsEvaluator(EvaluationFunction):
299
  "Arm Too Long for Bike", "Saddle Too Far From Handle", "Torso Too Long for Bike",
300
  "Saddle Too Far From Crank", "Upper Leg Too Long for Bike", "Lower Leg Too Long for Bike"]
301
 
302
- def return_types(self) -> List[str]:
303
  if self.penalize_constraints:
304
  return [1, 1, 1]
305
  elif self.constraints_only:
306
  return [0, 0, 0, 0, 0, 0]
307
  else:
308
  return [1, 1, 1, 0, 0, 0, 0, 0, 0]
 
 
 
 
 
 
 
 
 
309
 
310
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
311
  assert "Rider" in conditioning, "Rider dimensions must be provided in conditioning to calculate ergonomics."
@@ -373,7 +406,7 @@ class UsabilityEvaluator(EvaluationFunction):
373
  super().__init__(device, dtype)
374
  scaler_path = models_and_scalers_path("usability_scaler.pt")
375
  state_path = models_and_scalers_path("usability_model_weights.pt")
376
- state = torch.load(state_path, weights_only=True, map_location=torch.device(device))
377
  self.model = get_usability_model(dropout_on=False).to(device)
378
  self.model.load_state_dict(state)
379
  self.preprocessor = Preprocessor(scaler_path=scaler_path, preprocess_fn=None, device=device)
@@ -385,8 +418,11 @@ class UsabilityEvaluator(EvaluationFunction):
385
  return ['Usability Score']
386
 
387
 
388
- def return_types(self) -> List[str]:
389
  return [1]
 
 
 
390
 
391
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
392
  designs = self.preprocessor(designs)
@@ -461,10 +497,12 @@ def construct_tensor_evaluator(evaluation_functions: List[EvaluationFunction], c
461
 
462
  # Flatten all return names across evaluators
463
  all_return_names = []
464
- all_return_types = []
 
465
  for evaluation_function in evaluation_functions:
466
  all_return_names.extend(evaluation_function.return_names())
467
- all_return_types.extend(evaluation_function.return_types())
 
468
 
469
  def evaluate_tensor(designs: torch.Tensor, conditioning={}) -> torch.Tensor:
470
  n = designs.shape[0]
@@ -496,13 +534,13 @@ def construct_tensor_evaluator(evaluation_functions: List[EvaluationFunction], c
496
 
497
  return results_tensor
498
 
499
- return evaluate_tensor, all_return_names, all_return_types
500
 
501
  def construct_dataframe_evaluator(evaluation_functions: List[EvaluationFunction]):
502
 
503
  def evaluate_dataframe(designs: pd.DataFrame, conditioning={}) -> pd.DataFrame:
504
  designs_tensor = torch.tensor(designs.values, dtype=torch.float32)
505
- tensor_evaluator, return_names, return_types = construct_tensor_evaluator(evaluation_functions, list(designs.columns))
506
  results_tensor = tensor_evaluator(designs_tensor, conditioning)
507
 
508
  results_df = pd.DataFrame(
@@ -511,7 +549,7 @@ def construct_dataframe_evaluator(evaluation_functions: List[EvaluationFunction]
511
  index=designs.index
512
  )
513
 
514
- return results_df, return_types
515
 
516
  return evaluate_dataframe
517
 
 
39
  pass
40
 
41
  @abstractmethod # 1 = objective, 0 = constraint
42
+ def is_objective(self) -> List[str]:
43
+ pass
44
+
45
+ @abstractmethod
46
+ def is_conditional(self) -> List[str]:
47
  pass
48
 
49
  @abstractmethod
 
56
  super().__init__(device, dtype)
57
  state_path = models_and_scalers_path("aero_model_weights.pt")
58
  scaler_path = models_and_scalers_path("aero_scaler.pt")
59
+ state = torch.load(state_path, weights_only=True, map_location=device)
60
  self.model = get_aero_model(dropout_on=False).to(device)
61
  self.model.load_state_dict(state)
62
  self.preprocessor = Preprocessor(scaler_path=scaler_path, preprocess_fn=aero_predictor.calculate_features, device=device)
 
72
  def return_names(self) -> List[str]:
73
  return ['Drag Force (N)']
74
 
75
+ def is_objective(self) -> List[str]:
76
+ return [1]
77
+
78
+ def is_conditional(self) -> List[str]:
79
  return [1]
80
 
81
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
 
99
  super().__init__(device, dtype)
100
  state_path = models_and_scalers_path("validity_model_weights.pt")
101
  scaler_path = models_and_scalers_path("validity_scaler.pt")
102
+ state = torch.load(state_path, weights_only=True, map_location=device)
103
  self.model = get_validity_model(dropout_on=False).to(device)
104
  self.model.load_state_dict(state)
105
  self.preprocessor = Preprocessor(scaler_path=scaler_path, preprocess_fn=None, device=device)
 
112
  def return_names(self) -> List[str]:
113
  return ['Predicted Frame Validity']
114
 
115
+ def is_objective(self) -> List[str]:
116
+ return [0]
117
+
118
+ def is_conditional(self) -> List[str]:
119
  return [0]
120
 
121
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
 
132
  super().__init__(device, dtype)
133
  state_path = models_and_scalers_path("structural_model_weights.pt")
134
  scaler_path = models_and_scalers_path("structural_scaler.pt")
135
+ state = torch.load(state_path, weights_only=True, map_location=device)
136
  self.model = get_structural_model(dropout_on = False).to(device)
137
  self.model.load_state_dict(state)
138
  self.preprocessor = Preprocessor(scaler_path=scaler_path, preprocess_fn=None, device=device)
 
144
  def return_names(self) -> List[str]:
145
  return ['Mass (kg)', 'Planar Compliance Score', 'Transverse Compliance Score', 'Eccentric Compliance Score', 'Planar Safety Factor', 'Eccentric Safety Factor']
146
 
147
+ def is_objective(self) -> List[str]:
148
+ return [1, 1, 1, 1, 0, 0]
149
+
150
+ def is_conditional(self) -> List[str]:
151
+ return [0, 0, 0, 0, 0, 0]
152
 
153
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
154
  framed_tensor = self.converter(designs)
 
170
  scaler_path = models_and_scalers_path("aesthetics_scaler.pt")
171
  self.preprocessor = Preprocessor(
172
  scaler_path=scaler_path,
173
+ preprocess_fn=aesthetics_predictor.remove_wall_thickness_and_material,
174
  device=device
175
  )
176
+ state = torch.load(state_path, weights_only=True, map_location=device)
177
  self.model = get_aesthetics_model(dropout_on = False).to(device)
178
  self.model.load_state_dict(state)
179
  self.model.eval()
180
+ self.batch_size = batch_size
181
 
182
  self.mode = mode # "Image", "Image Path", or "Text"
 
 
 
 
183
 
184
  def variable_names(self) -> List[str]:
185
  return ordered_columns.bike_bench_columns
186
 
187
  def return_names(self) -> List[str]:
188
  # if self.mode in ["Image", "Image Path"]:
189
+ # return ["Cosine Distance To Image"]
190
  if self.mode == "Text":
191
+ return ["Cosine Distance To Text"]
192
  elif self.mode == "Embedding":
193
+ return ["Cosine Distance To Embedding"]
194
 
195
+ def is_objective(self) -> List[str]:
196
+ return [1]
197
+
198
+ def is_conditional(self) -> List[str]:
199
  return [1]
200
 
201
  def evaluate(self,
 
232
  texts = list(cond)
233
  else:
234
  raise TypeError("For Text mode, conditioning must be text or list of texts")
235
+
236
+ # Initialize embedding model if not already done
237
+ if not hasattr(self, 'embedding_model'):
238
+ self.embedding_model = clip_embedding_calculator.ClipEmbeddingCalculator(
239
+ device=self.device,
240
+ batch_size=self.batch_size
241
+ )
242
+
243
  embed = self.embedding_model.embed_texts(texts)
244
  elif self.mode == "Embedding":
245
  if isinstance(cond, torch.Tensor):
 
287
  def return_names(self) -> List[str]:
288
  return self.validation_names
289
 
290
+ def is_objective(self) -> List[str]:
291
+ return [0] * len(self.validation_names)
292
+
293
+ def is_conditional(self) -> List[str]:
294
  return [0] * len(self.validation_names)
295
 
296
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
 
323
  "Arm Too Long for Bike", "Saddle Too Far From Handle", "Torso Too Long for Bike",
324
  "Saddle Too Far From Crank", "Upper Leg Too Long for Bike", "Lower Leg Too Long for Bike"]
325
 
326
+ def is_objective(self) -> List[str]:
327
  if self.penalize_constraints:
328
  return [1, 1, 1]
329
  elif self.constraints_only:
330
  return [0, 0, 0, 0, 0, 0]
331
  else:
332
  return [1, 1, 1, 0, 0, 0, 0, 0, 0]
333
+
334
+ def is_conditional(self) -> List[str]:
335
+ if self.penalize_constraints:
336
+ return [1, 1, 1]
337
+ elif self.constraints_only:
338
+ return [1, 1, 1, 1, 1, 1]
339
+ else:
340
+ return [1, 1, 1, 1, 1, 1, 1, 1, 1]
341
+
342
 
343
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
344
  assert "Rider" in conditioning, "Rider dimensions must be provided in conditioning to calculate ergonomics."
 
406
  super().__init__(device, dtype)
407
  scaler_path = models_and_scalers_path("usability_scaler.pt")
408
  state_path = models_and_scalers_path("usability_model_weights.pt")
409
+ state = torch.load(state_path, weights_only=True, map_location=device)
410
  self.model = get_usability_model(dropout_on=False).to(device)
411
  self.model.load_state_dict(state)
412
  self.preprocessor = Preprocessor(scaler_path=scaler_path, preprocess_fn=None, device=device)
 
418
  return ['Usability Score']
419
 
420
 
421
+ def is_objective(self) -> List[str]:
422
  return [1]
423
+
424
+ def is_conditional(self) -> List[str]:
425
+ return [0]
426
 
427
  def evaluate(self, designs: torch.Tensor, conditioning: dict = {}) -> torch.Tensor:
428
  designs = self.preprocessor(designs)
 
497
 
498
  # Flatten all return names across evaluators
499
  all_return_names = []
500
+ all_is_objective = []
501
+ all_is_conditional = []
502
  for evaluation_function in evaluation_functions:
503
  all_return_names.extend(evaluation_function.return_names())
504
+ all_is_objective.extend(evaluation_function.is_objective())
505
+ all_is_conditional.extend(evaluation_function.is_conditional())
506
 
507
  def evaluate_tensor(designs: torch.Tensor, conditioning={}) -> torch.Tensor:
508
  n = designs.shape[0]
 
534
 
535
  return results_tensor
536
 
537
+ return evaluate_tensor, all_return_names, all_is_objective, all_is_conditional
538
 
539
  def construct_dataframe_evaluator(evaluation_functions: List[EvaluationFunction]):
540
 
541
  def evaluate_dataframe(designs: pd.DataFrame, conditioning={}) -> pd.DataFrame:
542
  designs_tensor = torch.tensor(designs.values, dtype=torch.float32)
543
+ tensor_evaluator, return_names, is_objective, all_is_conditional = construct_tensor_evaluator(evaluation_functions, list(designs.columns))
544
  results_tensor = tensor_evaluator(designs_tensor, conditioning)
545
 
546
  results_df = pd.DataFrame(
 
549
  index=designs.index
550
  )
551
 
552
+ return results_df, is_objective, all_is_conditional
553
 
554
  return evaluate_dataframe
555
 
bike_bench_internal/src/bikebench/prediction/aesthetics_predictor.py CHANGED
@@ -6,11 +6,10 @@ from bikebench.resource_utils import models_and_scalers_path
6
  from bikebench.prediction.prediction_utils import TorchStandardScaler
7
 
8
 
9
- def remove_wall_thickness(x, device):
10
- # indices_to_drop = [27, 28, 29, 30, 31, 32, 33]
11
- first_chunk = x[:, :27]
12
- second_chunk = x[:, 34:]
13
- x = torch.cat((first_chunk, second_chunk), dim=1)
14
  return x
15
 
16
  class ResidualBlock(nn.Module):
@@ -55,7 +54,7 @@ class ResidualNetwork(nn.Module):
55
 
56
  def get_aesthetics_model(dropout_on = False):
57
  if dropout_on:
58
- model = ResidualNetwork(80, 512, layer_size=256, layers_per_block=2, num_blocks=3)
59
  else:
60
- model = ResidualNetwork(80, 512, layer_size=256, layers_per_block=2, num_blocks=3)
61
  return model
 
6
  from bikebench.prediction.prediction_utils import TorchStandardScaler
7
 
8
 
9
+ def remove_wall_thickness_and_material(x, device):
10
+ indices_to_drop = [30, 31, 32, 33, 34, 35, 36, 55, 56, 57]
11
+ indices_to_keep = [i for i in range(x.shape[1]) if i not in indices_to_drop]
12
+ x = x[:, indices_to_keep]
 
13
  return x
14
 
15
  class ResidualBlock(nn.Module):
 
54
 
55
  def get_aesthetics_model(dropout_on = False):
56
  if dropout_on:
57
+ model = ResidualNetwork(73, 512, layer_size=256, layers_per_block=2, num_blocks=3)
58
  else:
59
+ model = ResidualNetwork(73, 512, layer_size=256, layers_per_block=2, num_blocks=3)
60
  return model
bike_bench_internal/src/bikebench/transformation/framed.py CHANGED
@@ -64,9 +64,6 @@ BIKEBENCH_TO_FRAMED_IDENTICAL = {
64
  MATERIALS = {"MATERIAL OHCLASS: ALUMINIUM": "Aluminum",
65
  "MATERIAL OHCLASS: STEEL": "Steel",
66
  "MATERIAL OHCLASS: TITANIUM": "Titanium",
67
- "MATERIAL OHCLASS: CARBON": "Steel", # Overridden to Steel
68
- "MATERIAL OHCLASS: BAMBOO": "Steel", # Overridden to Steel
69
- "MATERIAL OHCLASS: OTHER": "Steel" # Overridden to Steel
70
  }
71
  def clip_to_framed(X_clip):
72
  X_framed = pd.DataFrame()
 
64
  MATERIALS = {"MATERIAL OHCLASS: ALUMINIUM": "Aluminum",
65
  "MATERIAL OHCLASS: STEEL": "Steel",
66
  "MATERIAL OHCLASS: TITANIUM": "Titanium",
 
 
 
67
  }
68
  def clip_to_framed(X_clip):
69
  X_framed = pd.DataFrame()
bike_bench_internal/src/bikebench/transformation/one_hot_encoding.py CHANGED
@@ -6,6 +6,7 @@ import pandas as pd
6
  ONE_HOT_ENCODED_BIKEBENCH_COLUMNS: List[str] = [
7
  'MATERIAL',
8
  'Head tube type',
 
9
  'RIM_STYLE front',
10
  'RIM_STYLE rear',
11
  'Handlebar style',
@@ -17,9 +18,6 @@ ONE_HOT_ENCODED_BIKEBENCH_COLUMNS: List[str] = [
17
  ALL_CATEGORIES = {
18
  'MATERIAL': [
19
  'ALUMINIUM',
20
- 'BAMBOO',
21
- 'CARBON',
22
- 'OTHER',
23
  'STEEL',
24
  'TITANIUM'
25
  ],
@@ -29,6 +27,11 @@ ALL_CATEGORIES = {
29
  '2',
30
  '3'
31
  ],
 
 
 
 
 
32
  'RIM_STYLE front': [
33
  'DISC',
34
  'SPOKED',
@@ -127,6 +130,12 @@ def encode_to_continuous(df: pd.DataFrame) -> pd.DataFrame:
127
  for col in BOOLEAN_COLUMNS:
128
  if col in out.columns:
129
  out[col] = out[col].astype(float)
 
 
 
 
 
 
130
 
131
  return out.astype(np.float32)
132
 
 
6
  ONE_HOT_ENCODED_BIKEBENCH_COLUMNS: List[str] = [
7
  'MATERIAL',
8
  'Head tube type',
9
+ 'Down tube type',
10
  'RIM_STYLE front',
11
  'RIM_STYLE rear',
12
  'Handlebar style',
 
18
  ALL_CATEGORIES = {
19
  'MATERIAL': [
20
  'ALUMINIUM',
 
 
 
21
  'STEEL',
22
  'TITANIUM'
23
  ],
 
27
  '2',
28
  '3'
29
  ],
30
+ 'Down tube type': [
31
+ '0',
32
+ '1',
33
+ '2'
34
+ ],
35
  'RIM_STYLE front': [
36
  'DISC',
37
  'SPOKED',
 
130
  for col in BOOLEAN_COLUMNS:
131
  if col in out.columns:
132
  out[col] = out[col].astype(float)
133
+
134
+ #replace any non-floats with 0.0
135
+ for col in out.columns:
136
+ if out[col].dtype == object:
137
+ out[col] = pd.to_numeric(out[col], errors='coerce').fillna(0.0)
138
+ print(f"⚠️ Warning: Column '{col}' contained non-numeric values which were converted to 0.0")
139
 
140
  return out.astype(np.float32)
141
 
bike_bench_internal/src/bikebench/transformation/ordered_columns.py CHANGED
@@ -10,17 +10,17 @@ bike_bench_columns = ['Seatpost LENGTH', 'CS textfield', 'BB textfield', 'Stack'
10
  'Wall thickness Head tube', 'Wall thickness Down tube',
11
  'Wall thickness Chain stay', 'Wall thickness Seat stay',
12
  'Wall thickness Seat tube', 'Wheel diameter front', 'RDBSD',
13
- 'Wheel diameter rear', 'FDBSD', 'Display AEROBARS', 'BB length',
14
- 'Wheel cut', 'Front Fender include', 'Rear Fender include',
15
  'BELTorCHAIN', 'Number of cogs', 'Number of chainrings',
16
  'FIRST color R_RGB', 'FIRST color G_RGB', 'FIRST color B_RGB',
17
  'SPOKES composite front', 'SPOKES composite rear', 'SBLADEW front',
18
- 'SBLADEW rear', 'Saddle length', 'Saddle height', 'Down tube type',
19
- 'MATERIAL OHCLASS: ALUMINIUM', 'MATERIAL OHCLASS: BAMBOO',
20
- 'MATERIAL OHCLASS: CARBON', 'MATERIAL OHCLASS: OTHER',
21
  'MATERIAL OHCLASS: STEEL', 'MATERIAL OHCLASS: TITANIUM',
22
  'Head tube type OHCLASS: 0', 'Head tube type OHCLASS: 1',
23
  'Head tube type OHCLASS: 2', 'Head tube type OHCLASS: 3',
 
 
24
  'RIM_STYLE front OHCLASS: DISC', 'RIM_STYLE front OHCLASS: SPOKED',
25
  'RIM_STYLE front OHCLASS: TRISPOKE', 'RIM_STYLE rear OHCLASS: DISC',
26
  'RIM_STYLE rear OHCLASS: SPOKED', 'RIM_STYLE rear OHCLASS: TRISPOKE',
@@ -34,9 +34,6 @@ bike_bench_columns = ['Seatpost LENGTH', 'CS textfield', 'BB textfield', 'Stack'
34
  oh_columns = [
35
  [
36
  "MATERIAL OHCLASS: ALUMINIUM",
37
- "MATERIAL OHCLASS: BAMBOO",
38
- "MATERIAL OHCLASS: CARBON",
39
- "MATERIAL OHCLASS: OTHER",
40
  "MATERIAL OHCLASS: STEEL",
41
  "MATERIAL OHCLASS: TITANIUM",
42
  ],
@@ -46,6 +43,11 @@ oh_columns = [
46
  "Head tube type OHCLASS: 2",
47
  "Head tube type OHCLASS: 3",
48
  ],
 
 
 
 
 
49
  [
50
  "RIM_STYLE front OHCLASS: DISC",
51
  "RIM_STYLE front OHCLASS: SPOKED",
@@ -81,9 +83,6 @@ oh_columns = [
81
 
82
  oh_bool_columns = [
83
  "MATERIAL OHCLASS: ALUMINIUM",
84
- "MATERIAL OHCLASS: BAMBOO",
85
- "MATERIAL OHCLASS: CARBON",
86
- "MATERIAL OHCLASS: OTHER",
87
  "MATERIAL OHCLASS: STEEL",
88
  "MATERIAL OHCLASS: TITANIUM",
89
 
@@ -92,6 +91,10 @@ oh_bool_columns = [
92
  "Head tube type OHCLASS: 2",
93
  "Head tube type OHCLASS: 3",
94
 
 
 
 
 
95
  "RIM_STYLE front OHCLASS: DISC",
96
  "RIM_STYLE front OHCLASS: SPOKED",
97
  "RIM_STYLE front OHCLASS: TRISPOKE",
 
10
  'Wall thickness Head tube', 'Wall thickness Down tube',
11
  'Wall thickness Chain stay', 'Wall thickness Seat stay',
12
  'Wall thickness Seat tube', 'Wheel diameter front', 'RDBSD',
13
+ 'Wheel diameter rear', 'FDBSD', 'BB length', 'Wheel cut',
 
14
  'BELTorCHAIN', 'Number of cogs', 'Number of chainrings',
15
  'FIRST color R_RGB', 'FIRST color G_RGB', 'FIRST color B_RGB',
16
  'SPOKES composite front', 'SPOKES composite rear', 'SBLADEW front',
17
+ 'SBLADEW rear', 'Saddle length', 'Saddle height',
18
+ 'MATERIAL OHCLASS: ALUMINIUM',
 
19
  'MATERIAL OHCLASS: STEEL', 'MATERIAL OHCLASS: TITANIUM',
20
  'Head tube type OHCLASS: 0', 'Head tube type OHCLASS: 1',
21
  'Head tube type OHCLASS: 2', 'Head tube type OHCLASS: 3',
22
+ 'Down tube type OHCLASS: 0', 'Down tube type OHCLASS: 1',
23
+ 'Down tube type OHCLASS: 2',
24
  'RIM_STYLE front OHCLASS: DISC', 'RIM_STYLE front OHCLASS: SPOKED',
25
  'RIM_STYLE front OHCLASS: TRISPOKE', 'RIM_STYLE rear OHCLASS: DISC',
26
  'RIM_STYLE rear OHCLASS: SPOKED', 'RIM_STYLE rear OHCLASS: TRISPOKE',
 
34
  oh_columns = [
35
  [
36
  "MATERIAL OHCLASS: ALUMINIUM",
 
 
 
37
  "MATERIAL OHCLASS: STEEL",
38
  "MATERIAL OHCLASS: TITANIUM",
39
  ],
 
43
  "Head tube type OHCLASS: 2",
44
  "Head tube type OHCLASS: 3",
45
  ],
46
+ [
47
+ "Down tube type OHCLASS: 0",
48
+ "Down tube type OHCLASS: 1",
49
+ "Down tube type OHCLASS: 2",
50
+ ],
51
  [
52
  "RIM_STYLE front OHCLASS: DISC",
53
  "RIM_STYLE front OHCLASS: SPOKED",
 
83
 
84
  oh_bool_columns = [
85
  "MATERIAL OHCLASS: ALUMINIUM",
 
 
 
86
  "MATERIAL OHCLASS: STEEL",
87
  "MATERIAL OHCLASS: TITANIUM",
88
 
 
91
  "Head tube type OHCLASS: 2",
92
  "Head tube type OHCLASS: 3",
93
 
94
+ "Down tube type OHCLASS: 0",
95
+ "Down tube type OHCLASS: 1",
96
+ "Down tube type OHCLASS: 2",
97
+
98
  "RIM_STYLE front OHCLASS: DISC",
99
  "RIM_STYLE front OHCLASS: SPOKED",
100
  "RIM_STYLE front OHCLASS: TRISPOKE",
bike_bench_internal/src/bikebench/validation/bike_bench_validation_functions.py CHANGED
@@ -25,42 +25,42 @@ ZERO_IS_VALID_COLS = ['FIRST color R_RGB',
25
  'FIRST color G_RGB', 'FIRST color B_RGB']
26
 
27
  class SaddleHeightTooSmall(ValidationFunction):
28
- def friendly_name(self) -> str: return "Saddle height too small"
29
  def variable_names(self) -> List[str]: return ["Saddle height"]
30
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
31
  return 100.0 - ctx.col("Saddle height")
32
 
33
 
34
  class SaddleCollidesWithSeatTube(ValidationFunction):
35
- def friendly_name(self) -> str: return "Saddle collides with seat tube"
36
  def variable_names(self) -> List[str]: return ["Saddle height", "Seat tube length"]
37
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
38
  return ctx.col("Seat tube length") + 40.0 - ctx.col("Saddle height")
39
 
40
 
41
  class SaddleTooShort(ValidationFunction):
42
- def friendly_name(self) -> str: return "Saddle too short"
43
  def variable_names(self) -> List[str]: return ["Saddle length"]
44
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
45
  return 228.0 - ctx.col("Saddle length")
46
 
47
 
48
  class HeadAngleOverLimit(ValidationFunction):
49
- def friendly_name(self) -> str: return "Head angle over limit"
50
  def variable_names(self) -> List[str]: return ["Head angle"]
51
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
52
  return ctx.col("Head angle") - 180.0
53
 
54
 
55
  class SeatAngleOverLimit(ValidationFunction):
56
- def friendly_name(self) -> str: return "Seat angle over limit"
57
  def variable_names(self) -> List[str]: return ["Seat angle"]
58
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
59
  return ctx.col("Seat angle") - 180.0
60
 
61
 
62
  class SeatPostTooShort(ValidationFunction):
63
- def friendly_name(self) -> str: return "Seat post too short"
64
  def variable_names(self) -> List[str]:
65
  return ["Seat tube length", "Seatpost LENGTH", "Saddle height", "Seat angle"]
66
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
@@ -73,7 +73,7 @@ class SeatPostTooShort(ValidationFunction):
73
 
74
 
75
  class SeatPostTooLong(ValidationFunction):
76
- def friendly_name(self) -> str: return "Seat post too long"
77
  def variable_names(self) -> List[str]:
78
  return ["Seatpost LENGTH", "Saddle height", "Seat angle"]
79
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
@@ -83,7 +83,7 @@ class SeatPostTooLong(ValidationFunction):
83
 
84
 
85
  class RearWheelInnerDiameterTooSmall(ValidationFunction):
86
- def friendly_name(self) -> str: return "Rear Wheel inner diameter too small"
87
  def variable_names(self) -> List[str]: return ["Wheel diameter rear", "RDBSD"]
88
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
89
  inner_d = ctx.col("Wheel diameter rear") - 2.0 * ctx.col("RDBSD")
@@ -91,7 +91,7 @@ class RearWheelInnerDiameterTooSmall(ValidationFunction):
91
 
92
 
93
  class FrontWheelInnerDiameterTooSmall(ValidationFunction):
94
- def friendly_name(self) -> str: return "Front Wheel inner diameter too small"
95
  def variable_names(self) -> List[str]: return ["Wheel diameter front", "FDBSD"]
96
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
97
  inner_d = ctx.col("Wheel diameter front") - 2.0 * ctx.col("FDBSD")
@@ -99,14 +99,14 @@ class FrontWheelInnerDiameterTooSmall(ValidationFunction):
99
 
100
 
101
  class SeatTubeExtensionLongerThanSeatTube(ValidationFunction):
102
- def friendly_name(self) -> str: return "Seat tube extension longer than seat tube"
103
  def variable_names(self) -> List[str]: return ["Seat tube length", "Seat tube extension2"]
104
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
105
  return ctx.col("Seat tube extension2") - ctx.col("Seat tube length")
106
 
107
 
108
  class HeadTubeUpperExtensionAndLowerExtensionOverlap(ValidationFunction):
109
- def friendly_name(self) -> str: return "Head tube upper extension and lower extension overlap"
110
  def variable_names(self) -> List[str]:
111
  return ["Head tube length textfield", "Head tube upper extension2", "Head tube lower extension2"]
112
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
@@ -114,14 +114,14 @@ class HeadTubeUpperExtensionAndLowerExtensionOverlap(ValidationFunction):
114
 
115
 
116
  class SeatStayJunctionLongerThanSeatTube(ValidationFunction):
117
- def friendly_name(self) -> str: return "Seat stay junction longer than seat tube"
118
  def variable_names(self) -> List[str]: return ["Seat tube length", "Seat stay junction0"]
119
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
120
  return ctx.col("Seat stay junction0") - ctx.col("Seat tube length")
121
 
122
 
123
  class NonNegativeParameterIsNegative(ValidationFunction):
124
- def friendly_name(self) -> str: return "Non-negative parameter is negative"
125
  def variable_names(self) -> List[str]: return POSITIVE_COLS
126
  def validate(self, ctx: 'FeatureStore') -> torch.Tensor:
127
  X = torch.stack([ctx.col(c) for c in POSITIVE_COLS], dim=1) # (n, k)
@@ -135,21 +135,21 @@ class NonNegativeParameterIsNegative(ValidationFunction):
135
 
136
 
137
  class ChainStaySmallerThanRearWheelRadius(ValidationFunction):
138
- def friendly_name(self) -> str: return "Chain stay smaller than rear wheel radius"
139
  def variable_names(self) -> List[str]: return ["CS textfield", "Wheel diameter rear"]
140
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
141
  return (ctx.col("Wheel diameter rear") * 0.5) - ctx.col("CS textfield")
142
 
143
 
144
  class ChainStayShorterThanBBDrop(ValidationFunction):
145
- def friendly_name(self) -> str: return "Chain stay shorter than BB drop"
146
  def variable_names(self) -> List[str]: return ["CS textfield", "BB textfield"]
147
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
148
  return ctx.col("BB textfield") - ctx.col("CS textfield")
149
 
150
 
151
  class SeatStaySmallerThanRearWheelRadius(ValidationFunction):
152
- def friendly_name(self) -> str: return "Seat stay smaller than rear wheel radius"
153
  def variable_names(self) -> List[str]:
154
  return ["CS textfield", "BB textfield","Seat tube length", "Seat stay junction0", "Seat angle", "Wheel diameter rear"]
155
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
@@ -166,9 +166,9 @@ class SeatStaySmallerThanRearWheelRadius(ValidationFunction):
166
  return (ctx.col("Wheel diameter rear") * 0.5) - g
167
 
168
 
169
- class SeatTubeIntersectsRearWheel(ValidationFunction):
170
  def friendly_name(self) -> str:
171
- return "Seat Tube Intersects Rear Wheel"
172
 
173
  def variable_names(self) -> List[str]:
174
  return [
@@ -206,7 +206,7 @@ class SeatTubeIntersectsRearWheel(ValidationFunction):
206
 
207
 
208
  class DownTubeCantReachHeadTube(ValidationFunction):
209
- def friendly_name(self) -> str: return "Down tube can't reach head tube"
210
  def variable_names(self) -> List[str]:
211
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head angle", "DT Length"]
212
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
@@ -215,7 +215,7 @@ class DownTubeCantReachHeadTube(ValidationFunction):
215
 
216
  class RearWheelCutoutSeversSeatTube(ValidationFunction):
217
  def friendly_name(self) -> str:
218
- return "Rear wheel cutout severs seat tube"
219
 
220
  def variable_names(self) -> List[str]:
221
  return [
@@ -249,8 +249,8 @@ class RearWheelCutoutSeversSeatTube(ValidationFunction):
249
  q = torch.where(aero_mask, q_true, q_false)
250
  return (wheel_cut * 0.5) - q
251
 
252
- class FootIntersectsFrontWheel(ValidationFunction):
253
- def friendly_name(self) -> str: return "Foot intersects front wheel"
254
  def variable_names(self) -> List[str]:
255
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head angle",
256
  "BB textfield", "DT Length", "FORK0R", "Wheel diameter front", "Wheel diameter rear"]
@@ -264,14 +264,14 @@ class FootIntersectsFrontWheel(ValidationFunction):
264
 
265
 
266
  class CrankHitsGroundInLowestPosition(ValidationFunction):
267
- def friendly_name(self) -> str: return "Crank hits ground in lowest position"
268
  def variable_names(self) -> List[str]: return ["BB textfield", "Wheel diameter rear"]
269
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
270
  return (187.5 + ctx.col("BB textfield")) - (ctx.col("Wheel diameter rear") * 0.5)
271
 
272
 
273
- class RGBvalueGreaterThan255(ValidationFunction):
274
- def friendly_name(self) -> str: return "RGB value greater than 255"
275
  def variable_names(self) -> List[str]:
276
  return ["FIRST color R_RGB", "FIRST color G_RGB", "FIRST color B_RGB"]
277
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
@@ -285,14 +285,14 @@ class RGBvalueGreaterThan255(ValidationFunction):
285
 
286
 
287
  class ChainStaysIntersect(ValidationFunction):
288
- def friendly_name(self) -> str: return "Chain stays intersect"
289
  def variable_names(self) -> List[str]: return ["csd", "Chain stay position on BB","BB length"]
290
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
291
  return (ctx.col("csd")*0.5 + ctx.col("Chain stay position on BB")) - (ctx.col("BB length")*0.5)
292
 
293
 
294
  class TubeWallThicknessExceedsRadius(ValidationFunction):
295
- def friendly_name(self) -> str: return "Tube wall thickness exceeds radius"
296
  def variable_names(self) -> List[str]:
297
  return [
298
  "ttd", "Wall thickness Top tube",
@@ -312,8 +312,8 @@ class TubeWallThicknessExceedsRadius(ValidationFunction):
312
  return torch.sum(torch.clamp_min(violation, 0.0), dim=1)
313
 
314
 
315
- class SeatTubeInnerDiameterThinnerThanSeatPostOuterDiameter(ValidationFunction):
316
- def friendly_name(self) -> str: return "Seat tube inner diameter thinner than seat post outer diameter"
317
  def variable_names(self) -> List[str]: return ["std", "Wall thickness Seat tube"]
318
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
319
  inner_d = ctx.col("std") - ctx.col("Wall thickness Seat tube")
@@ -321,7 +321,7 @@ class SeatTubeInnerDiameterThinnerThanSeatPostOuterDiameter(ValidationFunction):
321
 
322
 
323
  class DownTubeImproperlyJoinsHeadTube(ValidationFunction):
324
- def friendly_name(self) -> str: return "Down tube improperly joins head tube"
325
  def variable_names(self) -> List[str]:
326
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head angle",
327
  "DT Length", "dtd", "htd"]
@@ -347,7 +347,7 @@ class DownTubeImproperlyJoinsHeadTube(ValidationFunction):
347
 
348
 
349
  class TopTubeImproperlyJoinsHeadTube(ValidationFunction):
350
- def friendly_name(self) -> str: return "Top tube improperly joins head tube"
351
  def variable_names(self) -> List[str]:
352
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head tube upper extension2",
353
  "Seat tube extension2", "Head angle", "Seat angle", "DT Length", "ttd", "htd","Seat tube length"]
@@ -376,7 +376,7 @@ class TopTubeImproperlyJoinsHeadTube(ValidationFunction):
376
 
377
 
378
  class TopTubeImproperlyJoinsSeatTube(ValidationFunction):
379
- def friendly_name(self) -> str: return "Top tube improperly joins seat tube"
380
  def variable_names(self) -> List[str]:
381
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head tube upper extension2",
382
  "Seat tube extension2", "Head angle", "Seat angle", "DT Length", "ttd", "std","Seat tube length"]
@@ -405,8 +405,8 @@ class TopTubeImproperlyJoinsSeatTube(ValidationFunction):
405
  return L1 + L2 - stux + below_0_pen + above_pi_pen + seatpost_clamp_default_offset
406
 
407
 
408
- class DownTubeIntersectsFrontWheel(ValidationFunction):
409
- def friendly_name(self) -> str: return "Down tube intersects front wheel"
410
  def variable_names(self) -> List[str]:
411
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head angle",
412
  "DT Length", "BB textfield", "FORK0R", "Wheel diameter front", "Wheel diameter rear", "dtd"]
@@ -427,7 +427,7 @@ class DownTubeIntersectsFrontWheel(ValidationFunction):
427
 
428
 
429
  class SaddleHitsTopTube(ValidationFunction):
430
- def friendly_name(self) -> str: return "Saddle hits top tube"
431
  def variable_names(self) -> List[str]:
432
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head tube upper extension2",
433
  "Seat tube extension2", "Head angle", "Seat angle", "DT Length", "ttd", "std",
@@ -465,7 +465,7 @@ class SaddleHitsTopTube(ValidationFunction):
465
 
466
 
467
  class SaddleHitsHeadTube(ValidationFunction):
468
- def friendly_name(self) -> str: return "Saddle hits head tube"
469
  def variable_names(self) -> List[str]:
470
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head tube upper extension2",
471
  "Seat tube extension2", "Head angle", "Seat angle", "DT Length", "ttd", "htd",
@@ -519,19 +519,19 @@ bike_bench_validation_functions: List[ValidationFunction] = [
519
  ChainStaySmallerThanRearWheelRadius(),
520
  ChainStayShorterThanBBDrop(),
521
  SeatStaySmallerThanRearWheelRadius(),
522
- SeatTubeIntersectsRearWheel(),
523
  DownTubeCantReachHeadTube(),
524
  RearWheelCutoutSeversSeatTube(),
525
- FootIntersectsFrontWheel(),
526
  CrankHitsGroundInLowestPosition(),
527
- RGBvalueGreaterThan255(),
528
  ChainStaysIntersect(),
529
  TubeWallThicknessExceedsRadius(),
530
- SeatTubeInnerDiameterThinnerThanSeatPostOuterDiameter(),
531
  DownTubeImproperlyJoinsHeadTube(),
532
  TopTubeImproperlyJoinsHeadTube(),
533
  TopTubeImproperlyJoinsSeatTube(),
534
- DownTubeIntersectsFrontWheel(),
535
  SaddleHitsTopTube(),
536
  SaddleHitsHeadTube(),
537
  ]
@@ -553,19 +553,19 @@ difficult_validation_functions: List[ValidationFunction] = [
553
  # ChainStaySmallerThanRearWheelRadius(),
554
  # ChainStayShorterThanBBDrop(),
555
  # SeatStaySmallerThanRearWheelRadius(),
556
- SeatTubeIntersectsRearWheel(),
557
  # DownTubeCantReachHeadTube(),
558
  # RearWheelCutoutSeversSeatTube(),
559
- FootIntersectsFrontWheel(),
560
  # CrankHitsGroundInLowestPosition(),
561
- # RGBvalueGreaterThan255(),
562
  # ChainStaysIntersect(),
563
  # TubeWallThicknessExceedsRadius(),
564
- SeatTubeInnerDiameterThinnerThanSeatPostOuterDiameter(),
565
  DownTubeImproperlyJoinsHeadTube(),
566
  # TopTubeImproperlyJoinsHeadTube(),
567
  TopTubeImproperlyJoinsSeatTube(),
568
- # DownTubeIntersectsFrontWheel(),
569
  # SaddleHitsTopTube(),
570
  # SaddleHitsHeadTube(),
571
  ]
 
25
  'FIRST color G_RGB', 'FIRST color B_RGB']
26
 
27
  class SaddleHeightTooSmall(ValidationFunction):
28
+ def friendly_name(self) -> str: return "Saddle Height Too Small"
29
  def variable_names(self) -> List[str]: return ["Saddle height"]
30
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
31
  return 100.0 - ctx.col("Saddle height")
32
 
33
 
34
  class SaddleCollidesWithSeatTube(ValidationFunction):
35
+ def friendly_name(self) -> str: return "Saddle Collides With Seat Tube"
36
  def variable_names(self) -> List[str]: return ["Saddle height", "Seat tube length"]
37
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
38
  return ctx.col("Seat tube length") + 40.0 - ctx.col("Saddle height")
39
 
40
 
41
  class SaddleTooShort(ValidationFunction):
42
+ def friendly_name(self) -> str: return "Saddle Too Short"
43
  def variable_names(self) -> List[str]: return ["Saddle length"]
44
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
45
  return 228.0 - ctx.col("Saddle length")
46
 
47
 
48
  class HeadAngleOverLimit(ValidationFunction):
49
+ def friendly_name(self) -> str: return "Head Angle Over Limit"
50
  def variable_names(self) -> List[str]: return ["Head angle"]
51
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
52
  return ctx.col("Head angle") - 180.0
53
 
54
 
55
  class SeatAngleOverLimit(ValidationFunction):
56
+ def friendly_name(self) -> str: return "Seat Angle Over Limit"
57
  def variable_names(self) -> List[str]: return ["Seat angle"]
58
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
59
  return ctx.col("Seat angle") - 180.0
60
 
61
 
62
  class SeatPostTooShort(ValidationFunction):
63
+ def friendly_name(self) -> str: return "Seat Post Too Short"
64
  def variable_names(self) -> List[str]:
65
  return ["Seat tube length", "Seatpost LENGTH", "Saddle height", "Seat angle"]
66
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
 
73
 
74
 
75
  class SeatPostTooLong(ValidationFunction):
76
+ def friendly_name(self) -> str: return "Seat Post Too Long"
77
  def variable_names(self) -> List[str]:
78
  return ["Seatpost LENGTH", "Saddle height", "Seat angle"]
79
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
 
83
 
84
 
85
  class RearWheelInnerDiameterTooSmall(ValidationFunction):
86
+ def friendly_name(self) -> str: return "Rear Wheel Inner Diameter Too Small"
87
  def variable_names(self) -> List[str]: return ["Wheel diameter rear", "RDBSD"]
88
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
89
  inner_d = ctx.col("Wheel diameter rear") - 2.0 * ctx.col("RDBSD")
 
91
 
92
 
93
  class FrontWheelInnerDiameterTooSmall(ValidationFunction):
94
+ def friendly_name(self) -> str: return "Front Wheel Inner Diameter Too Small"
95
  def variable_names(self) -> List[str]: return ["Wheel diameter front", "FDBSD"]
96
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
97
  inner_d = ctx.col("Wheel diameter front") - 2.0 * ctx.col("FDBSD")
 
99
 
100
 
101
  class SeatTubeExtensionLongerThanSeatTube(ValidationFunction):
102
+ def friendly_name(self) -> str: return "Seat Tube Extension Longer Than Seat Tube"
103
  def variable_names(self) -> List[str]: return ["Seat tube length", "Seat tube extension2"]
104
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
105
  return ctx.col("Seat tube extension2") - ctx.col("Seat tube length")
106
 
107
 
108
  class HeadTubeUpperExtensionAndLowerExtensionOverlap(ValidationFunction):
109
+ def friendly_name(self) -> str: return "Head Tube Upper Extension And Lower Extension Overlap"
110
  def variable_names(self) -> List[str]:
111
  return ["Head tube length textfield", "Head tube upper extension2", "Head tube lower extension2"]
112
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
 
114
 
115
 
116
  class SeatStayJunctionLongerThanSeatTube(ValidationFunction):
117
+ def friendly_name(self) -> str: return "Seat Stay Junction Longer Than Seat Tube"
118
  def variable_names(self) -> List[str]: return ["Seat tube length", "Seat stay junction0"]
119
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
120
  return ctx.col("Seat stay junction0") - ctx.col("Seat tube length")
121
 
122
 
123
  class NonNegativeParameterIsNegative(ValidationFunction):
124
+ def friendly_name(self) -> str: return "Non-negative Parameter Is Negative"
125
  def variable_names(self) -> List[str]: return POSITIVE_COLS
126
  def validate(self, ctx: 'FeatureStore') -> torch.Tensor:
127
  X = torch.stack([ctx.col(c) for c in POSITIVE_COLS], dim=1) # (n, k)
 
135
 
136
 
137
  class ChainStaySmallerThanRearWheelRadius(ValidationFunction):
138
+ def friendly_name(self) -> str: return "Chain Stay Smaller Than Rear Wheel Radius"
139
  def variable_names(self) -> List[str]: return ["CS textfield", "Wheel diameter rear"]
140
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
141
  return (ctx.col("Wheel diameter rear") * 0.5) - ctx.col("CS textfield")
142
 
143
 
144
  class ChainStayShorterThanBBDrop(ValidationFunction):
145
+ def friendly_name(self) -> str: return "Chain Stay Shorter Than BB Drop"
146
  def variable_names(self) -> List[str]: return ["CS textfield", "BB textfield"]
147
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
148
  return ctx.col("BB textfield") - ctx.col("CS textfield")
149
 
150
 
151
  class SeatStaySmallerThanRearWheelRadius(ValidationFunction):
152
+ def friendly_name(self) -> str: return "Seat Stay Smaller Than Rear Wheel Radius"
153
  def variable_names(self) -> List[str]:
154
  return ["CS textfield", "BB textfield","Seat tube length", "Seat stay junction0", "Seat angle", "Wheel diameter rear"]
155
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
 
166
  return (ctx.col("Wheel diameter rear") * 0.5) - g
167
 
168
 
169
+ class SeatTubeCollidesWithRearWheel(ValidationFunction):
170
  def friendly_name(self) -> str:
171
+ return "Seat Tube Collides With Rear Wheel"
172
 
173
  def variable_names(self) -> List[str]:
174
  return [
 
206
 
207
 
208
  class DownTubeCantReachHeadTube(ValidationFunction):
209
+ def friendly_name(self) -> str: return "Down Tube Can't Reach Head Tube"
210
  def variable_names(self) -> List[str]:
211
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head angle", "DT Length"]
212
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
 
215
 
216
  class RearWheelCutoutSeversSeatTube(ValidationFunction):
217
  def friendly_name(self) -> str:
218
+ return "Rear Wheel Cutout Severs Seat Tube"
219
 
220
  def variable_names(self) -> List[str]:
221
  return [
 
249
  q = torch.where(aero_mask, q_true, q_false)
250
  return (wheel_cut * 0.5) - q
251
 
252
+ class FootCollidesWithFrontWheel(ValidationFunction):
253
+ def friendly_name(self) -> str: return "Foot Collides With Front Wheel"
254
  def variable_names(self) -> List[str]:
255
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head angle",
256
  "BB textfield", "DT Length", "FORK0R", "Wheel diameter front", "Wheel diameter rear"]
 
264
 
265
 
266
  class CrankHitsGroundInLowestPosition(ValidationFunction):
267
+ def friendly_name(self) -> str: return "Crank Hits Ground In Lowest Position"
268
  def variable_names(self) -> List[str]: return ["BB textfield", "Wheel diameter rear"]
269
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
270
  return (187.5 + ctx.col("BB textfield")) - (ctx.col("Wheel diameter rear") * 0.5)
271
 
272
 
273
+ class RGBValueGreaterThan255(ValidationFunction):
274
+ def friendly_name(self) -> str: return "RGB Value Greater Than 255"
275
  def variable_names(self) -> List[str]:
276
  return ["FIRST color R_RGB", "FIRST color G_RGB", "FIRST color B_RGB"]
277
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
 
285
 
286
 
287
  class ChainStaysIntersect(ValidationFunction):
288
+ def friendly_name(self) -> str: return "Chain Stays Intersect"
289
  def variable_names(self) -> List[str]: return ["csd", "Chain stay position on BB","BB length"]
290
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
291
  return (ctx.col("csd")*0.5 + ctx.col("Chain stay position on BB")) - (ctx.col("BB length")*0.5)
292
 
293
 
294
  class TubeWallThicknessExceedsRadius(ValidationFunction):
295
+ def friendly_name(self) -> str: return "Tube Wall Thickness Exceeds Radius"
296
  def variable_names(self) -> List[str]:
297
  return [
298
  "ttd", "Wall thickness Top tube",
 
312
  return torch.sum(torch.clamp_min(violation, 0.0), dim=1)
313
 
314
 
315
+ class SeatTubeTooNarrowForSeatPost(ValidationFunction):
316
+ def friendly_name(self) -> str: return "Seat Tube Too Narrow For Seat Post"
317
  def variable_names(self) -> List[str]: return ["std", "Wall thickness Seat tube"]
318
  def validate(self, ctx: FeatureStore) -> torch.Tensor:
319
  inner_d = ctx.col("std") - ctx.col("Wall thickness Seat tube")
 
321
 
322
 
323
  class DownTubeImproperlyJoinsHeadTube(ValidationFunction):
324
+ def friendly_name(self) -> str: return "Down Tube Improperly Joins Head Tube"
325
  def variable_names(self) -> List[str]:
326
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head angle",
327
  "DT Length", "dtd", "htd"]
 
347
 
348
 
349
  class TopTubeImproperlyJoinsHeadTube(ValidationFunction):
350
+ def friendly_name(self) -> str: return "Top Tube Improperly Joins Head Tube"
351
  def variable_names(self) -> List[str]:
352
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head tube upper extension2",
353
  "Seat tube extension2", "Head angle", "Seat angle", "DT Length", "ttd", "htd","Seat tube length"]
 
376
 
377
 
378
  class TopTubeImproperlyJoinsSeatTube(ValidationFunction):
379
+ def friendly_name(self) -> str: return "Top Tube Improperly Joins Seat Tube"
380
  def variable_names(self) -> List[str]:
381
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head tube upper extension2",
382
  "Seat tube extension2", "Head angle", "Seat angle", "DT Length", "ttd", "std","Seat tube length"]
 
405
  return L1 + L2 - stux + below_0_pen + above_pi_pen + seatpost_clamp_default_offset
406
 
407
 
408
+ class DownTubeCollidesWithFrontWheel(ValidationFunction):
409
+ def friendly_name(self) -> str: return "Down Tube Collides With Front Wheel"
410
  def variable_names(self) -> List[str]:
411
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head angle",
412
  "DT Length", "BB textfield", "FORK0R", "Wheel diameter front", "Wheel diameter rear", "dtd"]
 
427
 
428
 
429
  class SaddleHitsTopTube(ValidationFunction):
430
+ def friendly_name(self) -> str: return "Saddle Hits Top Tube"
431
  def variable_names(self) -> List[str]:
432
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head tube upper extension2",
433
  "Seat tube extension2", "Head angle", "Seat angle", "DT Length", "ttd", "std",
 
465
 
466
 
467
  class SaddleHitsHeadTube(ValidationFunction):
468
+ def friendly_name(self) -> str: return "Saddle Hits Head Tube"
469
  def variable_names(self) -> List[str]:
470
  return ["Stack", "Head tube length textfield", "Head tube lower extension2", "Head tube upper extension2",
471
  "Seat tube extension2", "Head angle", "Seat angle", "DT Length", "ttd", "htd",
 
519
  ChainStaySmallerThanRearWheelRadius(),
520
  ChainStayShorterThanBBDrop(),
521
  SeatStaySmallerThanRearWheelRadius(),
522
+ SeatTubeCollidesWithRearWheel(),
523
  DownTubeCantReachHeadTube(),
524
  RearWheelCutoutSeversSeatTube(),
525
+ FootCollidesWithFrontWheel(),
526
  CrankHitsGroundInLowestPosition(),
527
+ RGBValueGreaterThan255(),
528
  ChainStaysIntersect(),
529
  TubeWallThicknessExceedsRadius(),
530
+ SeatTubeTooNarrowForSeatPost(),
531
  DownTubeImproperlyJoinsHeadTube(),
532
  TopTubeImproperlyJoinsHeadTube(),
533
  TopTubeImproperlyJoinsSeatTube(),
534
+ DownTubeCollidesWithFrontWheel(),
535
  SaddleHitsTopTube(),
536
  SaddleHitsHeadTube(),
537
  ]
 
553
  # ChainStaySmallerThanRearWheelRadius(),
554
  # ChainStayShorterThanBBDrop(),
555
  # SeatStaySmallerThanRearWheelRadius(),
556
+ SeatTubeCollidesWithRearWheel(),
557
  # DownTubeCantReachHeadTube(),
558
  # RearWheelCutoutSeversSeatTube(),
559
+ FootCollidesWithFrontWheel(),
560
  # CrankHitsGroundInLowestPosition(),
561
+ # RGBValueGreaterThan255(),
562
  # ChainStaysIntersect(),
563
  # TubeWallThicknessExceedsRadius(),
564
+ SeatTubeTooNarrowForSeatPost(),
565
  DownTubeImproperlyJoinsHeadTube(),
566
  # TopTubeImproperlyJoinsHeadTube(),
567
  TopTubeImproperlyJoinsSeatTube(),
568
+ # DownTubeCollidesWithFrontWheel(),
569
  # SaddleHitsTopTube(),
570
  # SaddleHitsHeadTube(),
571
  ]
bike_bench_internal/src/bikebench/xml_handling/bcad_to_bikebench.py CHANGED
@@ -324,12 +324,9 @@ TYPE_SPEC = {
324
  "FDBSD": "float",
325
  "Fork type": "int",
326
  "Stem kind": "int",
327
- "Display AEROBARS": "bool",
328
  "Handlebar style": "cat",
329
  "BB length": "float",
330
  "Wheel cut": "float",
331
- "Front Fender include": "bool",
332
- "Rear Fender include": "bool",
333
  "BELTorCHAIN": "bool",
334
  "Number of cogs": "int",
335
  "Number of chainrings": "int",
@@ -560,6 +557,11 @@ def build_bikebench_dataframe(
560
  result = converted[ordered_cols].copy()
561
  result.index.name = None
562
 
 
 
 
 
 
563
  # Enforce schema & impute
564
  result = apply_type_schema(result)
565
 
@@ -595,6 +597,29 @@ def build_bikebench_dataframe(
595
 
596
  return result
597
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598
  THICKNESS_COLS = [
599
  "Wall thickness Bottom Bracket",
600
  "Wall thickness Top tube",
 
324
  "FDBSD": "float",
325
  "Fork type": "int",
326
  "Stem kind": "int",
 
327
  "Handlebar style": "cat",
328
  "BB length": "float",
329
  "Wheel cut": "float",
 
 
330
  "BELTorCHAIN": "bool",
331
  "Number of cogs": "int",
332
  "Number of chainrings": "int",
 
557
  result = converted[ordered_cols].copy()
558
  result.index.name = None
559
 
560
+ result["MATERIAL"] = result["MATERIAL"].map(_material_to_3class)
561
+
562
+ # Enforce schema & impute
563
+ result = apply_type_schema(result)
564
+
565
  # Enforce schema & impute
566
  result = apply_type_schema(result)
567
 
 
597
 
598
  return result
599
 
600
+ def _material_to_3class(val: object) -> str:
601
+ """Map raw MATERIAL values to three classes: ALUMINIUM, STEEL, BAMBOO."""
602
+ if val is None or (isinstance(val, float) and pd.isna(val)):
603
+ return "ALUMINIUM" # treat missing like OTHER -> ALUMINIUM
604
+
605
+ s = str(val).strip().upper()
606
+ # normalize a bit
607
+ s_norm = re.sub(r"[^A-Z]", "", s) # remove spaces, dashes, etc.
608
+
609
+ # direct keep
610
+ if s_norm == "TITANIUM":
611
+ return "TITANIUM"
612
+ if s_norm == "STEEL":
613
+ return "STEEL"
614
+
615
+ # map CARBON (and a couple common aliases) to STEEL
616
+ if s_norm == "CARBON":
617
+ return "STEEL"
618
+
619
+ # everything else (OTHER, ALUMINIUM, ALLOY, unknowns...) → ALUMINIUM
620
+ return "ALUMINIUM"
621
+
622
+
623
  THICKNESS_COLS = [
624
  "Wall thickness Bottom Bracket",
625
  "Wall thickness Top tube",
bike_bench_internal/src/resources/misc/ref_point.csv CHANGED
@@ -1,50 +1,50 @@
1
- Usability Score,0.96583045
2
- Drag Force (N),26.949783
3
- Knee Angle Error (deg.),121.69888
4
- Hip Angle Error (deg.),45.15921
5
- Arm Angle Error (deg.),51.60288
6
- Arm Too Long for Bike,-0.2584031
7
- Saddle Too Far From Handle,-0.0924381
8
- Torso Too Long for Bike,-0.40180784
9
- Saddle Too Far From Crank,0.18204135
10
- Upper Leg Too Long for Bike,-0.6047877
11
- Lower Leg Too Long for Bike,-0.18946144
12
- Cosine Distance to Embedding,0.3990872
13
- Mass (kg),13.66151
14
- Planar Compliance Score,9.490826
15
- Transverse Compliance Score,8.403987
16
- Eccentric Compliance Score,8.238781
17
- Planar Safety Factor,1.0
18
- Eccentric Safety Factor,1.0
19
- Predicted Frame Validity,0.5
20
- Saddle height too small,-130.0
21
- Saddle collides with seat tube,56.299988
22
- Saddle too short,98.0
23
- Head angle over limit,-97.0
24
- Seat angle over limit,-90.0
25
- Seat post too short,227.887
26
- Seat post too long,43.014893
27
- Rear Wheel inner diameter too small,52.0
28
- Front Wheel inner diameter too small,52.0
29
- Seat tube extension longer than seat tube,-80.0
30
- Head tube upper extension and lower extension overlap,67.59999
31
- Seat stay junction longer than seat tube,-82.0
32
- Non-negative parameter is negative,210.0
33
- Chain stay smaller than rear wheel radius,37.0
34
- Chain stay shorter than BB drop,-230.0
35
- Seat stay smaller than rear wheel radius,-3.105194
36
- Seat Tube Intersects Rear Wheel,89.69733
37
- Down tube can't reach head tube,-81.2265
38
- Rear wheel cutout severs seat tube,-12.901978
39
- Foot intersects front wheel,36118.047
40
- Crank hits ground in lowest position,32.5
41
- RGB value greater than 255,0.0
42
- Chain stays intersect,22.625
43
- Tube wall thickness exceeds radius,3.2742386
44
- Seat tube inner diameter thinner than seat post outer diameter,29.329865
45
- Down tube improperly joins head tube,55.09871
46
- Top tube improperly joins head tube,27.778019
47
- Top tube improperly joins seat tube,25.929976
48
- Down tube intersects front wheel,30.511627
49
- Saddle hits top tube,0.3413951
50
- Saddle hits head tube,-0.0586164
 
1
+ Usability Score,1.0059241
2
+ Drag Force (N),27.861992
3
+ Knee Angle Error (deg.),127.78383
4
+ Hip Angle Error (deg.),47.41717
5
+ Arm Angle Error (deg.),54.18302
6
+ Arm Too Long for Bike,-0.21971205
7
+ Saddle Too Far From Handle,-0.056464136
8
+ Torso Too Long for Bike,-0.366885
9
+ Saddle Too Far From Crank,0.22086428
10
+ Upper Leg Too Long for Bike,-0.5684242
11
+ Lower Leg Too Long for Bike,-0.15311062
12
+ Cosine Distance To Embedding,0.39972186
13
+ Mass (kg),14.34326
14
+ Planar Compliance Score,9.962891
15
+ Transverse Compliance Score,8.808648
16
+ Eccentric Compliance Score,8.635697
17
+ Planar Safety Factor,1.2305812
18
+ Eccentric Safety Factor,1.1785439
19
+ Predicted Frame Validity,0.55
20
+ Saddle Height Too Small,-97.435
21
+ Saddle Collides With Seat Tube,83.23999
22
+ Saddle Too Short,115.5
23
+ Head Angle Over Limit,-95.1
24
+ Seat Angle Over Limit,-87.7
25
+ Seat Post Too Short,257.977
26
+ Seat Post Too Long,79.27089
27
+ Rear Wheel Inner Diameter Too Small,92.2
28
+ Front Wheel Inner Diameter Too Small,92.2
29
+ Seat Tube Extension Longer Than Seat Tube,-51.0
30
+ Head Tube Upper Extension And Lower Extension Overlap,85.07499
31
+ Seat Stay Junction Longer Than Seat Tube,-53.565
32
+ Non-negative Parameter Is Negative,-4571.084
33
+ Chain Stay Smaller Than Rear Wheel Radius,71.325
34
+ Chain Stay Shorter Than BB Drop,-196.775
35
+ Seat Stay Smaller Than Rear Wheel Radius,26.516027
36
+ Seat Tube Collides With Rear Wheel,123.08644
37
+ Down Tube Can't Reach Head Tube,-65.34191
38
+ Rear Wheel Cutout Severs Seat Tube,49999972.0
39
+ Foot Collides With Front Wheel,60363.273
40
+ Crank Hits Ground In Lowest Position,44.55
41
+ RGB Value Greater Than 255,38.25
42
+ Chain Stays Intersect,40.745003
43
+ Tube Wall Thickness Exceeds Radius,3.4379506
44
+ Seat Tube Too Narrow For Seat Post,32.42751
45
+ Down Tube Improperly Joins Head Tube,62.309597
46
+ Top Tube Improperly Joins Head Tube,35.511604
47
+ Top Tube Improperly Joins Seat Tube,42.88616
48
+ Down Tube Collides With Front Wheel,47.536987
49
+ Saddle Hits Top Tube,0.4291829
50
+ Saddle Hits Head Tube,0.037365556
bike_bench_internal/src/resources/models_and_scalers/aesthetics_model_weights.pt CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:b7da88a1b263a2e75e1b8d221e63c8e0bb05e4f4f7ac20bd5cd07970f434d16a
3
- size 2212539
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:79d5b3766259ab7b134eb94f1ba5632f1269c2ac23ca9b34ebe7ec387516df12
3
+ size 2205371
bike_bench_internal/src/resources/models_and_scalers/aesthetics_scaler.pt CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:26d2cdf2cc89b2ceb1b8a7015bbf53cc74e1448b03f0e616fbf37431f3338019
3
- size 3189
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fb3a5edc888be3f7ec468c1a852c2fce98b60d8ac90cb5f7c71ad308c342010a
3
+ size 3061