Terrible Search Engine

Using simulated annealing on websites with links pointing to neighbours is not a good idea, but a good example for async annealing.

from urllib.parse import urljoin
from random import choice

from anyio import sleep
from httpx import AsyncClient
from bs4 import BeautifulSoup
from altbacken.core.async_annealing import AnnealingState
from altbacken.external.async_annealing import SimpleAsyncSimulatedAnnealing
type URL = str | None
type Weight = int
SEARCH_KEYS: dict[str, Weight] = {
    "Python": -1,
    "Moon": -5,
    "Bass": -8,
    "Cheesecake": -10
}
class LinkNeighbourhood:
    async def __call__(self, state: AnnealingState[URL]) -> URL:
        if current_url := state.current_solution:
            try:
                async with AsyncClient(follow_redirects=True) as client:
                    response = await client.get(current_url)
                    if not response.is_success:
                        return current_url
                    await sleep(0.5)
                    soup = BeautifulSoup(response.text, "html.parser")
                    links: list[str] = [
                        urljoin(current_url, link["href"])
                        for link in soup.find_all("a", {"href": True})
                    ]
                    return choice(links) if links else None
            except Exception:
                return current_url
        else:
            return None
async def fitness(url: URL) -> float:
    if url is None:
        return 100.0
    async with AsyncClient(follow_redirects=True) as client:
        response = await client.get(url)
        await sleep(0.5)
        if not response.is_success:
            return 50.0
        text: str = response.text
        return sum(
            text.count(key) * weight
            for key, weight in SEARCH_KEYS.items()
        )
annealing: SimpleAsyncSimulatedAnnealing[URL] = SimpleAsyncSimulatedAnnealing(
    fitness,
    LinkNeighbourhood()
)

visited_sites: set[URL] = set()

async def tracer(state: AnnealingState[URL]) -> None:
    visited_sites.add(state.current_solution)

annealing.tracer = tracer
best_url, value = await annealing.simulate("https://stackoverflow.com/questions")
best_url, value
('https://stackoverflow.com/questions/79972182/scipy-import-from-module-fails-sometimes-path-dependent',
 -6)
visited_sites
{None,
 'https://biology.stackexchange.com/users/129209/keiv',
 'https://stackexchange.com',
 'https://stackoverflow.com/questions/79972182/scipy-import-from-module-fails-sometimes-path-dependent',
 'https://stackoverflowteams.com',
 'https://stackoverflowteams.com/teams/create/free/?utm_medium=referral&utm_source=biology-community&utm_campaign=side-bar&utm_content=explore-teams',
 'https://stackoverflowteams.com/teams/create/free?isLogin=false',
 'https://stackoverflowteams.com/teams/create/free?isLogin=true',
 'https://www.instagram.com/thestackoverflow'}