solverforge_solver/builder/context/
model_resolution.rs1use std::collections::{BTreeMap, HashSet};
4
5use solverforge_core::domain::{DynamicScalarVariableSlot, SolutionDescriptor};
6
7use crate::descriptor::collect_bindings;
8
9use super::{RuntimeModel, RuntimeScalarSlot, VariableSlot};
10
11impl<S, V, DM, IDM> RuntimeModel<S, V, DM, IDM> {
12 pub fn resolve_dynamic_descriptor_indexes(
13 mut self,
14 descriptor: &SolutionDescriptor,
15 ) -> Result<Self, String>
16 where
17 S: 'static,
18 {
19 for variable in &mut self.variables {
20 match variable {
21 VariableSlot::DynamicScalar(slot) => slot.resolve_descriptor_index(descriptor)?,
22 VariableSlot::DynamicList(slot) => slot.resolve_descriptor_index(descriptor)?,
23 VariableSlot::Scalar(_) | VariableSlot::List(_) => {}
24 }
25 }
26 let dynamic_scalar_slots = self.dynamic_scalar_variables().cloned().collect::<Vec<_>>();
27 self.canonicalize_dynamic_scalar_groups(&dynamic_scalar_slots)?;
28 let provider_scalar_slots = self
29 .variables
30 .iter()
31 .filter_map(|variable| match variable {
32 VariableSlot::Scalar(slot) => Some(RuntimeScalarSlot::Static(*slot)),
33 VariableSlot::DynamicScalar(slot) => Some(RuntimeScalarSlot::Dynamic(slot.clone())),
34 VariableSlot::List(_) | VariableSlot::DynamicList(_) => None,
35 })
36 .collect::<Vec<_>>();
37 self.runtime_provider_registry.freeze(
38 &provider_scalar_slots,
39 &self.scalar_groups,
40 &self.conflict_repairs,
41 )?;
42 self.bind_scalar_group_construction_slots(descriptor)?;
43 Ok(self)
44 }
45
46 pub fn assert_dynamic_descriptor_indexes_resolved(&self) {
47 for variable in &self.variables {
48 match variable {
49 VariableSlot::DynamicScalar(slot) => {
50 let _ = slot.descriptor_index();
51 let _ = slot.descriptor_variable_index();
52 }
53 VariableSlot::DynamicList(slot) => {
54 let _ = slot.descriptor_index();
55 let _ = slot.descriptor_variable_index();
56 }
57 VariableSlot::Scalar(_) | VariableSlot::List(_) => {}
58 }
59 }
60 for group in &self.scalar_groups {
61 for member in &group.members {
62 if member.is_dynamic() {
63 let _ = member.dynamic_identity();
64 assert_ne!(
65 member.descriptor_index,
66 usize::MAX,
67 "dynamic scalar group `{}` has an unresolved target descriptor",
68 group.group_name
69 );
70 assert_ne!(
71 member.variable_index,
72 usize::MAX,
73 "dynamic scalar group `{}` has an unresolved target variable",
74 group.group_name
75 );
76 }
77 }
78 }
79 }
80 fn canonicalize_dynamic_scalar_groups(
81 &mut self,
82 scalar_slots: &[DynamicScalarVariableSlot<S>],
83 ) -> Result<(), String> {
84 let mut group_names = HashSet::new();
85 let mut assignment_targets = HashSet::new();
86 for group in &mut self.scalar_groups {
87 if !group_names.insert(group.group_name) {
88 return Err(format!(
89 "scalar group `{}` is registered more than once",
90 group.group_name
91 ));
92 }
93 group.canonicalize_dynamic_members(scalar_slots)?;
94 if let Some(assignment) = group.assignment() {
95 let target = assignment.target();
96 if !assignment_targets.insert((target.descriptor_index, target.variable_index)) {
97 return Err(format!(
98 "assignment scalar target {}.{} is owned by more than one scalar group",
99 target.entity_type_name, target.variable_name
100 ));
101 }
102 }
103 }
104 Ok(())
105 }
106
107 fn bind_scalar_group_construction_slots(
108 &mut self,
109 descriptor: &SolutionDescriptor,
110 ) -> Result<(), String> {
111 let typed_binding_indexes = collect_bindings(descriptor)
112 .into_iter()
113 .map(|binding| {
114 (
115 (binding.descriptor_index, binding.variable_index),
116 binding.binding_index,
117 )
118 })
119 .collect::<BTreeMap<_, _>>();
120 let mut next_dynamic_binding_index = self.variables.len().max(
121 typed_binding_indexes
122 .values()
123 .copied()
124 .max()
125 .map_or(0, |index| index.saturating_add(1)),
126 );
127 let mut dynamic_binding_indexes = BTreeMap::new();
128
129 for group in &mut self.scalar_groups {
130 for member in &mut group.members {
131 let binding_index = if let Some(identity) = member.dynamic_identity() {
132 *dynamic_binding_indexes.entry(identity).or_insert_with(|| {
133 let binding_index = next_dynamic_binding_index;
134 next_dynamic_binding_index = next_dynamic_binding_index
135 .checked_add(1)
136 .expect("dynamic scalar group construction slot index overflow");
137 binding_index
138 })
139 } else {
140 *typed_binding_indexes
141 .get(&(member.descriptor_index, member.variable_index))
142 .ok_or_else(|| {
143 format!(
144 "scalar group `{}` target {}.{} has no descriptor construction binding",
145 group.group_name, member.entity_type_name, member.variable_name
146 )
147 })?
148 };
149 member.set_construction_binding_index(binding_index);
150 }
151
152 let assignment_target = group.assignment().map(|assignment| {
153 (
154 assignment.target().descriptor_index,
155 assignment.target().variable_index,
156 )
157 });
158 let Some((descriptor_index, variable_index)) = assignment_target else {
159 continue;
160 };
161 let binding_index = group
162 .members
163 .iter()
164 .find(|member| {
165 member.descriptor_index == descriptor_index
166 && member.variable_index == variable_index
167 })
168 .map(|member| member.construction_binding_index())
169 .ok_or_else(|| {
170 format!(
171 "assignment scalar group `{}` target has no registered construction slot",
172 group.group_name
173 )
174 })?;
175 group
176 .assignment_mut()
177 .expect("assignment target must retain its scalar group binding")
178 .target_mut()
179 .set_construction_binding_index(binding_index);
180 }
181
182 Ok(())
183 }
184}