Skip to content

Commit e3ef041

Browse files
committed
wip
1 parent 100ae62 commit e3ef041

File tree

3 files changed

+303
-25
lines changed

3 files changed

+303
-25
lines changed

crates/uv-resolver/src/graph_ops.rs

Lines changed: 201 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ pub(crate) fn marker_reachability<T>(
9191
pub(crate) fn simplify_conflict_markers(graph: &mut Graph<ResolutionGraphNode, UniversalMarker>) {
9292
// The set of activated extras (and TODO, in the future, groups)
9393
// for each node. The ROOT nodes don't have any extras activated.
94-
let mut activated: FxHashMap<NodeIndex, FxHashSet<(PackageName, ExtraName)>> =
95-
FxHashMap::with_capacity_and_hasher(graph.node_count(), FxBuildHasher);
94+
let mut activated: FxHashMap<NodeIndex, Vec<FxHashSet<(PackageName, ExtraName)>>> =
95+
FxHashMap::default();
9696

9797
// Collect the root nodes.
9898
//
@@ -108,35 +108,217 @@ pub(crate) fn simplify_conflict_markers(graph: &mut Graph<ResolutionGraphNode, U
108108
})
109109
.collect();
110110

111-
let mut assume_by_edge: FxHashMap<EdgeIndex, FxHashSet<(PackageName, ExtraName)>> =
112-
FxHashMap::default();
111+
// let mut assume_by_edge: FxHashMap<EdgeIndex, FxHashSet<(PackageName, ExtraName)>> =
112+
// FxHashMap::default();
113113
let mut seen: FxHashSet<NodeIndex> = FxHashSet::default();
114114
while let Some(parent_index) = queue.pop() {
115-
for child_edge in graph.edges_directed(parent_index, Direction::Outgoing) {
116-
// TODO: The below seems excessively clone-y.
117-
// Consider tightening this up a bit.
118-
let target = child_edge.target();
119-
let mut extras: FxHashSet<(PackageName, ExtraName)> =
120-
activated.get(&parent_index).cloned().unwrap_or_default();
121-
if let Some((package, extra)) = graph[parent_index].package_extra_names() {
122-
extras.insert((package.clone(), extra.clone()));
115+
if let Some((package, extra)) = graph[parent_index].package_extra_names() {
116+
for set in activated
117+
.entry(parent_index)
118+
.or_insert_with(|| vec![FxHashSet::default()])
119+
{
120+
set.insert((package.clone(), extra.clone()));
123121
}
124-
if let Some((package, extra)) = graph[target].package_extra_names() {
125-
extras.insert((package.clone(), extra.clone()));
122+
}
123+
let sets = activated.get(&parent_index).cloned().unwrap_or_default();
124+
for child_edge in graph.edges_directed(parent_index, Direction::Outgoing) {
125+
for set in sets.clone() {
126+
activated.entry(child_edge.target()).or_default().push(set);
126127
}
127-
activated.entry(target).or_default().extend(extras.clone());
128-
assume_by_edge
129-
.entry(child_edge.id())
130-
.or_default()
131-
.extend(extras);
128+
// activated
129+
// .entry(child_edge.target())
130+
// .or_default()
131+
// .extend(sets.clone());
132132
if seen.insert(child_edge.target()) {
133133
queue.push(child_edge.target());
134134
}
135135
}
136136
}
137+
/*
138+
eprintln!("EXTRA ASSUMPTIONS");
139+
for (&node_id, extras) in &activated {
140+
let node = &graph[node_id];
141+
eprintln!(
142+
" {} -> assuming ({})",
143+
node.debug(),
144+
extras
145+
.iter()
146+
.map(|set| {
147+
format!(
148+
"{{{}}}",
149+
set.iter()
150+
.map(|&(ref p, ref e)| format!("{p}[{e}]"))
151+
.collect::<Vec<String>>()
152+
.join(", "),
153+
)
154+
})
155+
.collect::<Vec<String>>()
156+
.join(", ")
157+
);
158+
}
159+
*/
160+
for edge_index in (0..graph.edge_count()).map(EdgeIndex::new) {
161+
let (from_index, _) = graph.edge_endpoints(edge_index).unwrap();
162+
let Some(activated) = activated.get(&from_index) else {
163+
continue;
164+
};
165+
let all_paths_satisfied = activated.iter().all(|set| {
166+
let extras = set
167+
.iter()
168+
.cloned()
169+
.collect::<Vec<(PackageName, ExtraName)>>();
170+
graph[edge_index].conflict().evaluate(&extras)
171+
});
172+
if all_paths_satisfied {
173+
for set in activated {
174+
for (package, extra) in set {
175+
graph[edge_index].assume_extra(package, extra);
176+
}
177+
}
178+
}
179+
/*
180+
let activated = activated
181+
.iter()
182+
.cloned()
183+
.collect::<Vec<(PackageName, ExtraName)>>();
184+
if activated.iter().all(|&(ref package, ref extra)| {
185+
graph[edge_index]
186+
.conflict()
187+
.evaluate(&[(package.clone(), extra.clone())])
188+
}) {
189+
for &(ref package, ref extra) in &activated {
190+
graph[edge_index].assume_extra(package, extra);
191+
}
192+
}
193+
*/
194+
}
195+
}
196+
197+
/*
198+
/// Traverse the given dependency graph and propagate activated markers.
199+
///
200+
/// For example, given an edge like `foo[x1] -> bar`, then it is known that
201+
/// `x1` is activated. This in turn can be used to simplify any downstream
202+
/// conflict markers with `extra == "x1"` in them.
203+
pub(crate) fn simplify_conflict_markers(graph: &mut Graph<ResolutionGraphNode, UniversalMarker>) {
204+
// Collect the root nodes.
205+
//
206+
// Besides the actual virtual root node, virtual dev dependencies packages are also root
207+
// nodes since the edges don't cover dev dependencies.
208+
let mut queue: Vec<EdgeIndex> = graph
209+
.node_indices()
210+
.filter(|node_index| {
211+
graph
212+
.edges_directed(*node_index, Direction::Incoming)
213+
.next()
214+
.is_none()
215+
})
216+
.flat_map(|node_index| graph.edges_directed(node_index, Direction::Outgoing))
217+
.map(|edge| edge.id())
218+
.collect();
219+
// let mut seen: FxHashSet<EdgeIndex> = FxHashSet::default();
220+
let mut assume_by_edge: FxHashMap<EdgeIndex, FxHashSet<(PackageName, ExtraName)>> =
221+
FxHashMap::default();
222+
while let Some(edge_id) = queue.pop() {
223+
// OK because we know `edge_id` is valid.
224+
let (_from_id, to_id) = graph.edge_endpoints(edge_id).unwrap();
225+
let parent_assumptions = assume_by_edge.get(&edge_id).cloned().unwrap_or_default();
226+
for child_edge in graph.edges_directed(to_id, Direction::Outgoing) {
227+
let mut assumptions = assume_by_edge
228+
.get(&child_edge.id())
229+
.cloned()
230+
.unwrap_or_default();
231+
assumptions.extend(parent_assumptions.iter().cloned());
232+
if let Some((package, extra)) = graph[to_id].package_extra_names() {
233+
assume_by_edge
234+
.entry(child_edge.id())
235+
.or_default()
236+
.insert((package.clone(), extra.clone()));
237+
assumptions.insert((package.clone(), extra.clone()));
238+
}
239+
if !child_edge
240+
.weight()
241+
.conflict()
242+
.evaluate(&assumptions.iter().cloned().collect::<Vec<_>>())
243+
{
244+
assume_by_edge.insert(child_edge.id(), assumptions);
245+
}
246+
queue.push(child_edge.id());
247+
// if seen.insert(child_item) {
248+
// queue.push(child_item);
249+
// }
250+
}
251+
}
252+
eprintln!("EXTRA ASSUMPTIONS");
137253
for (edge_id, extras) in assume_by_edge {
254+
let (ni1, ni2) = graph.edge_endpoints(edge_id).unwrap();
255+
let (n1, n2) = (&graph[ni1], &graph[ni2]);
256+
eprintln!(
257+
" ({}, {}) -> assuming {{{}}}",
258+
n1.debug(),
259+
n2.debug(),
260+
extras
261+
.iter()
262+
.map(|&(ref p, ref e)| format!("{p}[{e}]"))
263+
.collect::<Vec<String>>()
264+
.join(", ")
265+
);
138266
for &(ref package, ref extra) in &extras {
139267
graph[edge_id].assume_extra(package, extra);
140268
}
141269
}
142270
}
271+
*/
272+
273+
/*
274+
pub(crate) fn simplify_conflict_markers3(graph: &mut Graph<ResolutionGraphNode, UniversalMarker>) {
275+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
276+
struct Item<'a> {
277+
node_index: NodeIndex,
278+
extra: Option<(&'a PackageName, &'a ExtraName)>,
279+
}
280+
281+
// Collect the root nodes.
282+
//
283+
// Besides the actual virtual root node, virtual dev dependencies packages are also root
284+
// nodes since the edges don't cover dev dependencies.
285+
let mut queue: Vec<Item<'_>> = graph
286+
.node_indices()
287+
.filter(|node_index| {
288+
graph
289+
.edges_directed(*node_index, Direction::Incoming)
290+
.next()
291+
.is_none()
292+
})
293+
.map(|node_index| Item {
294+
node_index,
295+
extra: None,
296+
})
297+
.collect();
298+
let mut seen: FxHashSet<Item<'_>> = FxHashSet::default();
299+
let mut assume_by_edge: FxHashMap<EdgeIndex, FxHashSet<(PackageName, ExtraName)>> =
300+
FxHashMap::default();
301+
while let Some(item) = queue.pop() {
302+
let parent = &graph[item.node_index];
303+
for edge in graph.edges_directed(item.node_index, Direction::Outgoing) {
304+
// BREADCRUMBS: OK, so maybe we do need to traverse by edge?
305+
// Or just add it to `Item`? I think the issue here is that
306+
// we need to union the `assume_by_edge` sets as we traverse
307+
// the graph.
308+
if let Some((package, extra)) = parent.package_extra_names() {
309+
assume_by_edge
310+
.entry(edge.id())
311+
.or_default()
312+
.insert((package.clone(), extra.clone()));
313+
}
314+
let child_item = Item {
315+
node_index: edge.target(),
316+
extra: parent.package_extra_names(),
317+
};
318+
if seen.insert(child_item) {
319+
queue.push(child_item);
320+
}
321+
}
322+
}
323+
}
324+
*/

crates/uv-resolver/src/resolution/output.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ use crate::{
3434
VersionsResponse,
3535
};
3636

37+
static DEBUG: bool = false;
38+
3739
/// The output of a successful resolution.
3840
///
3941
/// Includes a complete resolution graph in which every node represents a pinned package and every
@@ -82,6 +84,29 @@ impl ResolutionGraphNode {
8284
}
8385
}
8486
}
87+
88+
#[allow(dead_code)]
89+
pub(crate) fn debug(&self) -> String {
90+
match *self {
91+
ResolutionGraphNode::Root => "ROOT".to_string(),
92+
ResolutionGraphNode::Dist(ref dist) => {
93+
use std::fmt::Write;
94+
let mut buf = String::new();
95+
write!(buf, "{}", dist.name).unwrap();
96+
if let Some(ref extra) = dist.extra {
97+
write!(buf, "[{extra}]").unwrap();
98+
}
99+
if let Some(ref group) = dist.dev {
100+
write!(buf, ":{group}").unwrap();
101+
}
102+
write!(buf, "=={}", dist.version).unwrap();
103+
if !dist.marker.is_true() {
104+
write!(buf, "{{{}}}", dist.marker).unwrap();
105+
}
106+
buf
107+
}
108+
}
109+
}
85110
}
86111

87112
impl Display for ResolutionGraphNode {
@@ -118,6 +143,43 @@ impl ResolverOutput {
118143
resolution_strategy: &ResolutionStrategy,
119144
options: Options,
120145
) -> Result<Self, ResolveError> {
146+
if DEBUG {
147+
for resolution in resolutions {
148+
dbg!(&resolution.env);
149+
eprintln!(" NODES");
150+
for (node, version) in &resolution.nodes {
151+
eprintln!(
152+
" name: {}, version: {}, extra: {:?}, dev: {:?}",
153+
node.name, version, node.extra, node.dev,
154+
);
155+
}
156+
eprintln!(" EDGES");
157+
for edge in &resolution.edges {
158+
let from_name = edge
159+
.from
160+
.as_ref()
161+
.map(|n| n.to_string())
162+
.unwrap_or("ROOT".to_string());
163+
eprintln!(
164+
" {}=={}{} -> {}=={}{}",
165+
from_name,
166+
edge.from_version,
167+
edge.from_extra
168+
.as_ref()
169+
.map(|x| format!("[{}]", x))
170+
.unwrap_or(String::new()),
171+
edge.to,
172+
edge.to_version,
173+
edge.to_extra
174+
.as_ref()
175+
.map(|x| format!("[{}]", x))
176+
.unwrap_or(String::new()),
177+
);
178+
}
179+
}
180+
eprintln!("------------------------------------");
181+
}
182+
121183
let size_guess = resolutions[0].nodes.len();
122184
let mut graph: Graph<ResolutionGraphNode, UniversalMarker, Directed> =
123185
Graph::with_capacity(size_guess, size_guess);
@@ -186,6 +248,19 @@ impl ResolverOutput {
186248
.collect()
187249
};
188250

251+
if DEBUG {
252+
dbg!(&fork_markers);
253+
for ni in graph.node_indices() {
254+
let n1 = &graph[ni];
255+
for edge in graph.edges(ni) {
256+
use petgraph::visit::EdgeRef;
257+
let n2 = &graph[edge.target()];
258+
let weight = edge.weight();
259+
eprintln!(" ({}, {}) -> {weight}", n1.debug(), n2.debug());
260+
}
261+
}
262+
}
263+
189264
// Compute and apply the marker reachability.
190265
let mut reachability = marker_reachability(&graph, &fork_markers);
191266

@@ -200,11 +275,38 @@ impl ResolverOutput {
200275
for weight in graph.edge_weights_mut() {
201276
weight.imbibe(conflicts);
202277
}
278+
279+
if DEBUG {
280+
eprintln!("AFTER REACHABILITY AND IMBIBING");
281+
for ni in graph.node_indices() {
282+
let n1 = &graph[ni];
283+
for edge in graph.edges(ni) {
284+
use petgraph::visit::EdgeRef;
285+
let n2 = &graph[edge.target()];
286+
let weight = edge.weight();
287+
eprintln!(" ({}, {}) -> {weight}", n1.debug(), n2.debug());
288+
}
289+
}
290+
}
291+
203292
simplify_conflict_markers(&mut graph);
204293

205294
// Discard any unreachable nodes.
206295
graph.retain_nodes(|graph, node| !graph[node].marker().is_false());
207296

297+
if DEBUG {
298+
eprintln!("AFTER SIMPLIFICATION");
299+
for ni in graph.node_indices() {
300+
let n1 = &graph[ni];
301+
for edge in graph.edges(ni) {
302+
use petgraph::visit::EdgeRef;
303+
let n2 = &graph[edge.target()];
304+
let weight = edge.weight();
305+
eprintln!(" ({}, {}) -> {weight}", n1.debug(), n2.debug());
306+
}
307+
}
308+
}
309+
208310
if matches!(resolution_strategy, ResolutionStrategy::Lowest) {
209311
report_missing_lower_bounds(&graph, &mut diagnostics);
210312
}

0 commit comments

Comments
 (0)