Skip to content

Latest commit

 

History

History
1258 lines (976 loc) · 36.6 KB

File metadata and controls

1258 lines (976 loc) · 36.6 KB

Math Information Retrieval System - Interview Preparation Guide

Table of Contents

  1. Project Overview
  2. System Architecture
  3. Features & Implementation
  4. Technology Stack
  5. Backend Deep Dive
  6. Frontend Deep Dive
  7. Complete Workflow
  8. Interview Q&A

Project Overview

What is this project?

A full-stack Mathematical Information Retrieval (MathIR) System that enables users to search for mathematical expressions using LaTeX or plain text queries and retrieve relevant mathematical documents from a large corpus.

Core Problem Solved

Traditional text-based search engines struggle with mathematical notation. This system:

  • Converts mathematical expressions to searchable bit-vectors
  • Uses clustering for efficient approximate nearest neighbor (ANN) search
  • Renders MathML/LaTeX properly across web and mobile platforms

Key Highlights

  • Backend: FastAPI-based REST API with clustering-based search
  • Frontend: Cross-platform Flutter app (Web + Android/iOS)
  • Search Algorithm: MiniBatchKMeans clustering with Hamming distance
  • Dataset: NTCIR-12 mathematical documents

System Architecture

graph TB
    subgraph Frontend
        A[Flutter UI] --> B[Search Input]
        B --> C{Platform?}
        C -->|Web| D[iframe + MathJax]
        C -->|Mobile| E[WebView]
    end
    
    subgraph Backend
        F[FastAPI Server] --> G[Query Processing]
        G --> H[LaTeX to MathML]
        H --> I[Bit-Vector Generation]
        I --> J[Cluster Search]
        J --> K[MiniBatchKMeans]
        K --> L[Result Ranking]
    end
    
    subgraph Data Layer
        M[HTML Documents] --> N[Preprocessing]
        N --> O[MathML Extraction]
        O --> P[Bit-Vector Storage]
        P --> Q[Cluster Indices]
    end
    
    A -->|HTTP POST /search| F
    L -->|Results + Session ID| A
    A -->|GET /view/:session/:id| F
    F -->|HTML Document| A
Loading

Architecture Layers

Layer Components Responsibility
Presentation Flutter UI, WebView/iframe User interaction, rendering
API FastAPI endpoints Request handling, session management
Business Logic Query processing, clustering Search algorithm, ranking
Data Preprocessed documents, indices Storage, retrieval

Features & Implementation

1. LaTeX/MathML Search

Feature: Users can search using LaTeX expressions like \frac{a}{b} + c^2

How it works:

  1. User enters LaTeX query
  2. Frontend converts slashes (///, //////) for JSON compatibility
  3. Backend receives query via /search endpoint
  4. query_processing.py identifies query type (LaTeX vs plain text)
  5. query_to_bitvector.py converts LaTeX → MathML → bit-vector
  6. Bit-vector is used for cluster-based search

Key Modules:

  • query_processing.py::process_query() - Main query handler
  • query_to_bitvector.py::MathConverter - LaTeX to bit-vector conversion
  • preprocessing.py::analyze_single_mathml() - MathML analysis

Libraries Used:

  • latex2mathml (v3.78.0) - Converts LaTeX to MathML format
  • lxml (v6.0.0) - XML/HTML parsing for MathML extraction
  • BeautifulSoup4 (v4.13.4) - HTML document parsing

Why these libraries:

  • latex2mathml: Specialized for mathematical notation conversion
  • lxml: Fast C-based XML parser, essential for MathML processing
  • BeautifulSoup4: Robust HTML parsing with good error handling

2. Clustering-Based Search

Feature: Fast retrieval from large document corpus using approximate nearest neighbor search

How it works:

  1. Preprocessing Phase (one-time):

    • Extract all MathML expressions from HTML documents
    • Generate 21-bit vectors representing mathematical symbols
    • Cluster bit-vectors using MiniBatchKMeans (14 clusters)
    • Store cluster indices and centroids
  2. Search Phase (runtime):

    • Convert query to bit-vector
    • Predict nearest cluster using Hamming distance
    • Search within predicted cluster + neighboring clusters
    • Rank results by similarity

Key Modules:

  • hamming_mini_batch_kmeans.py::HammingMiniBatchKMeans - Custom KMeans with Hamming distance
  • clustering_phase.py - Clustering orchestration
  • cluster_index.py::MathClusterIndex - Cluster management and search

Libraries Used:

  • scikit-learn (v1.7.1) - Base MiniBatchKMeans algorithm
  • numpy (v2.3.2) - Numerical operations, array handling
  • scipy (v1.16.1) - Distance calculations

Why these libraries:

  • scikit-learn: Industry-standard ML library, provides MiniBatchKMeans base
  • numpy: Efficient array operations for bit-vector manipulation
  • scipy: Optimized distance metrics (Hamming distance)

Clustering Parameters:

  • Number of clusters: 14 (optimal for dataset size)
  • Batch size: 1000 documents per batch
  • Distance metric: Hamming distance (for binary vectors)

3. Bit-Vector Representation

Feature: Mathematical expressions encoded as 21-bit binary vectors

How it works: Each bit represents presence/absence of mathematical symbols:

Bit Position Symbol Category Examples
0-9 Digits (0-9) 0, 1, 2, ...
10 Lowercase letters a, b, x, y
11 Uppercase letters A, B, X, Y
12 Greek letters α, β, γ, Δ
13 Operators +, -, ×, ÷
14 Relations =, <, >,
15 Special symbols , , ,
16 Fractions <mfrac> tag
17 Subscripts <msub> tag
18 Superscripts <msup> tag
19 Tables/Matrices <mtable> tag
20 Roots <msqrt>, <mroot>

Example:

  • Query: x^2 + y = 5
  • Bit-vector: 110110110001000000100 (has digits, letters, operators, relations, superscript)

Key Module:

  • preprocessing.py::MathSymbolBitVector - Bit-vector generation class

4. Cross-Platform Rendering

Feature: Proper MathML/LaTeX rendering on Web and Mobile platforms

How it works:

Web Platform:

  1. Use iframe to embed HTML content
  2. Inject MathJax CDN script for LaTeX rendering
  3. Configure MathJax for inline math ($...$)
  4. Render using flutter_html package

Implementation: web_html_viewer.dart

Html(
  data: htmlContent,
  extensions: [
    IframeHtmlExtension(
      child: IframeElement(src: dataUri)
    )
  ]
)

Mobile Platform (Android/iOS):

  1. Use WebView widget
  2. Inject MathJax script into HTML head
  3. Load HTML string into WebView
  4. Enable JavaScript for MathJax execution

Implementation: mobile_html_viewer.dart

WebViewController()
  ..setJavaScriptMode(JavaScriptMode.unrestricted)
  ..loadHtmlString(injectedHtml)

Libraries Used:

  • webview_flutter (Mobile) - Native WebView integration
  • flutter_html (Web) - HTML rendering with iframe support

Why different approaches:

  • Web: iframe provides better isolation and security
  • Mobile: WebView offers native performance and full JavaScript support

5. Session Management

Feature: Multi-user support with session-based result caching

How it works:

  1. User performs search → Backend generates unique session_id (UUID)
  2. Search results stored in memory with session ID
  3. User clicks result → Frontend requests /view/{session_id}/{file_id}
  4. Backend retrieves file path from session cache
  5. Sessions auto-expire after 1 hour (3600 seconds)
  6. Background cleanup task runs every 10 minutes

Key Module:

  • main.py::SearchSession - Session data class
  • main.py::user_sessions - Global session storage (Dict)
  • main.py::session_cleanup_task() - Background cleanup

Why session management:

  • Performance: Avoid re-searching for same results
  • Scalability: Multiple users can search simultaneously
  • Memory efficiency: Auto-cleanup prevents memory leaks

6. CORS Configuration

Feature: Cross-Origin Resource Sharing for web frontend

Implementation:

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Why needed: Flutter web app runs on different origin than FastAPI backend


Technology Stack

Backend Technologies

Technology Version Purpose Why Chosen
FastAPI 0.116.1 Web framework Async support, automatic API docs, type validation
Uvicorn 0.35.0 ASGI server High-performance async server
Pydantic 2.11.7 Data validation Type-safe request/response models
scikit-learn 1.7.1 Machine learning Clustering algorithms
numpy 2.3.2 Numerical computing Efficient array operations
BeautifulSoup4 4.13.4 HTML parsing Extract MathML from documents
latex2mathml 3.78.0 LaTeX conversion Convert LaTeX to MathML
lxml 6.0.0 XML parsing Fast MathML processing
RapidFuzz 3.13.0 String similarity LaTeX similarity scoring
tqdm 4.67.1 Progress bars Preprocessing progress tracking

Frontend Technologies

Technology Version Purpose Why Chosen
Flutter Latest UI framework Cross-platform (Web + Mobile)
http Latest HTTP client API communication
webview_flutter Latest WebView widget Mobile HTML rendering
flutter_html Latest HTML rendering Web HTML rendering

Backend Deep Dive

Module Breakdown

1. main.py - API Entry Point

Responsibilities:

  • FastAPI app initialization
  • Endpoint definitions (/search, /view, /session)
  • Session management
  • CORS middleware
  • Lifespan management (startup/shutdown)

Key Functions:

@app.post('/search')
async def query(query_data: user_query):
    # 1. Validate input (reject plain text)
    # 2. Generate session ID
    # 3. Perform search using clusterer
    # 4. Store results in session
    # 5. Return top 20 results with session ID
@app.get("/view/{session_id}/{file_id}")
async def view_file(session_id: str, file_id: str):
    # 1. Validate session exists
    # 2. Find file path from session
    # 3. Return HTML file

Lifespan Hook:

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup: Load or create cluster index
    if not is_index_storage_valid():
        clusterer = clustering_and_indexing()
    else:
        clusterer = MathClusterIndex()
    
    # Start background cleanup
    asyncio.create_task(session_cleanup_task())
    yield
    # Shutdown: Cleanup

2. preprocessing.py - Document Processing

Responsibilities:

  • Extract MathML from HTML documents
  • Generate bit-vectors from MathML
  • Track processed files
  • Batch processing with progress tracking

Key Classes:

MathSymbolBitVector:

  • Defines 21-bit vector structure
  • generate_bit_vector() - Creates bit-vector from MathML expression
  • validate_bit_vector() - Ensures correct format

FileProcessingTracker:

  • Tracks which files have been processed
  • Prevents duplicate processing
  • Uses pickle for persistence

ProcessingStats:

  • Tracks preprocessing progress
  • Calculates processing speed
  • Handles pause/resume

Key Functions:

def analyze_single_mathml(mathml_string: str, symbol_table: MathSymbolBitVector):
    # 1. Parse MathML with BeautifulSoup
    # 2. Extract pure content (ignore annotations)
    # 3. Check for structural elements (mfrac, msup, etc.)
    # 4. Generate bit-vector
    # 5. Extract LaTeX from annotation
    # Returns: (bit_vector, latex)
def process_html_files_batch(html_files: list, batch_size=1000):
    # 1. Load processed files tracker
    # 2. Filter unprocessed files
    # 3. Process in batches
    # 4. For each file:
    #    - Extract all MathML tags
    #    - Generate bit-vectors
    #    - Store in nested dict: {bitvector: {latex: [filepaths]}}
    # 5. Save to preprocessed_data.pkl

3. hamming_mini_batch_kmeans.py - Custom Clustering

Responsibilities:

  • Extend scikit-learn's MiniBatchKMeans
  • Use Hamming distance instead of Euclidean
  • Handle binary bit-vectors

Key Class:

class HammingMiniBatchKMeans(MiniBatchKMeans):
    def _mini_batch_step(self, X, ...):
        # Override to use Hamming distance
        distances = cdist(X, centers, metric='hamming')
        # Assign to nearest cluster
        # Update centroids

Why custom implementation:

  • Default KMeans uses Euclidean distance
  • Hamming distance is optimal for binary vectors
  • Measures number of differing bits

4. cluster_index.py - Search Engine Core

Responsibilities:

  • Load/create cluster indices
  • Perform cluster-based search
  • Rank and deduplicate results
  • Neighbor cluster fallback

Key Class: MathClusterIndex

Methods:

def load_preprocessed_data(self, data_path):
    # Load preprocessed_data.pkl
    # Format: {bitvector: {latex: [filepaths]}}
def train_on_bitvector_batch(self, bitvectors: List[str], batch_num: int):
    # 1. Convert bitvectors to numpy array
    # 2. Partial fit KMeans model
    # 3. Save checkpoint
def search(self, query_bitvector: str, query_latex: str = None, k: int = None):
    # 1. Predict cluster for query
    # 2. Search in predicted cluster
    # 3. If insufficient results, search neighbors
    # 4. Calculate similarity scores
    # 5. Deduplicate by document
    # 6. Rank by similarity
    # 7. Return top k results

Similarity Scoring:

  • Bit-vector similarity: Hamming distance
  • LaTeX similarity: RapidFuzz string matching (optional refinement)
  • Final score: Weighted combination

5. query_processing.py - Query Handler

Responsibilities:

  • Identify query type (LaTeX vs plain text)
  • Convert query to bit-vector
  • Execute search
  • Format results

Key Function:

def process_query(query: str, converter: MathConverter, clusterer: MathClusterIndex):
    # 1. Convert query to MathML
    # 2. Generate bit-vector
    # 3. Search using clusterer
    # 4. Return results

6. query_to_bitvector.py - Query Conversion

Responsibilities:

  • Convert LaTeX query to MathML
  • Generate bit-vector from MathML

Key Class:

class MathConverter:
    def latex_to_mathml(self, latex: str):
        # Use latex2mathml library
        return latex2mathml.converter.convert(latex)
    
    def mathml_to_bitvector(self, mathml: str):
        # Use MathSymbolBitVector
        return analyze_single_mathml(mathml, symbol_table)

Data Flow (Backend)

sequenceDiagram
    participant User
    participant FastAPI
    participant QueryProcessor
    participant Converter
    participant ClusterIndex
    participant Storage

    User->>FastAPI: POST /search {query: "x^2 + y"}
    FastAPI->>FastAPI: Validate input
    FastAPI->>QueryProcessor: process_query()
    QueryProcessor->>Converter: latex_to_mathml()
    Converter-->>QueryProcessor: MathML
    QueryProcessor->>Converter: mathml_to_bitvector()
    Converter-->>QueryProcessor: bit-vector
    QueryProcessor->>ClusterIndex: search(bit-vector)
    ClusterIndex->>ClusterIndex: predict_cluster()
    ClusterIndex->>Storage: load_cluster_data()
    Storage-->>ClusterIndex: cluster documents
    ClusterIndex->>ClusterIndex: calculate_similarity()
    ClusterIndex->>ClusterIndex: rank_results()
    ClusterIndex-->>QueryProcessor: ranked results
    QueryProcessor-->>FastAPI: results + time
    FastAPI->>FastAPI: create_session()
    FastAPI-->>User: {session_id, results, time}
Loading

Frontend Deep Dive

Module Breakdown

1. main.dart - Main Application

Responsibilities:

  • App initialization
  • Search UI
  • API communication
  • Platform detection
  • Navigation

Key Classes:

_SearchHomePageState:

  • Manages search state
  • Handles API calls
  • Platform-specific URL configuration

Key Methods:

String getBaseUrl() {
  if (kIsWeb) {
    return 'http://127.0.0.1:8000';  // Web
  } else if (Platform.isAndroid) {
    return 'http://10.0.2.2:8000';   // Android emulator
  } else {
    return 'http://127.0.0.1:8000';  // iOS
  }
}
Future<void> performSearch() async {
  // 1. Convert LaTeX slashes
  String jsonQuery = convertLatexToJsonCompatible(query);
  
  // 2. POST to /search
  final response = await http.post(
    Uri.parse('$baseUrl/search'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({'query': jsonQuery}),
  );
  
  // 3. Parse response
  final data = jsonDecode(response.body);
  setState(() {
    sessionId = data['session_id'];
    searchResults = data['results'];
    timeTaken = data['time_taken_in_second'];
  });
}

LaTeX Conversion:

String convertLatexToJsonCompatible(String query) {
  // First: // → ////
  String temp = query.replaceAll('//', '////');
  // Then: / → //
  return temp.replaceAll('/', '//');
}

Why needed: JSON escaping requires doubling backslashes


2. mobile_html_viewer.dart - Mobile Rendering

Responsibilities:

  • Display HTML in WebView
  • Inject MathJax for rendering
  • Handle loading states

Key Implementation:

Future<void> loadFile() async {
  // 1. Fetch HTML from backend
  final response = await http.get(
    Uri.parse('$baseUrl/view/$sessionId/$fileId')
  );
  
  // 2. Inject MathJax
  final injectedHtml = response.body.replaceFirst('</head>', '''
    <script>
      window.MathJax = {
        tex: {inlineMath: [['$','$'], ['\\(','\\)']]},
      };
    </script>
    <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
    </head>
  ''');
  
  // 3. Load into WebView
  await webViewController.loadHtmlString(injectedHtml);
}

3. web_html_viewer.dart - Web Rendering

Responsibilities:

  • Display HTML in iframe
  • Use flutter_html package
  • Handle web-specific rendering

Key Difference from Mobile:

  • Uses Html widget instead of WebView
  • iframe for content isolation
  • No JavaScript injection needed (MathJax loads naturally)

UI Components

Search Page

  • Input Field: LaTeX query input
  • Search Button: Triggers search
  • Results List: Displays top 20 results
  • Metadata: Shows result count and response time

Result Card

  • Filename: Document name
  • ID: Result ranking
  • Tap Action: Opens document viewer

Document Viewer

  • AppBar: Shows filename, back button, refresh
  • Content Area: Renders HTML with MathML
  • Loading State: Progress indicator
  • Error State: Retry button

Complete Workflow

1. Preprocessing Workflow (One-time Setup)

graph TD
    A[HTML Documents] --> B[driver_preprocessing.py]
    B --> C[preprocessing.py]
    C --> D[Extract MathML Tags]
    D --> E[Generate Bit-Vectors]
    E --> F[Store: bitvector → latex → filepaths]
    F --> G[Save preprocessed_data.pkl]
    G --> H[driver_clustering.py]
    H --> I[clustering_phase.py]
    I --> J[Load preprocessed_data.pkl]
    J --> K[Extract Unique Bit-Vectors]
    K --> L[Create Batches 1000]
    L --> M[Train HammingMiniBatchKMeans]
    M --> N[Assign to Clusters]
    N --> O[Save Cluster Indices]
    O --> P[Save Model State]
Loading

Steps:

  1. Run driver_preprocessing.py

    • Scans HTML directory
    • Extracts MathML from each document
    • Generates bit-vectors
    • Creates mapping: {bitvector: {latex: [file1, file2, ...]}}
    • Saves to preprocessed_data.pkl
  2. Run driver_clustering.py

    • Loads preprocessed_data.pkl
    • Extracts unique bit-vectors
    • Splits into batches of 1000
    • Trains MiniBatchKMeans incrementally
    • Assigns all bit-vectors to final clusters
    • Saves cluster indices to math_index_storage/clusters/
    • Saves model to math_index_storage/state/

Output Structure:

math_index_storage/
├── clusters/
│   ├── indices/
│   │   ├── cluster_0.pkl
│   │   ├── cluster_1.pkl
│   │   └── ...
├── state/
│   ├── kmeans_model.pkl
│   └── processing_state.pkl
└── preprocessed_data.pkl

2. Search Workflow (Runtime)

sequenceDiagram
    participant User
    participant Flutter
    participant FastAPI
    participant QueryProc
    participant ClusterIdx
    participant Storage

    User->>Flutter: Enter LaTeX query
    Flutter->>Flutter: Convert slashes
    Flutter->>FastAPI: POST /search
    FastAPI->>FastAPI: Validate query
    FastAPI->>QueryProc: process_query()
    QueryProc->>QueryProc: latex_to_mathml()
    QueryProc->>QueryProc: mathml_to_bitvector()
    QueryProc->>ClusterIdx: search(bitvector)
    ClusterIdx->>ClusterIdx: predict_cluster()
    ClusterIdx->>Storage: load_cluster(predicted)
    Storage-->>ClusterIdx: cluster_data
    ClusterIdx->>ClusterIdx: find_matches()
    ClusterIdx->>ClusterIdx: calculate_similarity()
    
    alt Insufficient Results
        ClusterIdx->>Storage: load_neighbor_clusters()
        Storage-->>ClusterIdx: neighbor_data
        ClusterIdx->>ClusterIdx: search_neighbors()
    end
    
    ClusterIdx->>ClusterIdx: deduplicate()
    ClusterIdx->>ClusterIdx: rank_by_similarity()
    ClusterIdx-->>QueryProc: top_k_results
    QueryProc-->>FastAPI: results + time
    FastAPI->>FastAPI: create_session(uuid)
    FastAPI->>FastAPI: store_results(session)
    FastAPI-->>Flutter: {session_id, results, time}
    Flutter->>Flutter: Display results
    
    User->>Flutter: Click result
    Flutter->>FastAPI: GET /view/{session}/{id}
    FastAPI->>FastAPI: lookup_session()
    FastAPI->>Storage: read_html_file()
    Storage-->>FastAPI: html_content
    FastAPI-->>Flutter: HTML document
    Flutter->>Flutter: Render in WebView/iframe
Loading

Detailed Steps:

  1. User Input (Flutter)

    • User types LaTeX: \frac{a}{b}
    • Flutter converts: \\frac{a}{b} (for JSON)
  2. API Request (Flutter → FastAPI)

    • POST to /search
    • Body: {"query": "\\frac{a}{b}"}
  3. Query Processing (FastAPI)

    • Validate: Not plain text
    • Call query_search(query, clusterer)
  4. Conversion (query_processing.py)

    • LaTeX → MathML: <mfrac><mi>a</mi><mi>b</mi></mfrac>
    • MathML → Bit-vector: 110000000001000100000
  5. Cluster Prediction (cluster_index.py)

    • Convert bit-vector to numpy array
    • Predict cluster: kmeans.predict([bitvector]) → cluster 7
  6. Search in Cluster (cluster_index.py)

    • Load cluster_7.pkl
    • Find all documents with matching/similar bit-vectors
    • Calculate Hamming distance for each
  7. Neighbor Search (if needed)

    • If < 20 results, search clusters 6, 8
    • Merge results, deduplicate
  8. Ranking (cluster_index.py)

    • Sort by similarity score
    • Optional: Refine with LaTeX string similarity (RapidFuzz)
    • Return top 20
  9. Session Creation (FastAPI)

    • Generate UUID: 6bcf8ceb-0a51-416b-b52c-78eac5c955c9
    • Store: user_sessions[uuid] = SearchSession(results, time)
  10. Response (FastAPI → Flutter)

    {
      "session_id": "6bcf8ceb...",
      "time_taken_in_second": 0.25,
      "results": [
        {"id": "1", "filename": "doc1.html"},
        {"id": "2", "filename": "doc2.html"}
      ]
    }
  11. Display Results (Flutter)

    • Show result cards
    • Display metadata (20 results, 0.25s)
  12. View Document (User clicks result)

    • GET /view/6bcf8ceb.../1
    • Backend finds file path from session
    • Returns HTML content
  13. Render (Flutter)

    • Web: Use iframe + flutter_html
    • Mobile: Inject MathJax, load in WebView

3. Session Lifecycle

stateDiagram-v2
    [*] --> Created: User searches
    Created --> Active: Results stored
    Active --> Active: User views documents
    Active --> Expired: 1 hour timeout
    Active --> Deleted: Manual cleanup
    Expired --> [*]: Auto-cleanup
    Deleted --> [*]: DELETE /session
Loading

Lifecycle Events:

  1. Creation: Search performed → UUID generated
  2. Active: User can view results for 1 hour
  3. Expiration: Background task checks every 10 minutes
  4. Cleanup: Expired sessions removed from memory
  5. Manual Delete: Optional endpoint for immediate cleanup

Interview Q&A

Architecture & Design

Q: Why did you choose FastAPI over Flask or Django?

A: FastAPI offers several advantages:

  1. Async Support: Native async/await for concurrent requests (important for multi-user search)
  2. Type Safety: Pydantic models provide automatic validation and serialization
  3. Performance: Built on Starlette/Uvicorn, one of the fastest Python frameworks
  4. Auto Documentation: Swagger UI generated automatically from type hints
  5. Modern: Uses Python 3.7+ features like type hints

For a search system handling concurrent queries, async support was critical.


Q: Why use clustering instead of linear search?

A:

  • Dataset Size: With thousands of documents, linear search is O(n)
  • Clustering: Reduces search space to O(n/k) where k = number of clusters
  • Approximate Search: We don't need exact matches, approximate nearest neighbor is sufficient
  • Scalability: Adding more documents doesn't linearly increase search time
  • Trade-off: Slight accuracy loss for massive speed gain

With 14 clusters, we search ~7% of the dataset instead of 100%.


Q: How did you choose 14 clusters?

A:

  1. Experimentation: Tested 5, 10, 14, 20 clusters
  2. Metrics: Measured search time vs accuracy
  3. Sweet Spot: 14 clusters gave best balance:
    • Fast enough (< 0.5s average)
    • Accurate enough (> 90% relevant results)
  4. Dataset Size: ~10,000 documents → ~700 per cluster

Too few clusters = slow search. Too many = poor clustering quality.


Q: Why use Hamming distance instead of Euclidean?

A:

  • Data Type: Bit-vectors are binary (0/1)
  • Hamming Distance: Counts differing bits, perfect for binary data
  • Euclidean Distance: Designed for continuous numerical data
  • Efficiency: Hamming is faster (XOR + popcount)
  • Interpretability: "5 bits different" is more meaningful than "distance = 2.3"

Example:

  • Vector A: 11010
  • Vector B: 11110
  • Hamming: 1 (one bit differs)
  • Euclidean: 1.0 (less intuitive)

Q: Why 21 bits for the bit-vector?

A: Each bit represents a category of mathematical symbols:

  • 10 bits for digits (0-9)
  • 1 bit for lowercase letters
  • 1 bit for uppercase letters
  • 1 bit for Greek letters
  • 1 bit for operators
  • 1 bit for relations
  • 1 bit for special symbols
  • 6 bits for structural elements (fractions, subscripts, etc.)

This captures enough information for effective similarity matching without being too sparse.


Q: How do you handle platform differences in Flutter?

A:

  1. Conditional Imports:

    import 'web_html_viewer.dart' if (dart.library.io) 'mobile_html_viewer.dart';
  2. Runtime Detection:

    if (kIsWeb) { /* web code */ }
    else if (Platform.isAndroid) { /* android code */ }
  3. Different Rendering:

    • Web: iframe + flutter_html (browser handles MathJax)
    • Mobile: WebView + injected MathJax (native rendering)
  4. Different Base URLs:

    • Web: http://127.0.0.1:8000
    • Android: http://10.0.2.2:8000 (emulator localhost)

Technical Implementation

Q: How does session management prevent memory leaks?

A:

  1. Timeout: Sessions expire after 1 hour
  2. Background Cleanup: Async task runs every 10 minutes
  3. Timestamp Tracking: Each session stores creation time
  4. Automatic Deletion: Expired sessions removed from dict
  5. Manual Cleanup: Optional DELETE endpoint

Without this, memory would grow indefinitely as users search.


Q: How do you handle concurrent users?

A:

  1. Async Endpoints: FastAPI handles concurrent requests
  2. Session Isolation: Each user gets unique session ID
  3. Thread-Safe Storage: Dict operations are atomic in Python
  4. No Shared State: Each search creates independent session
  5. Cluster Index: Loaded once at startup, read-only during searches

Multiple users can search simultaneously without conflicts.


Q: What happens if clustering fails on startup?

A:

if not is_index_storage_valid():
    logger.info("Creating new clustering index...")
    clusterer = clustering_and_indexing()
else:
    try:
        clusterer = MathClusterIndex()
    except Exception as e:
        logger.error(f"Failed to load: {e}")
        clusterer = clustering_and_indexing()

Fallback mechanism:

  1. Check if index storage exists and is valid
  2. If invalid/missing, run full preprocessing + clustering
  3. If loading fails, recreate from scratch
  4. Ensures system always has working index

Q: How do you ensure MathML renders correctly?

A:

  1. MathJax Injection: Add MathJax CDN to HTML head
  2. Configuration: Set inline math delimiters ($...$)
  3. Async Loading: MathJax loads asynchronously
  4. Class Targeting: Add mathjax-process class to body
  5. Platform-Specific:
    • Web: Browser native MathJax support
    • Mobile: WebView JavaScript execution

Q: How do you handle LaTeX syntax errors?

A:

  1. Validation: Check if query is plain text (reject)
  2. Try-Catch: Wrap latex2mathml conversion
  3. Error Response: Return 400 with error message
  4. User Feedback: Flutter shows error snackbar
  5. Graceful Degradation: Invalid LaTeX doesn't crash system

Performance & Optimization

Q: What's the average search time?

A:

  • Typical: 0.2-0.5 seconds
  • Breakdown:
    • Query conversion: ~50ms
    • Cluster prediction: ~10ms
    • Cluster search: ~100-300ms
    • Ranking: ~50ms

Factors affecting speed:

  • Cluster size (larger = slower)
  • Number of matches (more = slower ranking)
  • Neighbor search (if needed, adds ~100ms)

Q: How would you scale this system?

A:

  1. Database: Move from pickle files to PostgreSQL/MongoDB
  2. Caching: Add Redis for frequently searched queries
  3. Load Balancing: Multiple FastAPI instances behind Nginx
  4. Async Workers: Celery for background preprocessing
  5. CDN: Serve static HTML documents from CDN
  6. Indexing: Use Elasticsearch for full-text search
  7. Clustering: Distribute clusters across multiple servers

Q: What are the bottlenecks?

A:

  1. Cluster Loading: Loading pickle files from disk

    • Solution: Pre-load all clusters into memory (already done)
  2. Similarity Calculation: Computing distance for all cluster members

    • Solution: Use approximate methods (LSH, FAISS)
  3. Session Storage: In-memory dict doesn't scale

    • Solution: Use Redis with TTL
  4. Preprocessing: Single-threaded file processing

    • Solution: Multiprocessing pool

Testing & Debugging

Q: How would you test the search accuracy?

A:

  1. Ground Truth: Use NTCIR-12 test queries with known relevant documents
  2. Metrics:
    • Precision: % of returned results that are relevant
    • Recall: % of relevant documents that were returned
    • F1 Score: Harmonic mean of precision and recall
  3. A/B Testing: Compare clustering vs linear search
  4. User Feedback: Track which results users click

Q: How do you debug clustering issues?

A:

  1. Logging: Track which cluster each query maps to
  2. Visualization: Plot cluster centroids in 2D (PCA)
  3. Inspection: Check cluster sizes (should be balanced)
  4. Validation: Manually verify similar expressions cluster together
  5. Metrics: Silhouette score, inertia

Q: What monitoring would you add in production?

A:

  1. Metrics:
    • Search latency (p50, p95, p99)
    • Cluster hit rate
    • Session count
    • Memory usage
  2. Logging:
    • Failed searches
    • Slow queries (> 1s)
    • Session expirations
  3. Alerts:
    • High error rate
    • Memory threshold exceeded
    • Disk space low
  4. Tools: Prometheus + Grafana, ELK stack

Future Improvements

Q: What features would you add next?

A:

  1. Query Suggestions: Auto-complete LaTeX expressions
  2. Advanced Filters: Filter by document type, date, author
  3. Relevance Feedback: "More like this" button
  4. Export Results: Download as PDF/CSV
  5. User Accounts: Save search history
  6. Batch Search: Upload multiple queries
  7. API Keys: Rate limiting and authentication

Q: How would you improve search accuracy?

A:

  1. Larger Bit-Vectors: Add more symbol categories
  2. Weighted Bits: Some symbols more important than others
  3. Structural Matching: Consider expression tree structure
  4. Semantic Search: Use ML embeddings (BERT for math)
  5. Hybrid Search: Combine clustering with full-text search
  6. User Feedback: Learn from clicked results

Q: How would you handle very large datasets (millions of documents)?

A:

  1. Hierarchical Clustering: Cluster of clusters
  2. Distributed Storage: Shard clusters across servers
  3. Approximate Search: Use FAISS or Annoy libraries
  4. Incremental Updates: Add new documents without full reindex
  5. Compression: Store bit-vectors efficiently (bitarrays)
  6. Caching: Cache popular queries

Key Takeaways for Interview

Technical Highlights

  1. Full-Stack: Designed and implemented both frontend and backend
  2. Scalable Architecture: Clustering enables sub-linear search time
  3. Cross-Platform: Single Flutter codebase for Web + Mobile
  4. Production-Ready: Session management, error handling, CORS, cleanup
  5. Mathematical Domain: Specialized knowledge of LaTeX, MathML, bit-vectors

Problem-Solving Skills

  1. Performance Optimization: Clustering reduces search space by ~93%
  2. Platform Adaptation: Different rendering strategies for Web vs Mobile
  3. Data Modeling: Designed 21-bit vector representation
  4. Error Handling: Graceful degradation, validation, fallbacks

Technologies Demonstrated

  • Backend: FastAPI, async Python, scikit-learn, numpy
  • Frontend: Flutter, Dart, WebView, HTTP
  • Data Processing: BeautifulSoup, lxml, latex2mathml
  • Algorithms: MiniBatchKMeans, Hamming distance, ANN search

Metrics to Mention

  • Search Time: 0.2-0.5 seconds average
  • Accuracy: ~90% relevant results in top 20
  • Scalability: Handles concurrent users with session management
  • Dataset: NTCIR-12 mathematical documents
  • Clusters: 14 optimal clusters for dataset size

Common Interview Questions - Quick Answers

Q: Explain your project in 30 seconds.

A: "I built a full-stack mathematical search engine that lets users search for math expressions using LaTeX. The backend uses FastAPI with a clustering-based algorithm to quickly find similar expressions from thousands of documents. I convert math to 21-bit vectors and use MiniBatchKMeans with Hamming distance for fast approximate search. The Flutter frontend works on both web and mobile, rendering MathML properly using platform-specific approaches."


Q: What was the biggest challenge?

A: "Rendering MathML across platforms. Web browsers handle MathJax naturally, but mobile required injecting JavaScript into WebView. I had to create platform-specific implementations while maintaining a single codebase. Also, optimizing search speed—clustering reduced search time from 2-3 seconds to under 0.5 seconds."


Q: What would you do differently?

A: "I'd use a proper database instead of pickle files for better scalability. Also, I'd implement more comprehensive testing—unit tests for bit-vector generation, integration tests for the search pipeline, and load testing for concurrent users. Finally, I'd add query caching for frequently searched expressions."


Q: How does this demonstrate your skills?

A: "It shows full-stack development (Flutter + FastAPI), algorithm design (clustering, bit-vectors), performance optimization (sub-linear search), cross-platform development (Web + Mobile), and production considerations (session management, error handling, CORS). It also demonstrates domain-specific knowledge in mathematical information retrieval."


Conclusion

This project demonstrates:

  • ✅ Full-stack development capabilities
  • ✅ Algorithm design and optimization
  • ✅ Cross-platform mobile development
  • ✅ RESTful API design
  • ✅ Machine learning application (clustering)
  • ✅ Production-ready code (error handling, session management)
  • ✅ Domain expertise (mathematical notation, LaTeX, MathML)

Be prepared to discuss:

  • Trade-offs in design decisions
  • Alternative approaches considered
  • Performance metrics and optimization
  • Future improvements and scalability
  • Testing strategies
  • Real-world deployment considerations

Good luck with your interview! 🚀