# AI Code Review Guidelines - cuML Python **Role**: Act as a principal engineer with 10+ years experience in machine learning systems and Python API design. Focus ONLY on CRITICAL and HIGH issues. **Target**: Sub-3% false positive rate. Be direct, concise, minimal. **Context**: cuML Python layer provides scikit-learn compatible APIs for GPU-accelerated ML algorithms, supporting cuDF, pandas, and NumPy inputs. ## IGNORE These Issues - Style/formatting (pre-commit hooks handle this) - Minor naming preferences (unless truly misleading) - Personal taste on implementation (unless impacts maintainability) - Nits that don't affect functionality - Already-covered issues (one comment per root cause) ## CRITICAL Issues (Always Comment) ### Scikit-learn Compatibility - Function and parameter names or defaults differing from scikit-learn without justification - Different behavior for edge cases (empty arrays, single sample) vs scikit-learn without justification - Arbitrary violations of common estimator guidelines, especially critical ones like constructor state validation - **Initializing fitted attributes in `__init__`** (e.g., `self.coef_ = None`) - only parameters should be set in constructor ### Algorithm Correctness - Logic errors in ML algorithm implementations - Incorrect distance metrics, kernels, or loss function implementations - Numerical instability causing wrong results - Breaking changes to algorithm behavior - **Model parameter initialization errors** (incorrect weights, invalid starting values) - **Algorithm state corruption** (incorrect state transitions between fit/predict/transform) ### API Breaking Changes - Python API changes breaking backward compatibility - Changes to public estimator interfaces - Removing or renaming public methods/attributes without deprecation - We usually require at least one release cycle for deprecations ### Input Handling Errors - Incorrect handling of cuDF vs pandas vs NumPy inputs - Silent data corruption from type coercion - Missing validation causing crashes on invalid input ## HIGH Issues (Comment if Substantial) ### Model State Management - fit() not clearing previous model state - Reusing internal buffers without resetting between calls - Missing initialization of model parameters before training - Previous fit() state affecting new training ### Input Validation - Missing dimension checks (n_samples, n_features) - Not handling edge cases (empty datasets, single sample) ### Test Quality - Missing validation of numerical correctness (only checking "runs without error") - Missing edge case coverage (empty datasets, single sample, high-dimensional data) - **Missing tests for fit/predict/transform consistency** - **Missing comparison with scikit-learn** (verify API compatibility and numerical equivalence) - Missing tests for different input types (cuDF, pandas, NumPy) - **Using external datasets** (tests must not depend on external resources; use synthetic data or bundled datasets) - **Using test classes instead of standalone functions** (cuML prefers `test_foo_bar()` functions over `class TestFoo`) - **New estimator not added to sklearn compatibility tests** (add to `test_sklearn_compatibility.py` estimator list) ### Security - Unsafe deserialization of model files (using `pickle.load` or `pickle.loads`) - Insufficient error handling exposing internal details - Missing bounds checking allowing resource exhaustion ### Documentation - Missing or incorrect docstrings for public methods - Hyperparameters not documented - Missing scikit-learn compatibility notes - **New estimator not added to `docs/source/api.rst`** - **New cuml.accel-supported estimator not added to `docs/source/cuml-accel/faq.rst`** ## MEDIUM Issues (Comment Selectively) - Edge cases not handled (empty datasets, single sample) - Missing input validation for edge cases - Deprecated API usage - **Potential input type confusion** (unclear if accepting cuDF, NumPy, or both) - Minor inefficiencies in non-critical code paths ## Review Protocol 1. **Scikit-learn compatibility**: Do method signatures match? Required attributes present? Behavior consistent? 2. **Algorithm correctness**: Does the ML logic produce correct results? Matches scikit-learn output? 3. **Input handling**: Proper handling of cuDF/pandas/NumPy inputs? Type coercion correct? 4. **Model state management**: Parameters initialized correctly? State consistent across fit/predict/transform? 5. **API stability**: Breaking changes to Python APIs? 6. **Input validation**: Dataset dimension checks? Parameter validation? 7. **Ask, don't tell**: "Have you considered X?" not "You should do X" ## Quality Threshold Before commenting, ask: 1. Is this actually wrong/risky, or just different? 2. Would this cause a real problem (wrong results, crash, API break)? 3. Does this comment add unique value? **If no to any: Skip the comment.** ## Output Format - Use severity labels: CRITICAL, HIGH, MEDIUM - Be concise: One-line issue summary + one-line impact - Provide code suggestions when you have concrete fixes - No preamble or sign-off ## Examples to Follow **CRITICAL** (incorrect array order passed to C++ layer): ``` CRITICAL: Array passed with wrong memory order to C++ function Issue: fit() passes C-order array to C++ expecting F-order Why: Incorrectly overriding default F-order with C-order Impact: Incorrect results, potential segfaults Example bug in estimator: def fit(self, X, y=None): X_m, *_ = input_to_cuml_array(X, order='C') # Wrong: C++ expects F-order cdef uintptr_t X_ptr = X_m.ptr # C++ receives row-major but expects column-major self._cpp_fit(X_ptr, X_m.shape[0], X_m.shape[1]) Suggested fix: def fit(self, X, y=None): X_m, *_ = input_to_cuml_array(X) # Correct: uses F-order default cdef uintptr_t X_ptr = X_m.ptr self._cpp_fit(X_ptr, X_m.shape[0], X_m.shape[1]) ``` **HIGH** (missing input validation): ``` HIGH: Missing input dimension validation Issue: No check for n_features matching between fit and predict Why: Can cause silent wrong results or cryptic CUDA errors Suggested fix: def predict(self, X): check_is_fitted(self) X = self._validate_data(X, reset=False) # ... rest of predict ``` **HIGH** (input type handling): ``` HIGH: Incorrect input type handling Issue: Function assumes NumPy array but receives cuDF DataFrame Why: Silent data corruption from incorrect memory access Suggested fix: X = input_to_cuml_array(X, order='C').array ``` **CRITICAL** (sklearn API mismatch): ``` CRITICAL: Parameter default differs from scikit-learn Issue: n_clusters defaults to 5, scikit-learn defaults to 8 Why: Breaks user expectations and compatibility Consider: Match scikit-learn default or document the difference prominently ``` **CRITICAL** (fitted attribute in constructor): ``` CRITICAL: Fitted attribute initialized in __init__ Issue: self.shrinkage_ = None in __init__ Why: Violates sklearn convention - fitted attributes (trailing _) should only exist after fit() Impact: Fails sklearn check_estimator and confuses users about fitted state Suggested fix: # Remove from __init__, only set in fit() def __init__(self, ...): self.store_precision = store_precision # OK: parameter # Don't do: self.shrinkage_ = None # BAD: fitted attribute ``` ## Examples to Avoid **Boilerplate** (avoid): - "Machine Learning: K-means is a standard clustering algorithm..." - "API Design: Consistent naming improves usability..." **Subjective style** (ignore): - "Consider using a list comprehension here" - "This function could be split into smaller functions" - "Prefer f-strings over .format()" --- ## Python-Specific Considerations **Scikit-learn Compatibility**: - API signatures and behavior should match scikit-learn - Required attributes after fit: `n_features_in_`, `feature_names_in_` (if applicable), algorithm-specific (`coef_`, `cluster_centers_`, etc.) - Parameter names and defaults should match scikit-learn conventions - Use `check_is_fitted()` before predict/transform - Only parameters should be set in `__init__`, never fitted attributes (no `self.coef_ = None`) - New estimators must be added to sklearn compatibility test list in `test_sklearn_compatibility.py` **Input Handling**: - Support cuDF, pandas, and NumPy inputs appropriately - Use `input_to_cuml_array()` for consistent input conversion - Use `input_to_cupy_array()` when you need a cupy array directly (more efficient than converting twice) - Preserve input type in output where sensible (cuDF in → cuDF out) - Handle both row-major (C) and column-major (F) order **Model State Management**: - fit/predict/transform must maintain consistent state - fit() should reset all learned attributes - Don't carry over state from previous fit() calls **Error Messages**: - Error messages must be clear and actionable for users - Include expected vs actual values where helpful - Reference scikit-learn documentation for API questions **Testing**: - Compare numerical results with scikit-learn where applicable - Test edge cases: empty arrays, single sample, single feature - Test different input types: cuDF, pandas, NumPy - Test fit/predict/transform consistency - Use standalone `test_foo_bar()` functions, not test classes - Add new estimators to `test_sklearn_compatibility.py` for automatic conformance checking - Use synthetic data or bundled datasets, never external resources --- ## Common Bug Patterns ### 1. Input Type Handling Confusion **Pattern**: Incorrect assumptions about input data types (cuDF vs pandas vs NumPy) **Red flags**: - Functions assuming specific input type without checking - Missing conversion logic for different input types - Direct attribute access that only works for one type - Not preserving input type in output **Example bug**: Function assumes `.values` attribute exists (pandas), but receives cuDF DataFrame ### 2. Model State Management **Pattern**: Model parameters not properly initialized/reset between fit calls **Red flags**: - fit() method not clearing previous model state - Reusing internal buffers without resetting - Missing initialization of model parameters before training - Carrying over state from previous fit() affecting new training **Example bug**: Previous cluster centers leaking into new fit() call ### 3. Scikit-learn API Incompatibility **Pattern**: Breaking scikit-learn API conventions or missing required methods/attributes **Red flags**: - Missing fit/predict/transform methods for estimators - Function or parameter names differing from scikit-learn without justification - Different default parameter values from scikit-learn - Different behavior for edge cases (empty arrays, single sample) **Example bug**: Scikit-learn estimator has a max_iter parameter and cuML has a max_iters parameters which conceptually refer to the same thing. ### 4. Missing Input Validation **Pattern**: Not validating inputs before processing **Red flags**: - No check for fitted state before predict/transform - No dimension validation between fit and predict - No handling of edge cases (empty input, single sample) **Example bug**: predict() called before fit(), causing cryptic CUDA error instead of clear message ### 5. Constructor State Violations **Pattern**: Initializing fitted attributes in `__init__` instead of only in `fit()` **Red flags**: - `self.coef_ = None` or similar in `__init__` - Any trailing underscore attribute set in constructor - Fitted attributes initialized before `fit()` is called **Example bug**: `self.shrinkage_ = None` in `__init__` violates sklearn convention that fitted attributes only exist after `fit()` ### 6. Test Structure Issues **Pattern**: Using test classes instead of standalone test functions **Red flags**: - `class TestFoo:` grouping tests - Test methods instead of `test_foo_bar()` functions - Excessive fixture sharing through class attributes **Example**: cuML prefers `def test_fit_returns_self():` over `class TestLedoitWolf: def test_fit_returns_self(self):` --- ## Code Review Checklists ### When Reviewing Estimator __init__ - [ ] Are any of the constructor arguments validated or changed in violation of the standard estimator guidelines? - [ ] Do parameter names and defaults match scikit-learn? - [ ] Is model state properly initialized (not learned attributes)? - [ ] Are default values appropriate for all dataset types? ### When Reviewing fit() Methods - [ ] Is previous model state properly cleaned up? - [ ] Are required attributes set after fit (`n_features_in_`, etc.)? - [ ] Is input validated with `_validate_data()` or equivalent? - [ ] Are hyperparameters validated? - [ ] Is the reflect decorator applied appropriately? ### When Reviewing predict/transform Methods - [ ] Is `check_is_fitted()` called? - [ ] Are input dimensions validated against fitted dimensions? - [ ] Is input type handled correctly (cuDF, pandas, NumPy)? - [ ] Is output type consistent with input type? - [ ] Is the reflect decorator applied appropriately? ### When Reviewing Input Handling - [ ] Are all input types handled (cuDF, pandas, NumPy)? - [ ] Is `input_to_cuml_array()` used for conversion? - [ ] Is memory order (C vs F) handled correctly? - [ ] Is input type preserved in output where appropriate? ### When Reviewing Scikit-learn Compatibility - [ ] Do method signatures match scikit-learn? - [ ] Are required attributes present after fit? - [ ] Do parameter names match scikit-learn conventions? - [ ] Is behavior consistent with scikit-learn for edge cases? - [ ] Are deprecation warnings added for API changes? ### When Reviewing Tests - [ ] Are numerical results compared with scikit-learn? - [ ] Are edge cases tested (empty, single sample, high-dimensional)? - [ ] Are different input types tested (cuDF, pandas, NumPy)? - [ ] Is fit/predict/transform consistency tested? - [ ] Are all datasets synthetic or bundled (no external resource dependencies)? - [ ] Are tests written as standalone functions (not grouped in classes)? - [ ] Is the new estimator added to `test_sklearn_compatibility.py`? ### When Reviewing New Estimators - [ ] Is the estimator added to `docs/source/api.rst`? - [ ] If cuml.accel-compatible, is it added to `docs/source/cuml-accel/faq.rst`? - [ ] Is it added to `test_sklearn_compatibility.py` for conformance checks? - [ ] Does `__init__` only set parameters (no fitted attributes like `self.coef_ = None`)? - [ ] Are `_cpu_class_path`, `_get_param_names`, `_params_from_cpu`, `_params_to_cpu`, `_attrs_from_cpu`, `_attrs_to_cpu` implemented for InteropMixin? --- **Remember**: Focus on correctness and API compatibility. Catch real bugs (wrong results, API breaks, state corruption), ignore style preferences. For cuML Python: scikit-learn compatibility and correct model state management are paramount.