|
| 1 | +<!-- a86255fd-e559-425e-82e2-e2ad35c50047 f5adc961-97fa-4a2f-b1f7-c67734af5cd0 --> |
| 2 | +# Add Fixed Effects Support to feglm (Gaussian Family) |
| 3 | + |
| 4 | +## Current State |
| 5 | + |
| 6 | +The `Feglm` class currently raises `NotImplementedError` at line 99-100 when fixed effects are present. Additionally, `Feglm` hardcodes the numba demeaning backend instead of using the configurable backend system that `feols` uses. |
| 7 | + |
| 8 | +**Key differences from feols:** |
| 9 | + |
| 10 | +- `Feglm` imports `demean` directly from `demean_.py` (line 11) - this is the numba backend |
| 11 | +- `Feglm` calls this hardcoded `demean` function in `residualize()` (line 317) |
| 12 | +- `Feols` uses a `demeaner_backend` parameter and selects the demean function from the `BACKENDS` dictionary |
| 13 | +- `Fepois` tries to pass `demeaner_backend` to `Feglm.__init__`, but `Feglm` doesn't accept it |
| 14 | + |
| 15 | +## Implementation Steps |
| 16 | + |
| 17 | +### 1. Update Feglm to Use Backend Infrastructure |
| 18 | + |
| 19 | +Modify `pyfixest/estimation/feglm_.py`: |
| 20 | + |
| 21 | +**a) Update imports** (line 11): |
| 22 | + |
| 23 | +- Remove: `from pyfixest.estimation.demean_ import demean` |
| 24 | +- Add: `from pyfixest.estimation.backends import BACKENDS` |
| 25 | +- Add: `from pyfixest.estimation.literals import DemeanerBackendOptions` |
| 26 | + |
| 27 | +**b) Add `demeaner_backend` parameter to `__init__`** (line 21): |
| 28 | + |
| 29 | +- Add `demeaner_backend: DemeanerBackendOptions = "numba"` parameter |
| 30 | +- Store as `self._demeaner_backend = demeaner_backend` |
| 31 | +- Set `self._demean_func = BACKENDS[demeaner_backend]["demean"]` (similar to how `Feols` does it at line 323-327 of `feols_.py`) |
| 32 | + |
| 33 | +**c) Update `residualize()` method** (line 317): |
| 34 | + |
| 35 | +- Replace `demean(...)` with `self._demean_func(...)` |
| 36 | + |
| 37 | +### 2. Remove Fixed Effects Restriction |
| 38 | + |
| 39 | +In `pyfixest/estimation/feglm_.py`: |
| 40 | + |
| 41 | +- Delete lines 99-100 that raise `NotImplementedError` for fixed effects |
| 42 | + |
| 43 | +### 3. Update Fegaussian Constructor |
| 44 | + |
| 45 | +Modify `pyfixest/estimation/fegaussian_.py`: |
| 46 | + |
| 47 | +- Add `demeaner_backend: DemeanerBackendOptions = "numba"` parameter to `__init__` (around line 29) |
| 48 | +- Pass it to the parent `Feglm.__init__` call |
| 49 | + |
| 50 | +### 4. Test the Implementation |
| 51 | + |
| 52 | +**a) Enable skipped test in `test_feols_feglm_internally.py`:** |
| 53 | + |
| 54 | +- Remove the `@pytest.mark.skip` decorator on line 60 |
| 55 | + |
| 56 | +**b) Add R comparison tests in `test_vs_fixest.py`:** |
| 57 | + |
| 58 | +- Test formulas: `"Y ~ X1 | f1"`, `"Y ~ X1 | f1 + f2"`, `"Y ~ X1 + X2 | f2"` |
| 59 | +- Compare: coefficients, standard errors, residuals against R's `fixest::feglm(family="gaussian")` |
| 60 | + |
| 61 | +### 5. Validation |
| 62 | + |
| 63 | +Ensure the implementation correctly handles: |
| 64 | + |
| 65 | +- Backend selection (numba, rust, jax) works correctly |
| 66 | +- Single and multiple fixed effects |
| 67 | +- Proper weight handling in demeaning at each IRLS iteration |
| 68 | +- Convergence (should still converge in 1 iteration for Gaussian) |
| 69 | + |
| 70 | +## Key Files to Modify |
| 71 | + |
| 72 | +- `pyfixest/estimation/feglm_.py` (add backend infrastructure, remove restriction) |
| 73 | +- `pyfixest/estimation/fegaussian_.py` (pass demeaner_backend parameter) |
| 74 | +- `tests/test_feols_feglm_internally.py` (enable skipped test) |
| 75 | +- `tests/test_vs_fixest.py` (add R comparison tests) |
| 76 | + |
| 77 | +## Expected Behavior |
| 78 | + |
| 79 | +After implementation: |
| 80 | + |
| 81 | +1. Users can specify `demeaner_backend` for `feglm`, just like with `feols` |
| 82 | +2. `pf.feglm(fml="Y ~ X1 | f1", family="gaussian", demeaner_backend="rust")` works with any backend |
| 83 | +3. Results match R's `fixest::feglm()` with Gaussian family |
| 84 | +4. For Gaussian family, effectively matches `feols()` results (with appropriate inference adjustments) |
| 85 | + |
| 86 | +### To-dos |
| 87 | + |
| 88 | +- [ ] Update imports in feglm_.py to use BACKENDS and DemeanerBackendOptions |
| 89 | +- [ ] Add demeaner_backend parameter to Feglm.__init__ and set self._demean_func |
| 90 | +- [ ] Update residualize() method to use self._demean_func instead of hardcoded demean |
| 91 | +- [ ] Remove NotImplementedError for fixed effects in feglm_.py |
| 92 | +- [ ] Add demeaner_backend parameter to Fegaussian.__init__ and pass to parent |
| 93 | +- [ ] Remove @pytest.mark.skip decorator from test in test_feols_feglm_internally.py |
| 94 | +- [ ] Add R comparison tests to test_vs_fixest.py |
| 95 | +- [ ] Run tests and validate against R fixest results |
0 commit comments