Skip to content

Feature Request: Multi-Dimensional Scoring, Custom Group Sizes, and Advanced Categorical Constraints#34

Draft
genes3e7 wants to merge 104 commits intomainfrom
Feature-Request-Multi-Dimensional-Scoring,-Custom-Group-Sizes,-and-Advanced-Categorical-Constraints
Draft

Feature Request: Multi-Dimensional Scoring, Custom Group Sizes, and Advanced Categorical Constraints#34
genes3e7 wants to merge 104 commits intomainfrom
Feature-Request-Multi-Dimensional-Scoring,-Custom-Group-Sizes,-and-Advanced-Categorical-Constraints

Conversation

@genes3e7
Copy link
Copy Markdown
Owner

No description provided.

* Support custom group capacities in UI and solver

Replace fixed/balanced group sizing with explicit per-group capacity support. Updated solver and solver_interface to accept group_capacities: list[int], validate that capacities sum to participants, and use capacities in capacity and scoring constraints (removed previous balanced-size logic). UI (steps.py) adds per-group capacity inputs, validation/feedback, disables solving when capacities mismatch, and passes capacities via session_state to the solver. Misc: minor code cleanups and header comments.

* Switch solver API to group_capacities

Migrate solver usage from a num_groups parameter to an explicit group_capacities list so callers can specify unequal group sizes. Compute balanced capacities in the CLI (group_balancer.py) from num_groups input and pass group_capacities to solver; also rename participant count to num_people for clarity. Update UI (src/ui/steps.py) to clean up stale capacity keys when num_groups is reduced and fix capacity column indexing when rendering inputs. Update tests (tests/test_solver.py) to use group_capacities arguments and adjust test descriptions accordingly.

* Assert error message for empty group capacities

Update tests to expect a specific ValueError message when group_capacities is empty. Added pytest.raises(..., match=...) checks in test_solver_empty_input and test_solver_positive_participants_zero_groups to ensure the exception text mentions the capacity requirement. Also adjusted formatting of a solver.solve_with_ortools call in test_solver_zero_participants_positive_groups for improved readability.

Separately, linted files

* Enforce non-negative and capacity-limited groups

Add validation to reject negative values in group_capacities and update per-group star constraints to respect configured capacities. In run_optimization, compute upper_g and lower_g as the min of calculated bounds and group_capacities[g] so each group's assignments do not exceed its capacity, and raise ValueError if any capacity is negative. Also remove an unnecessary top-of-file path comment.

* Refactor solver to support arbitrary group capacities and improve star distribution logic.

* Fix: optimize CP-SAT solver search tree and star distribution bounds

Resolves critical performance regressions and infeasibility states introduced by custom group capacities. The solver previously suffered from thread starvation, combinatorial explosion during the proof-of-optimality phase, and mathematically contradictory constraints.

Key modifications:

Proportional Star Bounds: Replaced flat global min/max bounds with capacity-weighted bounds (len(stars) * group_capacities[g] / num_people). This strictly prevents the solver from attempting to assign more stars to a group than its total capacity allows.

Score Symmetry Breaking: Implemented g_sums[g1] <= g_sums[g2] for groups with identical capacities. This slices the redundant search tree by mathematically enforcing a single valid ordering for mirrored configurations, drastically accelerating the proof-of-optimality phase.

Objective Lower-Bound Integrity: Restored the exact linear multiplier (actual == g_sum * num_people) for the deviation target. This ensures the optimal lower bound remains exactly 0, preventing the solver from exhaustively searching for mathematically impossible fraction-based zero-gap states.

Thread Throttling: Added a minimum update interval (0.25s for Streamlit UI, 0.1s for CLI) to the solver callbacks. This prevents rapid micro-optimizations from overwhelming the main Python thread and freezing the DOM/console output.

* Refactor solver architecture for transparency, maintainability, and user-led optimization

This commit completes the architectural overhaul of the solver's execution pipeline, focusing on operational transparency and centralized configuration. It addresses the "round-robin" feedback loop regarding solver performance and status reporting.

Key Changes:

Architectural Refactoring: Extracted the core CP-SAT model building logic into a shared build_partition_model helper in src/core/solver.py. This ensures that the mathematical constraints (proportional stars, symmetry breaking, and scoring) remain identical across the CLI and Streamlit interfaces.

Solver Transparency: Implemented explicit tracking of the CP-SAT solver's return status. The UI now distinguishes between a "Proven Optimal" solution and a "Best Found" solution (Feasible status) resulting from a timeout.

User-Controllable Performance: Added a "Max Calculation Time" slider to Step 2, allowing users to define their own patience threshold. The solver now returns the exact elapsed time to the results page to provide better feedback on complexity.

Centralized Constants: Migrated all "magic numbers" and configuration limits—including UI timeout ranges, score scaling factors, and the server-side hard timeout cap—into src/core/config.py for simplified developer maintenance.

UI/UX Polish: Updated the Step 3 results header with dynamic alert banners that state the exact time taken to reach the displayed grouping and the mathematical certainty (Optimal vs. Feasible) of the result.

Stability Fixes: Corrected a logic error in the objective function where pre-rounding targets in Python prevented the solver from proving optimality, and implemented score-based symmetry breaking to speed up the convergence of identically-sized groups.

* Reduce solver timeout and clamp UI timeouts

Lower SOLVER_TIMEOUT from 300s to 120s to tighten the server hard cap. Compute UI_TIMEOUT_MAX as max(UI_TIMEOUT_MIN, SOLVER_TIMEOUT) so it never falls below the UI minimum, and update UI_TIMEOUT_DEFAULT to enforce the minimum while capping at 60s and respecting the new max. Also remove an extraneous blank line.
This commit refactors the core engine and user interface to transition from a single-score optimization model to a dynamic, multi-dimensional scoring system. This allows for more complex group balancing across various continuous variables (e.g., Score1, Score2, ...) using user-defined weights.Key Changes:Dynamic Data Schema: Deprecated the hardcoded Score column. The system now automatically identifies all input columns matching the SCORE_PREFIX (default: "Score") and treats them as continuous optimization dimensions.Multi-Objective Solver: Updated the CP-SAT objective function to minimize a weighted sum of absolute deviations across all detected score dimensions. This includes a new weighting mechanism where users can specify the relative importance ($W_n$) of each score in the UI.Architectural Refactoring:Centralized model construction logic into build_partition_model to support multi-score inputs across CLI and Web interfaces.Refactored group aggregation logic in group_helpers.py to calculate averages and sums for all score dimensions simultaneously.UI/UX Enhancements:Step 2: Dynamically generates numeric inputs for score weights based on detected columns in the participant data.Step 3: Updated Result Cards and the Assignments Editor to display and track statistics for all score dimensions.Excel Export Evolution: Overhauled exporter.py to generate a dynamic matrix view that adjusts its width and header structure based on the number of score dimensions used.Test Suite Synchronization: Updated all unit tests across test_config.py, test_data_loader.py, test_exporter.py, and test_solver.py to align with the new multi-dimensional API and variable-weighted solver signatures.Maintainability Fixes: Implemented defensive clamping for UI timeout constants in config.py to prevent validation errors if the hard timeout cap is set lower than the UI minimum.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 03abaa81-5d9a-4e64-83af-158c323b12eb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Feature-Request-Multi-Dimensional-Scoring,-Custom-Group-Sizes,-and-Advanced-Categorical-Constraints

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@genes3e7 genes3e7 moved this from Backlog to In progress in Epic: Advanced Grouping Engine (V6) Apr 16, 2026
genes3e7 and others added 19 commits April 16, 2026 16:35
Detect score columns across all participants and use safe lookups to avoid KeyErrors (group_balancer.py). Harden CP-SAT model bounds to support negative and zero-sum score dimensions, compute a dynamic diff bound, and use safer name lookups (src/core/solver.py). Tighten UI behavior and formatting: warn when no score columns, use 2-decimal formatting for numeric columns, and avoid rendering inputs when empty (src/ui/steps.py, src/ui/results_renderer.py). Upgrade Excel exporter with dynamic column labeling, computed header size, and accurate dataset-level stats derived from the result DataFrame (src/utils/exporter.py). Update and add tests to reflect SCORE_PREFIX expectation and multi-dimensional, weighted solver behavior; include small type/format tweaks. Also add a binary errors.err file.
* chore(deps): bump the dependencies group with 3 updates

Bumps the dependencies group with 3 updates: [numpy](https://github.com/numpy/numpy), [pandas](https://github.com/pandas-dev/pandas) and [ruff](https://github.com/astral-sh/ruff).


Updates `numpy` from 2.2.6 to 2.4.4
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](numpy/numpy@v2.2.6...v2.4.4)

Updates `pandas` from 2.3.3 to 3.0.2
- [Release notes](https://github.com/pandas-dev/pandas/releases)
- [Commits](pandas-dev/pandas@v2.3.3...v3.0.2)

Updates `ruff` from 0.15.10 to 0.15.11
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.10...0.15.11)

---
updated-dependencies:
- dependency-name: numpy
  dependency-version: 2.4.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: pandas
  dependency-version: 3.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: dependencies
- dependency-name: ruff
  dependency-version: 0.15.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore:  update supported python versions (3.10-3.14); update requirements.txt; fix linting/formatting

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Introduce advanced constraint support (Groupers/Separators) and solver options across core, UI and tests. Added COL_GROUPER and COL_SEPARATOR config keys and increased SOLVER_TIMEOUT; data loader and session init now inject missing tag columns. Solver was refactored to support opt_mode (Simple/Advanced) and conflict_priority, implement pigeonhole spreading for Separators, cohesion penalties to keep Groupers together, improved star handling, and cleaner objective construction; SolutionPrinter and solver parameter usage were streamlined. Streamlit interface and step UI updated with advanced solver controls, file import/tag handling, session defaults, and safer result rendering. Expanded tests to cover Simple mode, pigeonhole/separator behavior, cohesion, and tag conflict resolution; adjusted existing tests for new signatures and behaviors.
Treat Groupers/Separators as character-tokenized tags (ignore commas/spaces) and update conflict/cohesion logic accordingly. Inform users in the UI about the new tagging model, improve file upload handling and data validation (warnings for empty names/invalid scores), refine solver configuration controls and error handling, and adjust result editor behavior and metrics display. Reduce global SOLVER_TIMEOUT from 3600s to 600s. Update unit tests to reflect character tokenization and add tests for comma handling and character-based grouping. Miscellaneous cleanup: remove stray prints, minor comment tweaks, and normalize solver_interface call arguments.
Remove the legacy 'star' (ADVANTAGE_CHAR) feature and related distribution logic across the codebase. Deleted ADVANTAGE_CHAR and all respect_stars parameters and constraints from the solver and its callers, removed star counting from group aggregation and tests, and removed star-related test cases. UI updates: revised instructions to treat every character in Groupers/Separators as its own tag (commas/spaces ignored), updated sample manual data to use single-character tags, and added an "Add Score Column" button. Fixed results card column indexing for displayed averages. Misc: cleaned up an unused import, adjusted internal comment section numbering, and updated tests to match the simplified behavior.
Multiple improvements across data loading, solver logic, UI navigation, and tests:

- src/core/data_loader.py: Preserve original score series, coerce to numeric, and emit warnings for missing or non-numeric scores before filling with zeros.

- src/core/solver.py: Make conflict-priority checks exact (==) and simplify optimization-mode comparison; tighten weighted-difference variable bounds to avoid excessive ranges by computing a safe bound using weight multiplier.

- src/ui/session_manager.py: Clamp navigation input to valid steps (1-3) before rerunning.

- src/ui/steps.py: Robustly generate new score column names by computing the max numeric suffix to avoid collisions; simplify radio options using concise keys with formatters for labels; add has_scores guard and warning, and disable the "Generate Groupings" button unless capacities and scores are valid.

- tests/test_solver.py: Update solver tests to match the refined conflict-resolution and capacity logic; increase participant/capacity scenarios and strengthen assertions to verify group placement for both Groupers and Separators cases; add helper checks for grouping expectations.

These changes improve input robustness, prevent naming collisions, constrain solver variables safely, and make the UI and tests consistent with the updated behavior.
Replace the string sentinel "_SIMPLE_TOTAL_" with an object sentinel to avoid accidental string comparisons and to prevent that token from appearing in CP-SAT variable names. Derive a col_name_str for all score columns (including the simple total) and use it when naming model variables, and only apply the ordering constraint to the first non-skipped score column. Update UI table formatting to include the "Sum" column. Adjust tests to reflect separator and capacity changes (use a 3/1 split and empty-string separators) and assert separator spread counts instead of fixed group sizes.
Solver: normalize None/NaN values for grouper/separator before tokenization; compute max_total_w_diff during scoring and derive a dynamic base_cohesion_penalty (replacing the fixed 10**9) so cohesion penalties are scaled relative to score contributions; move cohesion penalty construction after scoring and adjust capacity penalty scaling. UI - results_renderer: include constraint tag columns (grouper/separator) in group card member table and ensure score columns formatting; add header metrics comment. UI - steps: improve numeric coercion by only counting non-empty invalid values, fill invalid numerics with 0, coerce categorical constraint columns to strings (empty for NaN), and add stable keys to weight inputs. These changes improve robustness to sparse input data and make cohesion objective weight-safe and UI displays more complete.
…nsional-Scoring

Implement dynamic data schema and multi-dimensional weighted scoring
- Added module and function-level docstrings to build.py and all __init__.py files

- Standardized documentation across all core, ui, and util modules

- Cleaned up annotations and ensured professional comment standards
- Softened 'proven optimal' claim to 'high-quality mathematically optimized' to reflect feasible solutions during timeouts.
…ation-readme.md

docs: enhance project documentation and standardize python docstrings
- Migrated CI to uv and updated requirements.
- Implemented core solver architecture (Builder, Strategy, TagProcessor).
- Hardened security with path validation, size limits, and CP-SAT overflow protection.
- Decoupled UI logic into Service layers and removed unsafe_allow_html.
- Modernized Streamlit parameters (use_container_width -> width='stretch').
- Achieved 100% Google-style documentation compliance.
- Expanded test suite to 77 tests with 94% functional coverage.
- Restored and verified original UI look and feel.
…-Sizes,-and-Advanced-Categorical-Constraints' into Professional-standards-refactor
genes3e7 and others added 30 commits April 26, 2026 04:06
…ew feedback

Phase 1: CI hardening, SHA pinning, and bot-actor guards.
Phase 2: Solver logic RUF046 fixes and missing-aware participant models.
Phase 3: Stabilized Pre-CI pipeline with sequential pytest and output streaming.
Phase 4: Hermeticized test suite with better mocking and dynamic scaling.
Phase 1: Hardened CI workflow skip logic and stabilized pre-CI tool exports/validation.
Phase 2: Optimized UI performance with Excel memoization and explicitly initialized solver symmetry.
Phase 3: Synchronized GEMINI.md standards with refined caching and testing insights.
Phase 4: Hermeticized UI tests to handle Streamlit cache serialization.
1. CI: Simplified version range determination by removing stale 3.15-dev filter.
2. UI: Optimized cache hashing by excluding the DataFrame from auto-hashing.
3. Tools: Hardened version validation with exact equality checks.
4. Tests: Hermeticized edge tests by removing dead code mocks.
1. UI: Fixed solver error swallowing by prioritizing error rendering over the missing-result guard.
2. Tools: Implemented logical version range fallback (3.10-3.14) for inverted min/max inputs.
3. Tools: Optimized directory cleanup with efficient in-place os.walk pruning.
4. Infrastructure: Simplified CI version range determination logic.
1. Removed ignored encoding argument from subprocess.run. 2. Tightened UI cache key to a tuple to prevent hash collisions.
…tants

1. GEMINI.md: Reframe PowerShell as maintainer-local preference. 2. tools/pre_ci.py: Define DEFAULT_MIN_PY and DEFAULT_MAX_PY constants.
1. tools/pre_ci.py: Updated execute() docstring to match implemented execution sequence.
refactor: professional standards refactor & uv migration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

Feature Request: Multi-Dimensional Scoring, Custom Group Sizes, and Advanced Categorical Constraints

1 participant