Skip to content

Commit 8860e87

Browse files
Merge main into jg/pretty-minimal and resolve core.py conflicts
2 parents a35cf5a + 7516a78 commit 8860e87

43 files changed

Lines changed: 1911 additions & 404 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,9 @@ repos:
3333
entry: make typecheck
3434
language: system
3535
pass_filenames: false
36+
37+
- id: test
38+
name: Test
39+
entry: make test
40+
language: system
41+
pass_filenames: false

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ lint:
1212

1313
.PHONY: format
1414
format:
15-
ruff check --fix .
1615
ruff format .
16+
ruff check --fix .
1717

1818
.PHONY: typecheck
1919
typecheck:

README.md

Lines changed: 17 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -1,233 +1,28 @@
1-
> [!WARNING]
2-
> This repo is public but under intense reconstruction and is **not ready for users**. Please check back soon!
1+
[![Documentation Status](https://readthedocs.org/projects/glass-box-umap/badge/?version=latest)](https://glass-box-umap.readthedocs.io/en/latest/?badge=latest)
2+
[![PyPI - Version](https://img.shields.io/pypi/v/glass-box-umap)](https://pypi.org/project/glass-box-umap/)
3+
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/glass-box-umap)](https://pypi.org/project/glass-box-umap/)
4+
[![GitHub license](https://img.shields.io/github/license/Arcadia-Science/glass-box-umap)](https://github.com/Arcadia-Science/glass-box-umap/blob/main/LICENSE)
35

46
# Glass Box UMAP
57

6-
This repo contains a Python package called `glass_box_umap`.
8+
Glass Box UMAP augments UMAP by computing exact feature contributions to the UMAP embedding.
79

8-
This package augments UMAP by computing exact feature contributions to the UMAP embedding.
10+
Standard UMAP produces embeddings but offers no insight into why points land where they do. Glass Box UMAP solves this by using a specially designed neural network that enables exact computation of feature contributions, and does so without approximations. The feature attributions are mathematically exact, validated to near machine precision.
911

10-
The publication for Glass Box UMAP can be found [here](https://arcadia-science.github.io/glass-box-umap-notebook-pub/).
12+
# Documentation
1113

12-
## Installation
14+
<a href="https://glass-box-umap.readthedocs.io">
15+
<img src="docs/_assets/rtd-badge.png" width="200" alt="rtd">
16+
</a>
1317

14-
```bash
15-
# pip
16-
pip install glass-box-umap
18+
All resources are hosted at [https://glass-box-umap.readthedocs.io](https://glass-box-umap.readthedocs.io).
1719

18-
# uv
19-
uv pip install glass-box-umap
20-
```
20+
**Quick links**:
2121

22-
Find complete installation instructions at `docs/install.md`.
22+
1. [**Installation**](https://glass-box-umap.readthedocs.io/en/latest/getting_started/install.html)
23+
1. [Quickstart](https://glass-box-umap.readthedocs.io/en/latest/getting_started/quickstart.html)
24+
1. [Citation](https://glass-box-umap.readthedocs.io/en/latest/meta/citation.html)
2325

24-
## Development
26+
# About
2527

26-
### Environment setup
27-
28-
We use [uv](https://docs.astral.sh/uv/) for dependency management and build tooling. First, install uv:
29-
30-
```bash
31-
curl -LsSf https://astral.sh/uv/install.sh | sh
32-
```
33-
34-
Make sure uv is installed and available. Verify this by checking which version you have:
35-
36-
```bash
37-
uv --version
38-
```
39-
40-
Then install the project with development dependencies:
41-
42-
```bash
43-
uv sync --group dev
44-
```
45-
46-
To also install documentation dependencies:
47-
48-
```bash
49-
uv sync --group dev --group docs
50-
```
51-
52-
This creates a virtual environment in `.venv` and installs all dependencies. The package itself is automatically installed in editable mode (equivalent to `pip install -e .`).
53-
54-
The easiest way to run code is to simply prefix commands with `uv run` (e.g., `uv run <YOUR_COMMAND>`). This executes the command inside the virtual environment automatically, so you don't need to activate it first.
55-
56-
Alternatively, if you prefer a traditional workflow, or are running from a different directory, you can manually source the activation script:
57-
58-
```bash
59-
source .venv/bin/activate
60-
```
61-
62-
### Formatting and linting
63-
64-
To format the code, use the following command:
65-
66-
```bash
67-
make format
68-
```
69-
70-
To run the lint checks and type checking, use the following command:
71-
72-
```bash
73-
make lint
74-
```
75-
76-
### Pre-commit hooks
77-
78-
We use pre-commit to run formatting and lint checks before each commit. To install the pre-commit hooks, use the following command:
79-
80-
```bash
81-
pre-commit install
82-
```
83-
84-
To run the pre-commit checks manually, use the following command:
85-
86-
```bash
87-
make pre-commit
88-
```
89-
90-
### Testing
91-
92-
We use `pytest` for testing. The tests are found in the `glass_box_umap/tests/` subpackage. To run the tests, use the following command:
93-
94-
```bash
95-
make test
96-
```
97-
98-
### Managing dependencies
99-
100-
We use uv to manage dependencies. To add a new dependency:
101-
102-
```bash
103-
uv add some-package
104-
```
105-
106-
To add a new development dependency:
107-
108-
```bash
109-
uv add --group dev some-dev-package
110-
```
111-
112-
To update a dependency:
113-
114-
```bash
115-
uv lock --upgrade-package some-package
116-
```
117-
118-
Whenever you add or update a dependency, uv will automatically update both `pyproject.toml` and `uv.lock`. Make sure to commit changes to these files.
119-
120-
### Documentation
121-
122-
We use Sphinx for documentation with the [`furo` theme](https://github.com/pradyunsg/furo). We also use some Sphinx extensions (described below) to make the process of writing documentation easier.
123-
124-
To build the docs, first install `pandoc`. On macOS, this can be done using `brew`:
125-
126-
```
127-
brew install pandoc
128-
```
129-
130-
Then, build the docs using the following command:
131-
132-
```bash
133-
uv run make docs
134-
```
135-
136-
Note: the `pandoc` dependency is only required by the `nbsphinx` extension. If this extension is removed, there is no need to install `pandoc`.
137-
138-
#### sphinx-autoapi
139-
140-
This extension generates API docs automatically from the docstrings in the source code. To do so, it requires that docstrings adhere to the Google or Numpy style. This style is described in the [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html).
141-
142-
#### napolean
143-
144-
Rather than writing our docstrings in RST, we use this extension to convert Google and NumPy-style docstrings to RST at build time.
145-
146-
#### myst-parser
147-
148-
RST is complicated to write. This extension lets us write our docs in Markdown and then converts them to RST at build time.
149-
150-
#### nbsphinx
151-
152-
It is often convenient to write examples as Jupyter notebooks. This extension executes Jupyter notebooks and renders the results in the docs at build time. It requires `pandoc`, which can be installed using `brew install pandoc`.
153-
154-
#### Removing unused Sphinx extensions
155-
156-
To remove an unused extension, delete the corresponding line from the `extensions` list in `docs/conf.py` and delete the extension from the `docs` dependency group in `pyproject.toml`.
157-
158-
## Publishing the package on PyPI
159-
160-
Publishing the package on PyPI requires that you have API tokens for the test and production PyPI servers. You can find these tokens in your PyPI account settings. Create a `.env` file by coping `.env.copy` and add your tokens to this file.
161-
162-
We use semantic versioning of the form `MAJOR.MINOR.PATCH`. See [semver.org](https://semver.org/) for more information. When you're ready to release a new version:
163-
164-
1. Update the `version` field in `pyproject.toml` to the new version number
165-
2. Commit the change: `git commit -am "Bump version to X.Y.Z"`
166-
3. Create a git tag matching the version:
167-
168-
```bash
169-
RELEASE_VERSION=0.1.0
170-
git tag -a v${RELEASE_VERSION} -m "Release version ${RELEASE_VERSION}"
171-
git push origin v${RELEASE_VERSION}
172-
```
173-
174-
__Before creating the tag, make sure that your local git repository is on `main`, is up-to-date, and does not contain uncommitted changes!__
175-
176-
If you need to delete a tag you've created:
177-
178-
```bash
179-
git tag -d v${RELEASE_VERSION}
180-
```
181-
182-
If you already pushed the deleted tag to GitHub, also delete it from the remote:
183-
184-
```bash
185-
git push origin :refs/tags/v${RELEASE_VERSION}
186-
```
187-
188-
Once you've created the correct new tag, build the package:
189-
```bash
190-
make build
191-
```
192-
193-
You should see an output that looks like this:
194-
```
195-
Building glass-box-umap (0.1.0)
196-
- Building sdist
197-
- Built glass-box-umap-0.1.0.tar.gz
198-
- Building wheel
199-
```
200-
201-
The build artifacts are written to the `dist/` directory.
202-
203-
__Make sure that the version number in the output from `make build` matches the one in `pyproject.toml` and the git tag!__
204-
205-
If it does not, double-check that you updated the `version` field in `pyproject.toml` before creating the tag.
206-
207-
Next, check that you can publish the package to the PyPI test server:
208-
209-
```bash
210-
make build-and-test-publish
211-
```
212-
213-
The `build-and-test-publish` command calls `uv build` to build the package and then `uv publish` to upload the build artifacts to the test server.
214-
215-
Check that you can install the new version of the package from the test server:
216-
217-
```bash
218-
pip install --index-url https://pypi.org/simple/ --extra-index-url https://test.pypi.org/simple/ glass-box-umap==${RELEASE_VERSION}
219-
```
220-
221-
(For accurate installation testing: dependencies are installed from main PyPI (to avoid unreliable TestPyPI mirrors), while your package (not yet on main PyPI) is installed from TestPyPI.)
222-
223-
If everything looks good, build and publish the package to the prod PyPI server:
224-
225-
```bash
226-
make build-and-publish
227-
```
228-
229-
Finally, check that you can install the new version of the package from the prod PyPI server:
230-
231-
```bash
232-
pip install glass-box-umap==${RELEASE_VERSION}
233-
```
28+
Glass Box UMAP is part of [Arcadia Science's](https://www.arcadiascience.com/) commitment to open, reproducible research tools.

docs/_assets/rtd-badge.png

246 KB
Loading

docs/_static/css/cards.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.pub-card {
2+
border: 2px solid #cfcfcf;
3+
border-radius: 4px !important;
4+
padding: 1rem;
5+
margin-bottom: 1rem;
6+
background: #eef3f9;
7+
}
8+
9+
div.nbinput.container div.input_area div[class*=highlight] > pre {
10+
padding: 15px !important;
11+
}
12+
13+
div.nbinput.container {
14+
margin-bottom: 1rem;
15+
}

docs/_static/css/headings.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
h2, h3, h4, h5, h6 {
2+
font-family: "Atkinson Hyperlegible Next", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
3+
font-weight: 700;
4+
}

docs/_static/css/rubric.css

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
dl.py .base-classes {
2+
font-family: var(--font-stack--monospace);
3+
font-size: var(--api-font-size);
4+
font-weight: 700;
5+
}
6+
7+
dl.py .rubric {
8+
text-transform: uppercase;
9+
font-size: 0.75em;
10+
font-weight: 700;
11+
letter-spacing: 0.1em;
12+
color: var(--color-foreground-secondary);
13+
border-bottom: 1px solid var(--color-foreground-border);
14+
padding-bottom: 0.3em;
15+
margin-top: 1.5em;
16+
margin-bottom: 0.75em;
17+
}
18+
19+
p.param-sig {
20+
font-family: var(--font-stack--monospace);
21+
font-size: var(--api-font-size);
22+
font-weight: 700;
23+
}
24+
25+
p.param-desc {
26+
margin-left: 1.5em;
27+
margin-top: 0;
28+
}
29+
30+
dd.return-type p {
31+
font-family: var(--font-stack--monospace);
32+
font-size: var(--api-font-size);
33+
font-weight: 700;
34+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.toggle-hidden dl.py.class,
2+
.toggle-hidden dl.py.function,
3+
.toggle-hidden dl.py.method,
4+
.toggle-hidden dl.py.attribute,
5+
.toggle-hidden dl.py.data {
6+
display: none !important;
7+
}

docs/_templates/autoapi/index.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
API Reference
2+
=============
3+
4+
This page contains auto-generated API reference documentation.
5+
6+
.. toctree::
7+
:titlesonly:
8+
:maxdepth: 1
9+
10+
{% for page in pages|selectattr("is_top_level_object") %}
11+
{{ page.include_path }}
12+
{% endfor %}

docs/_templates/autoapi/macros.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{% macro _render_item_name(obj, sig=False) -%}
2+
:py:obj:`{{ obj.name }} <{{ obj.id }}>`
3+
{%- if sig -%}
4+
\ (
5+
{%- for arg in obj.obj.args -%}
6+
{%- if arg[0] %}{{ arg[0]|replace('*', '\*') }}{% endif -%}{{ arg[1] -}}
7+
{%- if not loop.last %}, {% endif -%}
8+
{%- endfor -%}
9+
){%- endif -%}
10+
{%- endmacro %}
11+
12+
{% macro _item(obj, sig=False, label='') %}
13+
* - {{ _render_item_name(obj, sig) }}
14+
- {% if label %}:summarylabel:`{{ label }}` {% endif %}{% if obj.summary %}{{ obj.summary }}{% else %}\-{% endif +%}
15+
{% endmacro %}
16+
17+
{% macro auto_summary(objs, title='') -%}
18+
.. list-table:: {{ title }}
19+
:header-rows: 0
20+
:widths: auto
21+
:class: summarytable
22+
23+
{% for obj in objs -%}
24+
{%- set sig = (obj.type in ['method', 'function'] and not 'property' in obj.properties) -%}
25+
26+
{%- if 'property' in obj.properties -%}
27+
{%- set label = 'prop' -%}
28+
{%- elif 'classmethod' in obj.properties -%}
29+
{%- set label = 'class' -%}
30+
{%- elif 'abstractmethod' in obj.properties -%}
31+
{%- set label = 'abc' -%}
32+
{%- elif 'staticmethod' in obj.properties -%}
33+
{%- set label = 'static' -%}
34+
{%- else -%}
35+
{%- set label = '' -%}
36+
{%- endif -%}
37+
38+
{{- _item(obj, sig=sig, label=label) -}}
39+
{%- endfor -%}
40+
41+
{% endmacro %}

0 commit comments

Comments
 (0)