Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vector.arange() and Frequency #7

Merged
merged 3 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.0
hooks:
- id: ruff-format
name: Run Formatter
- id: ruff
name: Run Linter
args: [ --fix ]
- id: ruff-format
name: Run Formatter

- repo: local
hooks:
Expand Down
13 changes: 9 additions & 4 deletions generate/generate_boilerplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def _generate_base_unit(current_class: str, units: dict[str, str]) -> list[str]:
if base_unit is None:
raise ValueError(f"{current_class} needs a unit with factor equal to 1.")

return [f' _BASE_UNIT = "{base_unit}"', ""]
return [f' BASE_UNIT = "{base_unit}"', ""]


def _generate_properties(current_class: str, units: dict[str, str]) -> list[str]:
Expand All @@ -76,13 +76,13 @@ def _generate_properties(current_class: str, units: dict[str, str]) -> list[str]


def _generate_init(units: dict[str, str]) -> list[str]:
code = [" " * 4 + "def __init__(", " " * 8 + "self,"]
code = [" " * 4 + "def __init__(", " " * 8 + "self,", " " * 8 + "_base_value: float = 0.0,"]

for unit in units:
code.append(" " * 8 + f"{unit}: float = 0.0,")

code.append(" " * 4 + ") -> None:")
code.append(" " * 8 + "self._base_value = 0.0")
code.append(" " * 8 + "self._base_value = _base_value")

for unit, factor in units.items():
code.append(" " * 8 + f"self._base_value += {unit} * {factor}")
Expand All @@ -93,7 +93,6 @@ def _generate_init(units: dict[str, str]) -> list[str]:

def _generat_zero_function(current_class: str) -> list[str]:
return [
"",
" " * 4 + "@classmethod",
" " * 4 + f"def zero(cls) -> {current_class}:",
" " * 8 + f'"""Create a {current_class} with a value of zero."""',
Expand Down Expand Up @@ -122,6 +121,12 @@ def _generat_zero_function(current_class: str) -> list[str]:
"square_millimeters": "10 ** (-3 * 2)",
"square_micrometers": "10 ** (-6 * 2)",
},
"Frequency": {
"gigahertz": "10**9",
"megahertz": "10**6",
"kilohertz": "10**3",
"hertz": "1",
},
"Length": {
"miles": "1609.34",
"kilometers": "10**3",
Expand Down
2 changes: 1 addition & 1 deletion quantio/_quantity_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class _QuantityBase(ABC):
_base_value: float
"The base unit of the quantity."

_BASE_UNIT: str
BASE_UNIT: str
"Name of the unit with a factor of 1."

def __eq__(self, other: object) -> bool:
Expand Down
84 changes: 70 additions & 14 deletions quantio/quantities.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Acceleration(_QuantityBase):

# --- This part is auto generated. Do not change manually. ---

_BASE_UNIT = "meters_per_square_second"
BASE_UNIT = "meters_per_square_second"

@property
def meters_per_square_second(self) -> float:
Expand All @@ -22,10 +22,11 @@ def g_force(self) -> float:

def __init__(
self,
_base_value: float = 0.0,
meters_per_square_second: float = 0.0,
g_force: float = 0.0,
) -> None:
self._base_value = 0.0
self._base_value = _base_value
self._base_value += meters_per_square_second * 1
self._base_value += g_force * (1 / 9.8)

Expand All @@ -42,7 +43,7 @@ class Angle(_QuantityBase):

# --- This part is auto generated. Do not change manually. ---

_BASE_UNIT = "radians"
BASE_UNIT = "radians"

@property
def degrees(self) -> float:
Expand All @@ -56,10 +57,11 @@ def radians(self) -> float:

def __init__(
self,
_base_value: float = 0.0,
degrees: float = 0.0,
radians: float = 0.0,
) -> None:
self._base_value = 0.0
self._base_value = _base_value
self._base_value += degrees * (3.141592653589793 / 180)
self._base_value += radians * 1

Expand All @@ -76,7 +78,7 @@ class Area(_QuantityBase):

# --- This part is auto generated. Do not change manually. ---

_BASE_UNIT = "square_meters"
BASE_UNIT = "square_meters"

@property
def square_miles(self) -> float:
Expand Down Expand Up @@ -120,6 +122,7 @@ def square_micrometers(self) -> float:

def __init__(
self,
_base_value: float = 0.0,
square_miles: float = 0.0,
square_kilometers: float = 0.0,
square_meters: float = 0.0,
Expand All @@ -129,7 +132,7 @@ def __init__(
square_millimeters: float = 0.0,
square_micrometers: float = 0.0,
) -> None:
self._base_value = 0.0
self._base_value = _base_value
self._base_value += square_miles * 1609.34**2
self._base_value += square_kilometers * 10 ** (3 * 2)
self._base_value += square_meters * 1
Expand All @@ -147,12 +150,61 @@ def zero(cls) -> Area:
# --- End of auto generated part. ---


class Frequency(_QuantityBase):
"""Frequency is the number of occurrences of a repeating event per unit of time.."""

# --- This part is auto generated. Do not change manually. ---

BASE_UNIT = "hertz"

@property
def gigahertz(self) -> float:
"""The frequency in gigahertz."""
return self._base_value / 10**9

@property
def megahertz(self) -> float:
"""The frequency in megahertz."""
return self._base_value / 10**6

@property
def kilohertz(self) -> float:
"""The frequency in kilohertz."""
return self._base_value / 10**3

@property
def hertz(self) -> float:
"""The frequency in hertz."""
return self._base_value / 1

def __init__(
self,
_base_value: float = 0.0,
gigahertz: float = 0.0,
megahertz: float = 0.0,
kilohertz: float = 0.0,
hertz: float = 0.0,
) -> None:
self._base_value = _base_value
self._base_value += gigahertz * 10**9
self._base_value += megahertz * 10**6
self._base_value += kilohertz * 10**3
self._base_value += hertz * 1

@classmethod
def zero(cls) -> Frequency:
"""Create a Frequency with a value of zero."""
return Frequency()

# --- End of auto generated part. ---


class Length(_QuantityBase):
"""The one-dimensional extent of an object or the distance between two points."""

# --- This part is auto generated. Do not change manually. ---

_BASE_UNIT = "meters"
BASE_UNIT = "meters"

@property
def miles(self) -> float:
Expand Down Expand Up @@ -196,6 +248,7 @@ def micrometers(self) -> float:

def __init__(
self,
_base_value: float = 0.0,
miles: float = 0.0,
kilometers: float = 0.0,
meters: float = 0.0,
Expand All @@ -205,7 +258,7 @@ def __init__(
millimeters: float = 0.0,
micrometers: float = 0.0,
) -> None:
self._base_value = 0.0
self._base_value = _base_value
self._base_value += miles * 1609.34
self._base_value += kilometers * 10**3
self._base_value += meters * 1
Expand All @@ -228,7 +281,7 @@ class Mass(_QuantityBase):

# --- This part is auto generated. Do not change manually. ---

_BASE_UNIT = "kilograms"
BASE_UNIT = "kilograms"

@property
def tonnes(self) -> float:
Expand Down Expand Up @@ -267,6 +320,7 @@ def micrograms(self) -> float:

def __init__(
self,
_base_value: float = 0.0,
tonnes: float = 0.0,
kilograms: float = 0.0,
pounds: float = 0.0,
Expand All @@ -275,7 +329,7 @@ def __init__(
milligrams: float = 0.0,
micrograms: float = 0.0,
) -> None:
self._base_value = 0.0
self._base_value = _base_value
self._base_value += tonnes * 10**3
self._base_value += kilograms * 1
self._base_value += pounds * (1 / 2.20462)
Expand All @@ -297,7 +351,7 @@ class Velocity(_QuantityBase):

# --- This part is auto generated. Do not change manually. ---

_BASE_UNIT = "meters_per_second"
BASE_UNIT = "meters_per_second"

@property
def meters_per_second(self) -> float:
Expand All @@ -316,11 +370,12 @@ def miles_per_hour(self) -> float:

def __init__(
self,
_base_value: float = 0.0,
meters_per_second: float = 0.0,
kilometers_per_hour: float = 0.0,
miles_per_hour: float = 0.0,
) -> None:
self._base_value = 0.0
self._base_value = _base_value
self._base_value += meters_per_second * 1
self._base_value += kilometers_per_hour * (1 / 3.6)
self._base_value += miles_per_hour * (1 / 2.23694)
Expand All @@ -338,7 +393,7 @@ class Time(_QuantityBase):

# --- This part is auto generated. Do not change manually. ---

_BASE_UNIT = "seconds"
BASE_UNIT = "seconds"

@property
def hours(self) -> float:
Expand All @@ -362,12 +417,13 @@ def milliseconds(self) -> float:

def __init__(
self,
_base_value: float = 0.0,
hours: float = 0.0,
minutes: float = 0.0,
seconds: float = 0.0,
milliseconds: float = 0.0,
) -> None:
self._base_value = 0.0
self._base_value = _base_value
self._base_value += hours * 60 * 60
self._base_value += minutes * 60
self._base_value += seconds * 1
Expand Down
29 changes: 25 additions & 4 deletions quantio/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,34 @@


class Vector(Generic[T]):
"""An 1 dimensional array of either quantity or numeric elements."""
"""A 1 dimensional array of either quantity or numeric elements."""

_elements: np.array

def __init__(self, elements: list | tuple | np.ndarray) -> None:
self._elements = np.array(elements)

@classmethod
def arange(cls, start: T, stop: T, step: T) -> Vector[T]:
"""Return evenly spaced values within a given interval."""
if isinstance(start, _QuantityBase):
start_val = start._base_value

if not isinstance(stop, _QuantityBase):
raise TypeError
stop_val = stop._base_value

if not isinstance(step, _QuantityBase):
raise TypeError
step_val = step._base_value

element_type = type(start)
return Vector(
[element_type(value) for value in np.arange(start_val, stop_val, step_val)]
)

return Vector(np.arange(start, stop, step))

def to_numpy(self, unit: str | None = None) -> np.ndarray[float]:
"""Convert this vector into a numpy array of floats."""
if isinstance(self._elements[0], _QuantityBase):
Expand Down Expand Up @@ -53,15 +74,15 @@ def __sub__(self, other: Vector[T] | np.ndarray) -> Vector[T]:
def __mul__(self, other: Vector | np.ndarray | float) -> np.ndarray:
"""Multipy this vector with either another vector or a scalar."""
if isinstance(self._elements[0], _QuantityBase):
self_to_numpy = self.to_numpy(self._elements[0]._BASE_UNIT)
self_to_numpy = self.to_numpy(self._elements[0].BASE_UNIT)
else:
self_to_numpy = self.to_numpy()
return self_to_numpy * _other_to_numpy(other)

def __truediv__(self, other: Vector | np.ndarray | float) -> np.ndarray:
"""Multipy this vector with either another vector or a scalar."""
if isinstance(self._elements[0], _QuantityBase):
self_to_numpy = self.to_numpy(self._elements[0]._BASE_UNIT)
self_to_numpy = self.to_numpy(self._elements[0].BASE_UNIT)
else:
self_to_numpy = self.to_numpy()
return self_to_numpy / _other_to_numpy(other)
Expand All @@ -83,7 +104,7 @@ def _other_to_numpy(other: Vector | np.ndarray | float) -> np.ndarray:

if isinstance(other, Vector):
if isinstance(other._elements[0], _QuantityBase):
return other.to_numpy(other._elements[0]._BASE_UNIT)
return other.to_numpy(other._elements[0].BASE_UNIT)
return other.to_numpy()

if isinstance(other, np.ndarray):
Expand Down
18 changes: 18 additions & 0 deletions test/test_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from quantio import (
Vector,
Length,
Time,
CanNotAddTypesError,
CanNotSubtractTypesError,
NoUnitSpecifiedError,
Expand Down Expand Up @@ -182,5 +183,22 @@ def test_to_numpy__quantity_no_unit():
vec.to_numpy()


def test_arange__float():
actual = Vector[float].arange(start=0.0, stop=5.0, step=2)
assert actual == Vector([0, 2, 4])


def test_arange__quantity():
actual = Vector[Length].arange(
start=Length(meters=0), stop=Length(meters=5), step=Length(meters=2)
)
assert actual == Vector([Length(meters=0), Length(meters=2), Length(meters=4)])


def test_arange__false_type_combination():
with pytest.raises(TypeError):
Vector.arange(start=Length(meters=0), stop=Time(meters=5), step=Length(meters=2))


if __name__ == "__main__":
pytest.main([__file__, "-v"])
Loading