Last Updated: 2026-03-16
Branch: neural_net
Padly is a housing and roommate matching platform for students, interns, and early-career professionals. It connects users into roommate groups, then matches those groups to housing listings using intelligent algorithms.
Core differentiator: most platforms match individuals to listings. Padly matches groups to listings, ensuring roommate compatibility before housing search.
| Layer | Technology |
|---|---|
| Frontend | Next.js 15, React 19, Mantine UI, TanStack React Query |
| Backend | FastAPI (Python), Uvicorn ASGI |
| Database | PostgreSQL via Supabase |
| Auth | Supabase Auth (JWT) |
| ML (in-progress) | PyTorch, sentence-transformers, CLIP |
Run locally:
# Backend (from /backend)
.\venv\Scripts\activate
uvicorn app.main:app --reload # http://localhost:8000
# Frontend (from /frontend)
npm run dev # http://localhost:3000Padly/
├── backend/
│ └── app/
│ ├── main.py # FastAPI entry point
│ ├── models.py # Pydantic models
│ ├── routes/ # API endpoints
│ │ ├── auth.py
│ │ ├── users.py
│ │ ├── groups.py # Roommate groups (large file)
│ │ ├── listings.py
│ │ ├── preferences.py
│ │ ├── matches.py
│ │ ├── roommates.py
│ │ ├── stable_matching.py
│ │ └── admin.py
│ ├── services/
│ │ ├── stable_matching/
│ │ │ ├── feasible_pairs.py # Hard constraint filtering
│ │ │ ├── scoring.py # Soft preference scoring (0-100)
│ │ │ ├── deferred_acceptance.py # Gale-Shapley algorithm
│ │ │ ├── persistence.py
│ │ │ └── ai_scorer.py # [TODO] Hybrid AI+rule scorer
│ │ ├── user_group_matching.py # User-to-group compatibility
│ │ ├── lns_optimizer.py # LNS optimization
│ │ ├── group_preferences_aggregator.py
│ │ ├── group_rematching_service.py
│ │ ├── listing_categorizer.py # [TODO] 5-category labeling
│ │ └── data_parser.py
│ ├── ai/
│ │ ├── two_tower_baseline.py # TF baseline (exists)
│ │ ├── generate_renter_data.py # Synthetic data (exists)
│ │ ├── config.py # [TODO] Hyperparameters
│ │ ├── embeddings.py # [TODO] Text + image embedders
│ │ ├── feature_engineering.py # [TODO] Raw data → tensors
│ │ ├── model.py # [TODO] PyTorch TwoTowerModel
│ │ ├── training.py # [TODO] Training loop
│ │ ├── siamese.py # [TODO] Roommate compatibility
│ │ ├── inference.py # [TODO] ModelServer singleton
│ │ ├── heuristic_scorer.py # [TODO] Cold-start scorer
│ │ └── batch_embed.py # [TODO] Precompute embeddings
│ ├── dependencies/
│ │ ├── auth.py
│ │ └── supabase.py
│ └── db.py
├── frontend/
│ └── src/app/
│ ├── page.jsx # Landing page
│ ├── layout.jsx
│ ├── login/page.jsx
│ ├── signup/page.jsx
│ ├── account/page.jsx
│ ├── onboarding/page.jsx
│ ├── groups/
│ │ ├── page.jsx
│ │ ├── create/page.jsx
│ │ └── [id]/page.jsx + edit/
│ ├── listings/[id]/page.jsx
│ ├── matches/page.jsx
│ ├── preferences/page.jsx # Needs map picker
│ ├── invitations/page.jsx
│ ├── discover/page.jsx # [TODO] Swipe/discover UI
│ ├── components/
│ │ ├── Navigation.jsx
│ │ ├── ProtectedRoute.jsx
│ │ ├── OnboardingSwipe.jsx # [TODO]
│ │ ├── ListingCard.jsx # [TODO]
│ │ ├── SwipeControls.jsx # [TODO]
│ │ ├── SwipeFeedback.jsx # [TODO]
│ │ └── MapPicker.jsx # [TODO]
│ └── contexts/AuthContext.jsx
├── backend/migrations/
│ ├── 001_dynamic_group_sizing.sql # ✅ Applied
│ ├── 002_solo_user_groups.sql # ✅ Applied
│ ├── 003_expand_personal_preferences.sql # ✅ Applied (needs verification)
│ ├── 004_user_interactions.sql # [TODO] Swipe tracking
│ ├── 005_embedding_tables.sql # [TODO] Embedding cache
│ ├── 006_scoring_improvements.sql # [TODO] lat/lng + photo count
│ └── 007_listing_categories.sql # [TODO] category + onboarding_completed
└── run-dev.sh
User Preferences
↓
feasible_pairs.py ← Hard constraints: city, budget, bedrooms, date, lease
↓
scoring.py ← Soft scoring (0-100): bathrooms, furnished, utilities, deposit, house rules
↓
deferred_acceptance.py ← Gale-Shapley stable matching
↓
lns_optimizer.py ← LNS optimization (+13.8% quality in tests)
↓
persistence.py ← Save stable matches to DB
Test results (Oakland, Dec 2025): 23/24 groups matched, 1.37s execution time.
Replace static soft scoring with a Two-Tower Neural Network that learns from user swipe behavior. Add a Swipe/Discover UI to collect training data.
User Preferences + Swipe History
↓
feasible_pairs.py ← Hard constraints (unchanged)
↓
ai_scorer.py ← Hybrid: (ai_weight × AI score) + ((1-ai_weight) × rule score)
↓
deferred_acceptance.py ← Gale-Shapley (unchanged)
↓
lns_optimizer.py ← LNS (unchanged)
- User Tower: ~480-dim input → 256 → 128 → 64-dim embedding
- Item Tower: ~923-dim input → 256 → 128 → 64-dim embedding
- Output:
sigmoid(dot(user_emb, item_emb))→ 0-1 score - Training: Binary cross-entropy on (like=1, pass=0) swipe data
- Loss options:
binary_crossentropyorsoftmax
| Swipe count | AI weight | Behavior |
|---|---|---|
| < 20 | 0.0 | Pure rule-based |
| 20–50 | 0.3 | Mostly rules |
| 50–100 | 0.5 | Balanced |
| 100+ | 0.7 | Mostly AI |
- Compares two user profiles through the same network
- Small Euclidean distance = compatible roommates
- Training data from
group_memberstable (positive: same accepted group)
Hard constraints: target_city, target_state_province, budget_min, budget_max, required_bedrooms, move_in_date, target_lease_type, target_lease_duration_months
Soft preferences: target_bathrooms, target_furnished, target_utilities_included, target_deposit_amount, target_house_rules
Pending (migration 006): target_latitude, target_longitude
| Category | Rule |
|---|---|
budget |
price/person < $700 |
premium |
price/person > $1200 + 5+ amenities |
campus |
< 2km from nearest campus |
social |
downtown_score > 0.7 |
spacious |
3+ bedrooms OR 1000+ sqft |
Used for onboarding: new users swipe on 5 categorized listings before seeing main feed.
| Factor | Points |
|---|---|
| Bathrooms | 20 |
| Furnished | 20 |
| Utilities | 20 |
| Deposit | 20 |
| House rules | 20 |
| Tier | Factor | Points |
|---|---|---|
| 1 (High) | Location proximity | 25 |
| 1 (High) | House rules | 20 |
| 1 (High) | Price efficiency | 15 |
| 2 (Med) | Deposit | 15 |
| 2 (Med) | Listing quality | 10 |
| 2 (Med) | Date closeness | 5 |
| 3 (Low) | Bathrooms | 4 |
| 3 (Low) | Furnished | 3 |
| 3 (Low) | Utilities | 3 |
POST /api/auth/signup/signin/signoutGET /api/auth/me
GET/PUT /api/users/{user_id}
GET /api/roommate-groups(with filters)POST /api/roommate-groups(create)POST /api/roommate-groups/{id}/request-joinPOST /api/roommate-groups/{id}/accept-request/{user_id}DELETE /api/roommate-groups/{id}/leave
GET /api/matches/groups— compatible groups for userPOST /api/stable-matches/run— run matching algorithmGET /api/stable-matches/active
GET/PUT /api/preferences/{user_id}
POST /api/interactions— log swipeGET /api/interactions/{user_id}/historyGET /api/interactions/{user_id}/statsDELETE /api/interactions/{interaction_id}
GET /api/discover/{user_id}— ranked listing feedGET /api/discover/{user_id}/onboarding— 5 categorized onboarding listingsPOST /api/discover/{user_id}/onboarding/complete
- Auth system (signup, login, JWT)
- User profiles and account management
- Roommate groups (create, join, request, approve, leave)
- Housing listings (browse, view, manage)
- Preferences system (all 13 fields synced frontend ↔ backend)
- Gale-Shapley stable matching
- LNS optimization
- User-to-group compatibility scoring
- TensorFlow two-tower baseline (
two_tower_baseline.py) - Synthetic data generator (
generate_renter_data.py)
Backend:
- Migration 004:
user_interactionstable - Migration 005:
user_embeddings+listing_embeddingstables - Migration 006:
target_latitude/longitude,photo_count,host_verified - Migration 007:
listings.category,users.onboarding_completed -
routes/interactions.py— swipe logging API -
services/listing_categorizer.py— 5-category rule-based labeling -
services/stable_matching/scoring.py— rewrite with 9-factor system -
services/stable_matching/ai_scorer.py— hybrid scorer -
ai/config.py— hyperparameters -
ai/embeddings.py— TextEmbedder (MiniLM) + ImageEmbedder (CLIP) -
ai/feature_engineering.py— raw data → tensors -
ai/model.py— PyTorch TwoTowerModel -
ai/training.py— training loop + InteractionDataset -
ai/siamese.py— SiameseNetwork -
ai/inference.py— ModelServer singleton -
ai/heuristic_scorer.py— cold-start scoring wrapper -
ai/batch_embed.py— batch embedding pipeline - Wire
GET /api/discover/{user_id}with hybrid scoring - Load model on FastAPI startup via
lifespan
Frontend:
-
discover/page.jsx— swipe/discover main page -
components/OnboardingSwipe.jsx— 5-card onboarding flow -
components/ListingCard.jsx— swipeable listing card -
components/SwipeControls.jsx— like/pass/save buttons -
components/SwipeFeedback.jsx— swipe animation overlay -
components/MapPicker.jsx— map pin for target location - Update
preferences/page.jsx— add map picker - Swipe history section on profile
- Supabase project — credentials in
backend/app/.env(not committed) - Migration 003 is written but needs Supabase SQL Editor execution to take effect
- Migrations 004–007 are planned but not yet written
| File | Contents |
|---|---|
README.md |
Setup instructions, tech stack, API overview |
ML_ROADMAP.md |
Full ML integration plan (Phases 0–7) |
SPRINT_PLAN.md |
2-week sprint plan (Feb 10–24, 2026), 4-member team roles |
SCORING_IMPROVEMENTS.md |
Proposed 9-factor scoring rewrite with code |
TWO_TOWER_EXPLAINER.md |
How the Two-Tower model works, with full example |
DATASET_REQUIREMENTS.md |
Dataset schemas for ML training (A–E) |
ML_addition.md |
High-level AI implementation guide |
PREFERENCES_UPDATE_COMPLETE.md |
Preferences sync status (frontend ↔ backend) |
backend/MATCHING_ALGORITHM.md |
Full matching algorithm documentation |
SOURCE_OF_TRUTH.md |
This file |