Skip to content

Commit

Permalink
type sparse.py, enable mypy in CI (#773)
Browse files Browse the repository at this point in the history
* typing configs

* remove fallback implemenations of combination/permutation functions

* refactor "test" to a test

* some types

* stash

* register test_sparse in `run_tests.py`

* isort

* types

* revert

* unused

* remove type restriction

* python 3.7

* does claude know travis?

* add comprehensive blacklist

* travis

* omit one more

* black

* add mypy to github actions

* remove travis

* better type ignore

* remove whitespace cruft from merge

* turns out black wanted that whitespace...

* took out versions from requirements-dev.txt and simplified exclude rule for mypy

---------

Co-authored-by: Franco Peschiera <[email protected]>
  • Loading branch information
frrad and pchtsp authored Nov 1, 2024
1 parent 1527a4f commit c06fe08
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 62 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Update pip
- name: Update pip, install dev deps
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
- name: Code Quality
run: |
python -m pip install black
black pulp/ --check
- name: Type Check
run: |
mypy ./
- name: Install dependencies
run: |
python -m pip install .
Expand Down
42 changes: 0 additions & 42 deletions .travis.yml

This file was deleted.

46 changes: 32 additions & 14 deletions pulp/sparse.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Dict, Generic, List, Tuple, TypeVar, cast

# Sparse : Python basic dictionary sparse matrix

# Copyright (c) 2007, Stuart Mitchell ([email protected])
Expand Down Expand Up @@ -27,22 +29,31 @@
notably this allows the sparse matrix to be output in various formats
"""

T = TypeVar("T")


class Matrix(dict):
class Matrix(Generic[T], Dict[Tuple[int, int], T]):
"""This is a dictionary based sparse matrix class"""

def __init__(self, rows, cols):
def __init__(self, rows: List[int], cols: List[int]):
"""initialises the class by creating a matrix that will have the given
rows and columns
"""
self.rows = rows
self.cols = cols
self.rowdict = {row: {} for row in rows}
self.coldict = {col: {} for col in cols}
self.rows: List[int] = rows
self.cols: List[int] = cols
self.rowdict: Dict[int, Dict[int, T]] = {row: {} for row in rows}
self.coldict: Dict[int, Dict[int, T]] = {col: {} for col in cols}

def add(self, row, col, item, colcheck=False, rowcheck=False):
if not (rowcheck and row not in self.rows):
if not (colcheck and col not in self.cols):
def add(
self,
row: int,
col: int,
item: T,
colcheck: bool = False,
rowcheck: bool = False,
) -> None:
if not rowcheck or row in self.rows:
if not colcheck or col in self.cols:
dict.__setitem__(self, (row, col), item)
self.rowdict[row][col] = item
self.coldict[col][row] = item
Expand All @@ -52,20 +63,26 @@ def add(self, row, col, item, colcheck=False, rowcheck=False):
else:
raise RuntimeError(f"row {row} is not in the matrix rows")

def addcol(self, col, rowitems):
def addcol(self, col: int, rowitems: Dict[int, T]) -> None:
"""adds a column"""
if col in self.cols:
for row, item in rowitems.items():
self.add(row, col, item, colcheck=False)
else:
raise RuntimeError("col is not in the matrix columns")

def get(self, k, d=0):
return dict.get(self, k, d)
def get( # type: ignore[override]
self,
coords: Tuple[int, int],
default: T = 0, # type: ignore[assignment]
) -> T:
return dict.get(self, coords, default)

def col_based_arrays(self):
def col_based_arrays(
self,
) -> Tuple[int, List[int], List[int], List[int], List[T]]:
numEls = len(self)
elemBase = []
elemBase: List[T] = []
startsBase = []
indBase = []
lenBase = []
Expand All @@ -74,5 +91,6 @@ def col_based_arrays(self):
elemBase.extend(list(self.coldict[col].values()))
indBase.extend(list(self.coldict[col].keys()))
lenBase.append(len(elemBase) - startsBase[-1])

startsBase.append(len(elemBase))
return numEls, startsBase, lenBase, indBase, elemBase
2 changes: 0 additions & 2 deletions pulp/tests/test_pulp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1218,7 +1218,6 @@ def add_const(prob):

@gurobi_test
def test_measuring_solving_time(self):

time_limit = 10
solver_settings = dict(
PULP_CBC_CMD=30,
Expand Down Expand Up @@ -1256,7 +1255,6 @@ def test_measuring_solving_time(self):

@gurobi_test
def test_time_limit_no_solution(self):

time_limit = 1
solver_settings = dict(HiGHS_CMD=60, HiGHS=60, PULP_CBC_CMD=60, COIN_CMD=60)
bins = solver_settings.get(self.solver.name)
Expand Down
40 changes: 38 additions & 2 deletions pulp/tests/test_sparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@


class SparseTest(unittest.TestCase):
def test_sparse(self):
def test_sparse(self) -> None:
rows = list(range(10))
cols = list(range(50, 60))
mat = Matrix(rows, cols)
mat = Matrix[str](rows, cols)
mat.add(1, 52, "item")
mat.add(2, 54, "stuff")
assert mat.col_based_arrays() == (
Expand All @@ -17,3 +17,39 @@ def test_sparse(self):
[1, 2],
["item", "stuff"],
)

assert mat.get((1, 52)) == "item"

mat.addcol(51, {2: "hello"})
assert mat.col_based_arrays() == (
3,
[0, 0, 1, 2, 2, 3, 3, 3, 3, 3, 3],
[0, 1, 1, 0, 1, 0, 0, 0, 0, 0],
[2, 1, 2],
["hello", "item", "stuff"],
)

def test_sparse_floats(self) -> None:
rows = list(range(10))
cols = list(range(50, 60))
mat = Matrix[float](rows, cols)
mat.add(1, 52, 1.234)
mat.add(2, 54, 5.678)
assert mat.col_based_arrays() == (
2,
[0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2],
[0, 0, 1, 0, 1, 0, 0, 0, 0, 0],
[1, 2],
[1.234, 5.678],
)

assert mat.get((1, 52)) == 1.234

mat.addcol(51, {2: 9.876})
assert mat.col_based_arrays() == (
3,
[0, 0, 1, 2, 2, 3, 3, 3, 3, 3, 3],
[0, 1, 1, 0, 1, 0, 0, 0, 0, 0],
[2, 1, 2],
[9.876, 1.234, 5.678],
)
17 changes: 17 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[tool.mypy]
exclude = [
"doc/",
"examples/",
"pulp/__init__.py",
"pulp/apis/",
"pulp/mps_lp.py",
"pulp/pulp.py",
"pulp/solverdir/",
"pulp/tests/",
"pulp/utilities.py",
"setup.py",
"venv/.*"
]
follow_imports = "silent"
strict = true
check_untyped_defs = true
3 changes: 2 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
black
mypy
pre-commit==2.12.0
sphinx
sphinx_rtd_theme
black

0 comments on commit c06fe08

Please sign in to comment.