Worked example: web crawling – data acquisition as graph crawling #
This file is the library's second tutorial, a companion to
DescriptiveComplexity.Examples.ConjunctiveQueries: it walks, step by step,
through the addition of a new problem domain, ending in an NP-completeness
theorem and a machine-checked concrete encoding. The domain is Web data
acquisition: graph crawling, the optimization problem underlying focused
crawling of structured documents, formalized by
Gauquier–Manolescu–Senellart 2026. A website is a
rooted directed graph of pages and hyperlinks; a crawl is an r-rooted
subtree; the decision problem asks for a crawl containing every target page
within a cost budget. The paper proves this NP-complete (its Prop. 4) already
for unit page costs, by reduction from Set Cover; that unit-cost variant is
what this file formalizes, following the paper's proof.
The file follows the user-first arc of the conjunctive-query tutorial: start
from the concrete problem in the user's own formalism (step 1), construct
the encoding with the machinery of DescriptiveComplexity.Encoding – size
bounds discharged at construction (step 3) – prove the encoded problem
equivalent to the concrete one (step 6), and only then establish the
NP-completeness of the encoded variant (steps 7–9), which faithfulness reads
back to the concrete instances. Every step exercises machinery the CQ file
does not, which is why this example is worth reading second:
- the budget is a cardinality threshold – a number in the unary
representation of
DescriptiveComplexity.Numbers.Unary, honest here because crawl costs are bounded by the page count – checked by a guessed injection (DescriptiveComplexity.nonempty_embedding_iff_ncard_le); - the semantics contains a reachability condition, which is not first-order; the membership proof replaces it by a certificate (an order in which every crawled page has a crawled in-neighbour strictly below it), the directed, single-root sibling of the Steiner-tree certificate;
- the hardness reduction is an ordered FO reduction (
≤ᶠᵒ[≤]): the paper's budget|U| + B + 1needs a marked set with exactly one extra element, and a canonical singleton is definable only from an order – the same triage point as for Chromatic Number; - the concrete instance type is single-sorted with a clamped budget, a
second worked instance of
DescriptiveComplexity.Encodingafter the two-sorted CQ one.
Main results:
DescriptiveComplexity.GraphCrawling: the bundled decision problem;DescriptiveComplexity.graphCrawling_sigmaSODefinable: membership in NP;DescriptiveComplexity.setCover_ordered_fo_reduction_graphCrawling : SetCover ≤ᶠᵒ[≤] GraphCrawling: the paper's reduction;DescriptiveComplexity.graphCrawling_NP_complete;DescriptiveComplexity.crawlEncodingwithDescriptiveComplexity.crawlEncoding_faithful: the size-honest concrete encoding and its semantic faithfulness;DescriptiveComplexity.crawlDecodingandDescriptiveComplexity.crawlWF_NP_complete: the computable decoding of well-formed (single-root) websites, and completeness restricted to them.
Step 1: the concrete problem, in the user's own formalism #
A user does not start from finite structures: they start from the data of their own development. Here that is a packaged crawling instance – a page count, a set of links, a root, a set of targets, a budget – with the textbook semantics as a plain Lean predicate. Nothing in this step mentions model theory; it is the problem as the paper states it, and it is what the final completeness theorem will be about, through the faithfulness theorem of step 5.
A packaged concrete crawling instance: n + 1 pages, the hyperlinks, the
root page, the target pages, and the budget (clamped to n + 1 by its type:
a crawl never has more pages than the site).
- n : ℕ
The page count, minus one: pages are
Fin (n + 1), so a website is never empty. The hyperlinks.
The root page, where crawls start.
The target pages.
The budget, clamped by its type: a crawl never has more pages than the site.
Instances For
Dependency graph
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Dependency graph
The textbook size of a packaged instance: pages, links, targets, and the budget in unary. The one audited line of the encoding.
Equations
Instances For
Dependency graph
The textbook semantics of a packaged instance: some set of pages containing the root and every target, each of its pages reachable from the root by links inside it, within budget.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Step 2: the vocabulary of website graphs #
An instance is a single finite structure: the pages, the directed links, a mark for the root, a mark for the targets, and a marked set whose cardinality is the budget – the unary representation of numbers, as used by Set Cover, Steiner Tree and the other threshold problems of the catalog.
Relation symbols of the language of website graphs.
- edge : siteGraphRel 2
edge a b: a hyperlink from pageato pageb. - root : siteGraphRel 1
root a: the pageais the crawl's starting point. - target : siteGraphRel 1
target a: the pageamust be crawled. - marked : siteGraphRel 1
marked a: the pageabelongs to the marked set carrying the budget.
Instances For
Dependency graph
Dependency graph
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The relational language of website graphs: directed links, a root, a set of target pages, and a marked set whose cardinality is the crawling budget.
Equations
- FirstOrder.Language.siteGraph = { Functions := fun (x : ℕ) => Empty, Relations := FirstOrder.Language.siteGraphRel }
Instances For
Dependency graph
Dependency graph
The edge symbol of website graphs.
Instances For
Dependency graph
The root symbol of website graphs.
Instances For
Dependency graph
The target symbol of website graphs.
Instances For
Dependency graph
The mark symbol of website graphs.
Instances For
Dependency graph
Step 3: the encoding, size bounds discharged at construction #
With the vocabulary fixed, the concrete instances are encoded as finite
structures using DescriptiveComplexity.Encoding: a computable encoder
(crawlRelBool, standalone so it can be audited in isolation) and the bundle
whose card_le/le_card fields force the encoding to be size-honest – no
padding, no compression – before anything is proved about its meaning.
Two packaging choices are dictated by the machinery:
- the budget is clamped by the type (
Fin (n + 2)): a crawl never has more thann + 1pages, so any larger budget is equivalent ton + 1– and an unclampedℕbudget, counted in unary in the size, would breakle_cardexactly asList-repetition would for CQ atoms. The WLOG is baked into the packaging; - the budget is counted in unary in
crawlSize. This is the unary representation ofDescriptiveComplexity.Numbers.Unary, and it is honest here because the clamp keeps the budget below the page count – contrast the weighted problems ofDescriptiveComplexity/Encoding/UnaryBlowup.lean, where unary weights are exactly what the size bounds reject.
The encoder, standalone and auditable (cf. cqRelBool): a plain def,
so the compiler vouches that it computes, and the #guards below run it.
Equations
- DescriptiveComplexity.crawlRelBool i FirstOrder.Language.siteGraphRel.edge = fun (x : Fin 2 → Fin (i.n + 1)) => decide ((x 0, x 1) ∈ i.edges)
- DescriptiveComplexity.crawlRelBool i FirstOrder.Language.siteGraphRel.root = fun (x : Fin 1 → Fin (i.n + 1)) => decide (x 0 = i.root)
- DescriptiveComplexity.crawlRelBool i FirstOrder.Language.siteGraphRel.target = fun (x : Fin 1 → Fin (i.n + 1)) => decide (x 0 ∈ i.targets)
- DescriptiveComplexity.crawlRelBool i FirstOrder.Language.siteGraphRel.marked = fun (x : Fin 1 → Fin (i.n + 1)) => decide (↑(x 0) < ↑i.budget)
Instances For
Dependency graph
The encoding of packaged crawling instances by
Language.siteGraph-structures: the pages themselves as universe, the budget
decoded as the marked initial segment DescriptiveComplexity.initSeg. Both size
bounds are discharged at construction.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Step 4: abstract semantics – rooted reachability, crawls, and their certificates #
The paper defines a crawl as an r-rooted subtree of the website; the
semantics below phrases "some r-rooted subtree with node set S" as
"every node of S is reachable from r by edges inside S" – equivalent,
since the parent pointers of a breadth-first traversal assemble any such
reachable set into a tree, and it is precisely those parent pointers that
the first-order certificate recovers (each non-root node has an in-neighbour
strictly closer to the root). Reachability itself is a transitive-closure
condition, not first-order; as for connectivity in the Steiner-tree problem,
the certificate is what makes the membership proof possible, and it reuses
the DescriptiveComplexity.reachIn staging for the distance that
Relation.ReflTransGen does not carry.
The directed step available inside a chosen set: an edge whose two endpoints are both chosen.
Equations
- DescriptiveComplexity.DiLink Adjp S a b = (S a ∧ S b ∧ Adjp a b)
Instances For
Dependency graph
Every member of S is reachable from r by directed steps inside S –
the shape of an r-rooted subtree with node set S, reachability being all a
breadth-first traversal needs to assemble the tree.
Equations
- DescriptiveComplexity.ReachesAllOn Adjp r S = ∀ (x : A), S x → Relation.ReflTransGen (DescriptiveComplexity.DiLink Adjp S) r x
Instances For
Dependency graph
Rooted reachability is first-order certifiable: every member of S is
reachable from r inside S exactly when some strict partial order makes
every non-root member of S have a chosen in-neighbour strictly below it.
Walking down the order reaches the root; the order “distance to the root”
witnesses the converse. The in-neighbours below are the parent pointers of an
r-rooted spanning subtree of S.
Dependency graph
The crawling property #
Some marked root admits a crawl: a set of pages containing the root and every target, entirely reachable from the root inside itself, of size (the paper's total cost, at unit page costs) at most the number encoded by the marked set.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The certified form, with reachability witnessed by an order on the chosen set and the budget by an injection into the marked set: the shape the second-order definition guesses. The root is guessed as a relation constrained to hold of exactly one element, which the object language can express.
Dependency graph
CrawlOn transports along an equivalence commuting with the four
predicates.
Dependency graph
CrawlOn transports along an equivalence, iff version.
Dependency graph
Step 5: isomorphism-invariance and the bundled problem #
As always, the invariance proof is a transport of the RelMap shorthands
along the isomorphism via the shared relMap_equiv₁/₂ lemmas; the junk
conventions were stated with the semantics (a structure marking no root is a
no-instance, one marking several is read disjunctively).
A hyperlink in a website graph.
Equations
Instances For
Dependency graph
Being the root of a website graph.
Equations
Instances For
Dependency graph
Being a target page.
Equations
Instances For
Dependency graph
Belonging to the marked set carrying the budget.
Equations
Instances For
Dependency graph
A website graph admits a crawl within budget: a set of pages containing the marked root and every target, reachable from the root inside itself, with at most as many pages as the marked set has elements. (Finiteness of the universe is part of the property: cardinality thresholds are only meaningful on finite structures.)
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The crawling property is isomorphism-invariant.
Dependency graph
GRAPH CRAWLING (Gauquier–Manolescu–Senellart 2026, decision variant, unit page costs), as a problem on website graphs: is there a crawl – a set of pages containing the marked root and every target, reachable from the root inside itself – of at most as many pages as the marked set has elements?
Equations
- DescriptiveComplexity.GraphCrawling = { Holds := fun (A : Type) (inst : FirstOrder.Language.siteGraph.Structure A) => DescriptiveComplexity.HasCheapCrawl A, iso_invariant := ⋯ }
Instances For
Dependency graph
Step 6: faithfulness – the encoded problem is the concrete one #
The bridge between steps 1 and 5: the abstract problem computes the textbook
semantics on every encoded instance (Encoding.Faithful). With the size
bounds already discharged by the bundle of step 3, this equivalence is the
whole of the encoding obligation – from here on, every theorem about
GraphCrawling reads back to the concrete instances. The worked instance
and the #guards then run the encoder against a hand computation.
The decoding direction is where well-formedness earns its keep. An abstract
structure may mark several roots (read disjunctively by the semantics), and
no packaged instance – carrying a single root – transcribes such a structure
without deciding which root works, a computation a decoder should not
contain. The well-formedness sentence crawlWFSentence ("exactly one root")
removes these, and on well-formed structures a computable decoder
crawlDecode exists: find the root, read the links, targets and clamped
budget off the tables. It assembles into
DescriptiveComplexity.crawlDecoding, runs (#guards below), and step 9
restricts the completeness theorem to well-formed instances accordingly
(crawlWF_NP_complete). See DescriptiveComplexity/Decoding.lean for why
the bundled computation – and not an ∃-only covering statement – is the
meaningful notion.
The packaged encoding is faithful: the abstract problem
GraphCrawling computes the textbook semantics on every encoded instance –
obligation (1), with obligation (2) already discharged by the bundle.
Dependency graph
A small worked instance: four pages, root 0, links 0 → 1, 1 → 2
and 0 → 3, targets {2, 3}, budget 4 – a tight yes-instance (both
targets need the whole chain through 1).
Equations
Instances For
Dependency graph
The packaged instance is a concrete yes-instance: crawl everything.
Dependency graph
As in the CQ tutorial, the encoder can be run: the #guards evaluate the
encoded relations of the worked instance against the hand computation.
The well-formedness sentence, its meaning, and the decoder.
Well-formedness of a website graph: exactly one root page. What makes an honest decoder possible.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Dependency graph
The root pages of a presented website.
Equations
- DescriptiveComplexity.crawlRoots S = {x : Fin S.card | S.relBool FirstOrder.Language.wsRoot ![x] = true}
Instances For
Dependency graph
Dependency graph
Decode a presented website whose unique root has been found: read the links, targets and budget off the tables, the budget clamped to the page count as the packaging requires. (The root's existence is what makes the page count positive.)
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The decoder: none unless the site has exactly one root.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Dependency graph
Dependency graph
The computable decoding of well-formed website graphs. Together with
crawlEncoding_faithful it closes the loop between the concrete and the
abstract problem – a decoder that exists only because well-formedness
removed the multi-root structures.
Equations
Instances For
Dependency graph
The worked instance of step 1, presented as raw tables.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Step 7: membership in NP #
NP is defined as Σ₁ second-order definability, so membership means
exhibiting a second-order sentence: guess an object-level certificate, check
it with a first-order kernel. The certificate is the one of
DescriptiveComplexity.crawlOn_iff_certificate: the crawled set, its root, the
order witnessing reachability, and the injection witnessing the budget – four
relation variables, nine first-order clauses. The construction mirrors the
Steiner-tree kernel (DescriptiveComplexity.Problems.Steiner.Membership), with
three differences worth spotting: the step clause walks into each crawled
page along a directed edge, the guessed root must exist and carry the
vocabulary's root mark, and the budget injection is total on the whole
crawled set, since the paper's cost counts every crawled page.
The four relation variables guessed by the Σ₁ definition of Graph
Crawling.
- set : CrawlGuess
The crawled set of pages.
- root : CrawlGuess
The root the crawl starts from.
- order : CrawlGuess
The order certifying reachability from the root.
- inj : CrawlGuess
The injection witnessing the budget.
Instances For
Dependency graph
Dependency graph
Equations
- One or more equations did not get rendered due to their size.
Dependency graph
The single existential block of the Σ₁ definition of Graph Crawling.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
The symbol of the crawled-set relation variable.
Equations
Instances For
Dependency graph
The symbol of the root relation variable.
Equations
Instances For
Dependency graph
The symbol of the order relation variable.
Equations
Instances For
Dependency graph
The symbol of the injection relation variable.
Equations
Instances For
Dependency graph
The vocabulary of the kernel.
Equations
Instances For
Dependency graph
The edge symbol in the kernel's vocabulary.
Instances For
Dependency graph
The root-mark symbol in the kernel's vocabulary.
Instances For
Dependency graph
The target symbol in the kernel's vocabulary.
Instances For
Dependency graph
The budget-mark symbol in the kernel's vocabulary.
Instances For
Dependency graph
The crawled-set symbol in the kernel's vocabulary.
Instances For
Dependency graph
The root symbol in the kernel's vocabulary.
Instances For
Dependency graph
The order symbol in the kernel's vocabulary.
Instances For
Dependency graph
The injection symbol in the kernel's vocabulary.
Instances For
Dependency graph
The clauses #
The first-order kernel of the Σ₁ definition of Graph Crawling.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Realization #
Graph Crawling is Σ₁-definable: guess the crawled set, its root, an
order certifying that everything crawled is reachable from the root, and an
injection of the crawled set into the marked set, then check the nine
conditions first-order.
Dependency graph
Step 8: NP-hardness, by reduction from Set Cover #
The hardness half follows the paper's proof: a set-cover instance becomes a
depth-2 website – a root linking to one page per set of the family, each set
page linking to the elements it contains – whose targets are the elements,
and a cover of at most B sets is exactly a crawl of at most |U| + B + 1
pages (the root, the chosen sets, and all the elements).
The budget |U| + B + 1 is assembled per tag: the marked set of the produced
instance is the elements (on the element tag, |U|), the marked elements of
the source (on the set tag, B), and one copy of the minimum of the
input's order (on the root tag, 1). The + 1 is why this is an ordered
reduction: a marked set with exactly one more element needs a canonical
singleton, which only an order provides. The root mark and the root's
out-edges are guarded by minimality too, so the junk copies of the root tag
are unmarked and edgeless.
Cardinality bookkeeping is by tag slices: a set of interpreted elements that
is a per-tag family of subsets of the input has as cardinality the sum of
the slices (DescriptiveComplexity.ncard_tagged_eq_sum), and both the crawl
produced from a cover and the marked set decompose this way, so both
directions of the correctness proof reduce to arithmetic on three slice
sizes.
Tags for the interpretation of website graphs in set systems.
- root : CrawlTag
(root, a): a copy of the crawl root (only the copy of the minimum is marked as root, linked, and counted). - fam : CrawlTag
(fam, a): the page of the setaof the family. - elem : CrawlTag
(elem, a): the page of the ground elementa– a target.
Instances For
Dependency graph
Dependency graph
Dependency graph
Equations
- One or more equations did not get rendered due to their size.
Dependency graph
The ground-element symbol over the ordered expansion of set systems.
Instances For
Dependency graph
The family symbol over the ordered expansion of set systems.
Instances For
Dependency graph
The incidence symbol over the ordered expansion of set systems.
Instances For
Dependency graph
The mark symbol over the ordered expansion of set systems.
Instances For
Dependency graph
The interpretation producing, from a set system, the depth-2 website of
the paper's reduction: the minimum's root copy links to every set of the
family, each set links to its elements, the elements are the targets, and the
marked set carries |U| + B + 1.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Dependency graph
Dependency graph
Dependency graph
Dependency graph
Counting by tag slices #
Membership in a per-tag family of subsets of the input, stated at the raw product type so that it applies to interpreted elements.
Equations
- DescriptiveComplexity.InSlices s p = (p.2 0 ∈ s p.1)
Instances For
Dependency graph
The cardinality of a per-tag family of slices is the sum of the slice sizes.
Dependency graph
The tag slices of the crawl produced from a subfamily G: the minimum on
the root tag, G on the set tag, the ground elements on the element tag.
Instantiated at G := SSMarked this is also the decomposition of the marked
set.
Equations
- DescriptiveComplexity.coverSlices G DescriptiveComplexity.CrawlTag.root = {a : A | ∀ (b : A), a ≤ b}
- DescriptiveComplexity.coverSlices G DescriptiveComplexity.CrawlTag.fam = {a : A | G a}
- DescriptiveComplexity.coverSlices G DescriptiveComplexity.CrawlTag.elem = {a : A | DescriptiveComplexity.SSElem a}
Instances For
Dependency graph
On a finite linearly ordered nonempty type, the set of minima is a singleton.
Dependency graph
The marked set of the interpreted website decomposes into tag slices.
Dependency graph
Correctness #
Correctness of the interpretation: the set system has a small cover iff the interpreted website has a cheap crawl.
Dependency graph
Set Cover ordered-FO-reduces to Graph Crawling – the reduction of
Gauquier–Manolescu–Senellart 2026, Prop. 4: a cover
of at most B sets is a crawl of at most |U| + B + 1 pages of the depth-2
website.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Dependency graph
Step 9: completeness #
Membership and hardness combine into the completeness theorem – the
formalized counterpart of Prop. 4 of
Gauquier–Manolescu–Senellart 2026. Through the
faithfulness theorem of step 6, it is a statement about the packaged
concrete instances of step 1: ConcreteCrawlHolds is decided by a problem
that is NP-complete, over an encoding that provably neither pads nor
compresses.
Graph Crawling is in NP.
Dependency graph
Graph Crawling is NP-hard: Set Cover, which is NP-hard, reduces to it by an ordered FO reduction.
Dependency graph
The graph crawling problem is NP-complete (Gauquier–Manolescu–Senellart 2026, Prop. 4).
Dependency graph
The image of the hardness reduction is well-formed: the only root-marked page of an interpreted website is the minimum's root copy.
Dependency graph
Well-formed graph crawling is NP-complete: crawling restricted to
websites with exactly one root – the instances the decoder of step 6
handles. Both halves are one-line upgrades of the plain completeness proof
(OrderedFOReduction.withInvariant, SigmaSODefinable.inf_ofSentence).