Skip to content

Tooling foundation: packaging config, community files, bug fixes & gitignore#59

Open
emapuljak wants to merge 23 commits into
devfrom
54-tooling
Open

Tooling foundation: packaging config, community files, bug fixes & gitignore#59
emapuljak wants to merge 23 commits into
devfrom
54-tooling

Conversation

@emapuljak

Copy link
Copy Markdown
Collaborator

Closes #54

First of three PRs (A → B → C) porting the python-template tooling into PQuantML.

What's in it

  • Packaging & tooling config in pyproject.toml: Ruff (line-length 125), mypy, bandit,
    pytest and coverage sections; pytest-cov in test; new docs and dev extras;
    packages.find auto-discovery + package-data so subpackages and configs/*.yaml
    actually ship; dropped the unused all extra.
  • Community files: CONTRIBUTING.md and CHANGELOG.md, surfaced in the docs via MyST include - created here so PR B's changelog gate can be added in CI
    has something to check.
  • Pre-existing bug fixes: tox.ini extras name, .readthedocs.yaml conf path,
    docs/Makefile HGQ leftover, README links, stray root __init__.py, hardcoded docs
    version, and torch.load(..., weights_only=True).
  • README: installation, the KERAS_BACKEND requirement (previously undocumented —
    PyTorch users silently got the tensorflow backend), a quick-start example, and
    Contributing/License sections.
  • .gitignore: tool caches + coverage artifacts; narrowed the blanket *.txt.
  • mypy clean-up: mypy src tests goes from 256 errors → 0, fixing several real
    defects it surfaced — hardcoded CUDA in add_compression_layers (the library was
    unusable on CPU-only machines), pAdam/pSGD *args collisions, PQActivation.get_config()
    calling a nonexistent superclass method, and a missing update_mask() on the torch PDP.

Notes

  • Ruff's D (docstring) rule is deliberately excluded — enabling it before the
    docstring backfill would flood ruff check. It goes on in PR C.
  • coverage fail_under is 40 (current measured combined coverage is 43%); raising it
    toward 80 is tracked separately - Increase code test coverage to 80% #57
  • Side effect: 23 more tests pass than before (torch 40→17 failures, tf 81→77). Remaining
    failures are pre-existing and unrelated - will be fixed in Fix tests and code before CI implementation #58

@emapuljak
emapuljak requested a review from nroope July 14, 2026 19:20
@emapuljak emapuljak self-assigned this Jul 14, 2026
@emapuljak emapuljak linked an issue Jul 22, 2026 that may be closed by this pull request
44 tasks

@nastiapetrovych nastiapetrovych left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @emapuljak , thanks a lot for your PR. I really appreciate the effort you've put into it.

  • I would recommend splitting it into two separate PRs: one focused on the CI/CD pipeline, and another for the smaller code improvements. That will make the review process much easier.

  • Also, since the codebase is changing quite frequently, updating 74 modules at once might not be the best approach. It would likely mean rebasing every time Roope makes changes, which could become quite time-consuming.

  • A few of the suggested changes also aren't really needed for this project. I've left more detailed comments in the review.

If anything is unclear or you'd like to discuss any of the feedback, feel free to reach out anytime:)

"def get_scheduler(optimizer, config):\n",
" if config.training_parameters.lr_schedule is None:\n",
" return None\n",
" elif config.training_parameters.lr_schedule == \"cosine\":\n",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not necessary to modify elif -> else conditions as there is a return statement;

just a matter of preferences

"loss_func = nn.CrossEntropyLoss()\n",
"\n",
"# Run optimization loop\n",
"best_params = tuner.run_optimization(model,\n",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here, there is a matter of preferences as well, not a strict requirement

beta = to_numpy(keras.ops.cast(layer.beta, layer.dtype))
else:
beta = np.zeros_like(mean)
gamma = to_numpy(keras.ops.cast(layer.gamma, layer.dtype)) if layer.scale else np.ones_like(mean)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the matter of preferences, not really needed as well

self.t_start_collecting_batch = self.config.pruning_parameters.t_start_collecting_batch

def build(self, input_shape):
self.shape = (input_shape[0], 1)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not see a need in changing this


def build(self, input_shape):
self.shape = (input_shape[0], 1)
shape: tuple[Any, ...] = (input_shape[0], 1)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, specifying the type annotation here is completely optional

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a good coding practice, not my preference. So I am just adding ruff/mypy rules that are good for code health.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment. The project has been actively developed by @nroope and me for more than two years. Before proposing changes such as switching to Ruff or adding MyPy, it would be helpful to first discuss the needs and priorities of the main contributors and whether those changes would provide meaningful benefits for this project.

Our current pre-commit setup, can be seen in pre-commit-config.yaml file, includes Black, isort, Flake8, and the other tools, that has worked well in practice and satisfies the project's requirements. We're happy with this workflow, and at the moment we don't see a huge need to replace it with newer approaches simply because it's more modern. Given the project's current priorities, I believe there are more impactful tasks to focus on.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So ruff is explicitly designed to replace Black, isort, Flake8 and many common Flake8 plugins. Thats why I suggested to change it, because it can cover them all. Plus Mypy then adds additional layer that the current tools do not provide - it can catch invalid function arguments, return values, missing attributes, etc. Ruff is released in 2022, opposed to others which are older, and already has the same count of users because its easier to keep all checkes in one library with ruff check and ruff format commands. so basically you can put formatting, import sorting and linting into one tool now.

I agree that maybe I overshot by including them all in one PR. I could have added ruff (replacing these 3), then mypy, then bandit for security checks (which doesnt exist) all in 3 separate PRs. I could still do that. You can also do your research on why ruff is good and what are mypy and bandit.



class PQLayerNorm(nn.LayerNorm):
_weight: nn.Parameter | None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

self.in_quant_granularity = in_quant_granularity
self.out_quant_granularity = out_quant_granularity
self.param_quant_granularity = param_quant_granularity
proj_kwargs = dict(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about the matter of preferences

quantize_input=quantize_input,
quantize_output=quantize_output,
)
ln_kwargs = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment here



class ActivationPruning(nn.Module):
mask: Tensor

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no real need to add this type annotation here

if self.precompute_layer_inputs:
tmpdir = tempfile.TemporaryDirectory(prefix="ldistil_", dir=self.cache_dir or os.getcwd())
dataloader = self.precompute_layer_io(teacher_layer, dataloader, self.device, tmpdir.name)
tmpdir = tempfile.TemporaryDirectory(prefix="ldistil_", dir=self.cache_dir or str(Path.cwd()))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about Path

@emapuljak

emapuljak commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

This is one of 4 PRs that need to be done. This does not include CI pipeline at all.

Its impossible to have smaller changes then this for adding the tooling because adding it means a lot of rules are added that are changing the code writing, that is why I separated it into 4 PRs. This is just the first one.

Also, many comments that you made, are more on code health side, that are imposed by mypy / ruff / bandit and were being addressed. Not my preferences. But good quality of code writing.

  1. the library should always follow updated syntax of Python
  2. Path is better to use because it object-oriented - explanation: Path treats paths as smart objects, whereas os.path treats them as dumb strings.
  3. type annotations are very neccessary for code quality - mypy flags them - and they keep in track with new python syntax which should be updated.
  4. if/elif statements - should have final return statement if there is no ending else. also why elif is changed to if --> Since the if blocks already use return statements, the function exits immediately if a match is found, making the subsequent elif and else guards redundant. This approach flattens our code by stripping out unnecessary nesting, makes the final ValueError stand out clearly as our fallback error path, and keeps ruff happy since it explicitly flags nested blocks after returns.
  5. ordering of imports are required by new code checkers

All these are added for the library to have a good base for future development which will involve more people/contributors. Some stricter rules on code quality should be in place to avoid any confusion. And when a new feature is being developed that any developer of the library can come and say "I can work on this issue" and you can guarantee that the code would be written in the same manner.

It takes some time to get used to these practices but they really make sense overall. And pay off.

@emapuljak

Copy link
Copy Markdown
Collaborator Author

If something is more modern and used more then it should be included in my opinion. I am trying to help you stay in track with the current development design, and I already discussed this with Roope and he agreed.
In any case we can meet to discuss more.
The excuse that there are more pressure things to do its just an excuse, these things are not hard to implement as seen in the PR.

If you want to scale this library to something bigger I am giving you suggestions to do so. Don't need to take them. But it will help you in the future.

I am not trying to make criticism for the sake of changing something, I am trying to transfer knowledge that other ppl who know better trassfered to me before.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[sub-issue #53] Implement Tooling

2 participants