kbasic 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kbasic-0.1.0 → kbasic-0.1.2}/PKG-INFO +5 -1
- kbasic-0.1.2/pyproject.toml +25 -0
- kbasic-0.1.2/src/kbasic/Tex.py +23 -0
- kbasic-0.1.2/src/kbasic/__init__.py +6 -0
- kbasic-0.1.2/src/kbasic/array.py +25 -0
- kbasic-0.1.2/src/kbasic/audio/__init__.py +1 -0
- kbasic-0.1.2/src/kbasic/audio/lib/Caroline Rose - year of the slug - 01 everything in its right place.wav +0 -0
- kbasic-0.1.2/src/kbasic/audio/lib/success.mp3 +0 -0
- kbasic-0.1.2/src/kbasic/audio/sound.py +8 -0
- kbasic-0.1.2/src/kbasic/bar.py +43 -0
- kbasic-0.1.2/src/kbasic/shell.py +133 -0
- kbasic-0.1.2/src/kbasic/typing.py +13 -0
- kbasic-0.1.2/src/kbasic/user_input.py +23 -0
- kbasic-0.1.2/src/kbasic/vectors.py +146 -0
- kbasic-0.1.0/pyproject.toml +0 -11
- kbasic-0.1.0/src/kbasic/__init__.py +0 -2
- {kbasic-0.1.0 → kbasic-0.1.2}/README.md +0 -0
- {kbasic-0.1.0 → kbasic-0.1.2}/src/kbasic/py.typed +0 -0
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: kbasic
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Add your description here
|
|
5
|
+
Requires-Dist: numpy>=2.4.2
|
|
6
|
+
Requires-Dist: pylatexenc>=2.10
|
|
7
|
+
Requires-Dist: scipy>=1.17.0
|
|
8
|
+
Requires-Dist: tqdm>=4.67.3
|
|
5
9
|
Requires-Python: >=3.11
|
|
6
10
|
Description-Content-Type: text/markdown
|
|
7
11
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "kbasic"
|
|
3
|
+
version = "0.1.2"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"numpy>=2.4.2",
|
|
9
|
+
"pylatexenc>=2.10",
|
|
10
|
+
"scipy>=1.17.0",
|
|
11
|
+
"tqdm>=4.67.3",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["uv_build>=0.10.2,<0.11.0"]
|
|
16
|
+
build-backend = "uv_build"
|
|
17
|
+
|
|
18
|
+
[dependency-groups]
|
|
19
|
+
dev = [
|
|
20
|
+
"pytest>=9.0.2",
|
|
21
|
+
"pytest-cov>=7.0.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[tool.pytest.ini_options]
|
|
25
|
+
addopts = "-v --maxfail=1 --cov=kbasic"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
# Syntax warnings show up everytime you \something so we ignore all of em, hope this doesn't fuck anything up!
|
|
3
|
+
warnings.filterwarnings(action='ignore', category=SyntaxWarning)
|
|
4
|
+
from pylatexenc.latex2text import LatexNodes2Text
|
|
5
|
+
l2t = LatexNodes2Text().latex_to_text
|
|
6
|
+
|
|
7
|
+
class Tex(str):
|
|
8
|
+
def __init__(self, x):
|
|
9
|
+
x = x.strip(' $')
|
|
10
|
+
if x[-1]=='\n': x = x[:-1]
|
|
11
|
+
if x[-2:]=='.0': x = x[:-2]
|
|
12
|
+
# replace wonky \ letter commands with raw versions
|
|
13
|
+
# WARNING: DOES NOT WORK WITH \U OR \X BECAUSE THESE ARE UNICODE THINGS AND IDK HOW TO OVERRIDE THAT BEHAVIOR
|
|
14
|
+
x = x.replace("\a", r"\a").replace("\b", r"\b").replace("\f", r"\f").replace("\n", r"\n").replace("\r", r"\r").replace("\t", r"\t").replace("\v", r"\v")
|
|
15
|
+
# make compatible with fstring
|
|
16
|
+
x = x.replace("[", "{").replace("]", "}")
|
|
17
|
+
# get rid of extraneous .0's
|
|
18
|
+
x = x.replace(".0 ", " ").replace(".0}", "}").replace(".0$", "$").replace(".0\n", "\n")
|
|
19
|
+
self.string = fr"{l2t(x)}"
|
|
20
|
+
self.wrap = "$"+self.string+"$"
|
|
21
|
+
|
|
22
|
+
def __repr__(self): return self.string.strip("$")
|
|
23
|
+
def __str__(self): return self.string
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.interpolate import RegularGridInterpolator
|
|
3
|
+
|
|
4
|
+
def where_closest(arr:np.ndarray, x): return np.argmin(np.abs(arr-x))
|
|
5
|
+
def where_between(arr:np.ndarray, low, high): return np.where((arr>=low)&(arr<=high))
|
|
6
|
+
|
|
7
|
+
def bin_this(x, y, n_bins=50, func=np.nanmean):
|
|
8
|
+
xbins = np.linspace(np.nanmin(x),np.nanmax(x),n_bins)
|
|
9
|
+
Y,error = list(),list()
|
|
10
|
+
for i,low_edge in enumerate(xbins[:-1]):
|
|
11
|
+
high_edge = xbins[i+1]
|
|
12
|
+
mask = (low_edge<x)&(x<high_edge)
|
|
13
|
+
yin = y[mask]
|
|
14
|
+
Y.append(func(yin))
|
|
15
|
+
error.append(np.nanstd(yin)/np.sqrt(len(yin)))
|
|
16
|
+
return np.array(xbins)[:-1],np.array(Y),np.array(error)
|
|
17
|
+
|
|
18
|
+
def nan_clip(*args):
|
|
19
|
+
mask = ~np.any([np.isnan(a) for a in args], axis=0)
|
|
20
|
+
nanless_args = tuple([np.array(a)[mask] for a in args])
|
|
21
|
+
return nanless_args
|
|
22
|
+
|
|
23
|
+
def interpolate2d(data, factor, method='cubic'):
|
|
24
|
+
Nx, Ny = np.array(data).shape
|
|
25
|
+
return RegularGridInterpolator((np.arange(Nx), np.arange(Ny)), data, method=method)(np.mgrid[:Nx-1:1/factor, :Ny-1:1/factor].T).T
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from kbasic.audio.sound import *
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from os import system
|
|
2
|
+
audioDir = '/Users/keyan/code/packages/keyutils/audio/lib/'
|
|
3
|
+
rightplace = '"Caroline Rose - year of the slug - 01 everything in its right place.wav"'
|
|
4
|
+
success = 'success.mp3'
|
|
5
|
+
|
|
6
|
+
def play(file: str):
|
|
7
|
+
system(f"afplay {audioDir}{file}")
|
|
8
|
+
def success(): system(f"afplay {audioDir}success.mp3")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
import inspect
|
|
3
|
+
from tqdm import tqdm
|
|
4
|
+
|
|
5
|
+
def bar(x, total, width: int = 20, border="|", block="▉"):
|
|
6
|
+
full = int((x/total) * width // 1)
|
|
7
|
+
empty = width - full
|
|
8
|
+
bar = border + full*block + empty*" " + border
|
|
9
|
+
return bar
|
|
10
|
+
|
|
11
|
+
@contextmanager
|
|
12
|
+
def redirect_to_tqdm():
|
|
13
|
+
# Store builtin print
|
|
14
|
+
old_print = print
|
|
15
|
+
def new_print(*args, **kwargs):
|
|
16
|
+
# If tqdm.tqdm.write raises error, use builtin print
|
|
17
|
+
try:
|
|
18
|
+
tqdm.write(*args, **kwargs)
|
|
19
|
+
except:
|
|
20
|
+
old_print(*args, ** kwargs)
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
# Globaly replace print with new_print
|
|
24
|
+
inspect.builtins.print = new_print
|
|
25
|
+
yield
|
|
26
|
+
finally:
|
|
27
|
+
inspect.builtins.print = old_print
|
|
28
|
+
|
|
29
|
+
def progress_bar(iterator, **kwargs):
|
|
30
|
+
with redirect_to_tqdm():
|
|
31
|
+
for x in tqdm(iterator, **kwargs):
|
|
32
|
+
yield x
|
|
33
|
+
|
|
34
|
+
def verbose_bar(iterator, verbose, **kwargs):
|
|
35
|
+
return progress_bar(iterator, **kwargs) if verbose else iterator
|
|
36
|
+
|
|
37
|
+
class ProgressBar(tqdm):
|
|
38
|
+
def __init__(self, *args, **kwargs):
|
|
39
|
+
super().__init__(self, *args, **kwargs)
|
|
40
|
+
self.iter = self.initial
|
|
41
|
+
def update(self, iter: int):
|
|
42
|
+
super().update(n=iter-self.iter)
|
|
43
|
+
self.iter = iter
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from subprocess import check_output, DEVNULL
|
|
2
|
+
import numpy as np
|
|
3
|
+
from kbasic.bar import ProgressBar, redirect_to_tqdm
|
|
4
|
+
from kbasic.audio import success
|
|
5
|
+
from pysim.dhybridr.io import dHybridRinput
|
|
6
|
+
from tqdm import tqdm
|
|
7
|
+
from time import sleep
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
|
|
11
|
+
_USERNAME_ = 'x-kgootkin'
|
|
12
|
+
|
|
13
|
+
bad = ['\x1b[31m', '\x1b[34m', '\x1b[m']
|
|
14
|
+
|
|
15
|
+
def parse_shell_output(output):
|
|
16
|
+
match output:
|
|
17
|
+
case str(): return output
|
|
18
|
+
case [x]: return x
|
|
19
|
+
case [x, *_]:
|
|
20
|
+
for i in range(len(output)):
|
|
21
|
+
for b in bad:
|
|
22
|
+
if b in output[i]:
|
|
23
|
+
output[i] = output[i].strip(b)
|
|
24
|
+
return output
|
|
25
|
+
|
|
26
|
+
def system(cmd: str):
|
|
27
|
+
print(cmd)
|
|
28
|
+
command = cmd.split(' ') if type(cmd)==str else cmd
|
|
29
|
+
print(command)
|
|
30
|
+
for i in range(len(command)-1):
|
|
31
|
+
if command[i].startswith('"'):
|
|
32
|
+
print(command[i])
|
|
33
|
+
start_quote = i
|
|
34
|
+
end_quote = i+1
|
|
35
|
+
while not command[end_quote].endswith('"'): end_quote+=1
|
|
36
|
+
command[i] = " ".join(command[start_quote:end_quote+1])
|
|
37
|
+
for j in range(start_quote+1, end_quote+1): del command[j]
|
|
38
|
+
output = parse_shell_output(check_output(command, stderr=DEVNULL).decode().splitlines())
|
|
39
|
+
return output
|
|
40
|
+
|
|
41
|
+
def anvil(cmd: str):
|
|
42
|
+
output = parse_shell_output(check_output(['ssh', 'x-kgootkin@anvil.rcac.purdue.edu', *cmd.split(' ')], stderr=DEVNULL).decode().splitlines())
|
|
43
|
+
return output
|
|
44
|
+
|
|
45
|
+
async def anvil_async(cmd: str): asyncio.to_thread(anvil, cmd)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def anvil_queue(username=_USERNAME_): return anvil(f"squeue -u {username}")
|
|
49
|
+
qs = anvil_queue
|
|
50
|
+
|
|
51
|
+
class AnvilJob:
|
|
52
|
+
def __init__(self, queue_row: str, sep="DISTINCTSEPERATOR"):
|
|
53
|
+
self.sep = sep
|
|
54
|
+
[
|
|
55
|
+
jobid, username, account, name, nodes, cpus, time_limit, status, time
|
|
56
|
+
] = queue_row.split()
|
|
57
|
+
self.jobid = int(jobid)
|
|
58
|
+
self.username = str(username)
|
|
59
|
+
self.account = str(account)
|
|
60
|
+
self.name = str(name)
|
|
61
|
+
self.nodes = int(nodes)
|
|
62
|
+
self.cpus = int(cpus)
|
|
63
|
+
self.time_limit = str(time_limit)
|
|
64
|
+
self.status = str(status)
|
|
65
|
+
self.time = str(time)
|
|
66
|
+
|
|
67
|
+
def __repr__(self): return "-"*30 + f"\n{self.name}: {self.status}\n\t{self.time}/{self.time_limit}"
|
|
68
|
+
|
|
69
|
+
def update(self, input=True, iter=True):
|
|
70
|
+
match input, iter:
|
|
71
|
+
case True, True:
|
|
72
|
+
x = anvil(f"cat /anvil/scratch/{self.username}/sims/{self.name}/input/input; echo {self.sep}; ls /anvil/scratch/{self.username}/sims/{self.name}/Output/Fields/Magnetic/Total/x/")
|
|
73
|
+
input_lines = "\n".join(x).split(self.sep)[0].split('\n')
|
|
74
|
+
self.input = dHybridRinput(input_lines)
|
|
75
|
+
self.iter = int(x[-1][5:-3])
|
|
76
|
+
case True, False:
|
|
77
|
+
self.input = dHybridRinput(anvil(f"cat /anvil/scratch/{self.username}/sims/{self.name}/input/input"))
|
|
78
|
+
case False, True:
|
|
79
|
+
self.iter = int(anvil(f"ls /anvil/scratch/{self.username}/sims/{self.name}/Output/Fields/Magnetic/Total/x/")[-1][5:-3])
|
|
80
|
+
|
|
81
|
+
def get_anvil_jobs(username=_USERNAME_):
|
|
82
|
+
q = anvil(f"squeue -u {username}")
|
|
83
|
+
if type(q)==str: return []
|
|
84
|
+
return [AnvilJob(x) for x in q[1:]]
|
|
85
|
+
|
|
86
|
+
async def get_anvil_jobs_async(username=_USERNAME_):
|
|
87
|
+
q = await asyncio.to_thread(anvil, f"squeue -u {username}")
|
|
88
|
+
if type(q)==str: return []
|
|
89
|
+
return [AnvilJob(x) for x in q[1:]]
|
|
90
|
+
|
|
91
|
+
async def get_anvil_sim_iter(name: str):
|
|
92
|
+
iter = int(await asyncio.to_thread(anvil, f"ls /anvil/scratch/{username}/sims/{name}/Output/Fields/Magnetic/Total/x/")[-1][5:-3])
|
|
93
|
+
return iter
|
|
94
|
+
|
|
95
|
+
class AnvilQueue:
|
|
96
|
+
def __init__(self, username=_USERNAME_):
|
|
97
|
+
self.username = username
|
|
98
|
+
@property
|
|
99
|
+
def jobs(self): return get_anvil_jobs()
|
|
100
|
+
def monitor(self, sep="DISTINCTSEPERATOR"):
|
|
101
|
+
with redirect_to_tqdm():
|
|
102
|
+
js = self.jobs
|
|
103
|
+
print(js, self.jobs)
|
|
104
|
+
pbars=[tqdm(position=i, leave=True, desc=js[i].name, postfix=f"{js[i].time}/{js[i].time_limit}") for i in range(len(js))]
|
|
105
|
+
for i in range(len(js)):
|
|
106
|
+
j = js[i]
|
|
107
|
+
j.update()
|
|
108
|
+
pbars[i].total = j.input.niter
|
|
109
|
+
pbars[i].n = j.iter
|
|
110
|
+
pbars[i].refresh()
|
|
111
|
+
while len(js:=self.jobs) > 0:
|
|
112
|
+
x = anvil(f";echo {sep};".join([f"ls /anvil/scratch/{self.username}/sims/{js[i].name}/Output/Fields/Magnetic/Total/x/" for i in range(len(js))]))
|
|
113
|
+
sep_ind = 0
|
|
114
|
+
for i in range(len(js)):
|
|
115
|
+
if i==range(len(js))[-1]:
|
|
116
|
+
sep_ind=0
|
|
117
|
+
else:
|
|
118
|
+
while x[sep_ind]!=sep: sep_ind+=1
|
|
119
|
+
it = int(x[sep_ind-1][5:-3])
|
|
120
|
+
pbars[i].n = it
|
|
121
|
+
pbars[i].postfix = f"{js[i].time}/{js[i].time_limit}"
|
|
122
|
+
pbars[i].refresh()
|
|
123
|
+
sep_ind+=1
|
|
124
|
+
def sound_when_running(self, i=0):
|
|
125
|
+
while len(js:=self.jobs) > 0:
|
|
126
|
+
j = js[i]
|
|
127
|
+
if j.status=='R': return success()
|
|
128
|
+
def sound_when_queue_clear(self):
|
|
129
|
+
while len(js:=self.jobs)>0: sleep(20)
|
|
130
|
+
return success()
|
|
131
|
+
def sound_monitor(self):
|
|
132
|
+
self.sound_when_running()
|
|
133
|
+
self.sound_when_queue_clear()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from numpy import ndarray, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float16, float32, float64, longdouble, complex64, complex128, clongdouble
|
|
2
|
+
|
|
3
|
+
class Number:
|
|
4
|
+
types: list = [
|
|
5
|
+
int, int8, uint8, int16, uint16, int32, uint32, int64, uint64,
|
|
6
|
+
float, float16, float32, float64, longdouble,
|
|
7
|
+
complex, complex64, complex128, clongdouble
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
class Iterable:
|
|
11
|
+
types: list = [
|
|
12
|
+
list, set, dict, tuple, ndarray
|
|
13
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
def yesno(prompt: str):
|
|
2
|
+
"""
|
|
3
|
+
prompt the user to either reply yes or no
|
|
4
|
+
:param prompt: the yes/no question to be answered
|
|
5
|
+
:return: True if yes False if no
|
|
6
|
+
"""
|
|
7
|
+
response = input(prompt).lower()
|
|
8
|
+
if 'y' in response and not 'n' in response:
|
|
9
|
+
return True
|
|
10
|
+
elif 'n' in response and not 'y' in response:
|
|
11
|
+
return False
|
|
12
|
+
else:
|
|
13
|
+
def retry_yesno():
|
|
14
|
+
retry_prompt = "Sorry I couldn't read that please respond with yes or no\n" + prompt
|
|
15
|
+
retry_response = input(retry_prompt).lower()
|
|
16
|
+
if 'y' in retry_response and not 'n' in retry_response:
|
|
17
|
+
return True
|
|
18
|
+
elif 'n' in retry_response and not 'y' in retry_response:
|
|
19
|
+
return False
|
|
20
|
+
else:
|
|
21
|
+
raise ValueError("need a response with either y or n in it.")
|
|
22
|
+
|
|
23
|
+
return retry_yesno()
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
|
|
2
|
+
# >-|===|> Imports <|===|-<
|
|
3
|
+
# !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
|
|
4
|
+
from numpy import array, ndarray, cos, arccos, sin, arcsin, sqrt, float64, float32, exp, pi, complex128
|
|
5
|
+
from typing import Self
|
|
6
|
+
from collections.abc import Generator
|
|
7
|
+
# !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
|
|
8
|
+
# >-|===|> Definitions <|===|-<
|
|
9
|
+
# !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
|
|
10
|
+
class Number:
|
|
11
|
+
types: list = [float, float64, float32, int, complex, complex128]
|
|
12
|
+
class Iterable:
|
|
13
|
+
types: list = [list, ndarray, tuple, Generator]
|
|
14
|
+
# !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
|
|
15
|
+
# >-|===|> Functions <|===|-<
|
|
16
|
+
# !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
|
|
17
|
+
def hamilton_product(q1, q2):
|
|
18
|
+
a1, b1, c1, d1 = q1
|
|
19
|
+
a2, b2, c2, d2 = q2
|
|
20
|
+
return array([
|
|
21
|
+
a1*a2 - b1*b2 - c1*c2 - d1*d2,
|
|
22
|
+
a1*b2 + b1*a2 + c1*d2 - d1*c2,
|
|
23
|
+
a1*c2 - b1*d2 + c1*a2 + d1*b2,
|
|
24
|
+
a1*d2 + b1*c2 - c1*b2 + d1*a2
|
|
25
|
+
], dtype=float)
|
|
26
|
+
def organize_components(components) -> tuple[float|int]:
|
|
27
|
+
match components:
|
|
28
|
+
case (x, *_) if type(x) in Number.types: return components
|
|
29
|
+
case (x,) if type(x) in Iterable.types: return tuple(x)
|
|
30
|
+
case (Generator(),): return tuple(components[0])
|
|
31
|
+
case (Vector(),): return components[0].components
|
|
32
|
+
case _: raise TypeError(f"{components} cannot be matched")
|
|
33
|
+
# !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
|
|
34
|
+
# >-|===|> Classes <|===|-<
|
|
35
|
+
# !==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==!==
|
|
36
|
+
class Vector:
|
|
37
|
+
def __init__(self, *components) -> None:
|
|
38
|
+
self.components = organize_components(components)
|
|
39
|
+
self.ndims = self.dim = self.dimensions = len(components) #i always forget which one i choose :)
|
|
40
|
+
def __repr__(self) -> str:
|
|
41
|
+
return str(self.components)
|
|
42
|
+
def __len__(self) -> int:
|
|
43
|
+
return self.dimensions
|
|
44
|
+
def __abs__(self) -> Number:
|
|
45
|
+
return sqrt(sum(array(self.components)**2))
|
|
46
|
+
def __iter__(self) -> Self:
|
|
47
|
+
self._index = 0
|
|
48
|
+
return self
|
|
49
|
+
def __next__(self) -> Number:
|
|
50
|
+
try:
|
|
51
|
+
value = self.components[self._index]
|
|
52
|
+
self._index += 1
|
|
53
|
+
return value
|
|
54
|
+
except IndexError: raise StopIteration
|
|
55
|
+
def __add__(self, other) -> Self:
|
|
56
|
+
match other:
|
|
57
|
+
case Vector(): return type(self)(xs+xo for xs, xo in zip(self.components, other.components))
|
|
58
|
+
case x if type(x) in Number.types: return type(self)(x+other for x in self.components)
|
|
59
|
+
case x if type(x) in Iterable.types: return type(self)(xs+xo for xs, xo in zip(self.components, other))
|
|
60
|
+
def __sub__(self, other) -> Self:
|
|
61
|
+
match other:
|
|
62
|
+
case x if type(x) in Number.types: return type(self)(x-other for x in self.components)
|
|
63
|
+
case x if type(x) in Iterable.types: return type(self)(xs-xo for xs, xo in zip(self.components, other))
|
|
64
|
+
case Vector(): return type(self)(xs-xo for xs, xo in zip(self.components, other.components))
|
|
65
|
+
def __mul__(self, other) -> Self|Number:
|
|
66
|
+
match other:
|
|
67
|
+
case Vector(): return sum([c1*c2 for c1,c2 in zip(self.components, other.components)])
|
|
68
|
+
case x if type(x) in Number.types: return type(self)(c*other for c in self.components)
|
|
69
|
+
def __truediv__(self, other) -> Self:
|
|
70
|
+
match other:
|
|
71
|
+
case x if type(x) in Number.types: return type(self)(c/other for c in self.components)
|
|
72
|
+
def __floordiv__(self, other) -> Self:
|
|
73
|
+
match other:
|
|
74
|
+
case x if type(x) in Number.types: return type(self)(c//other for c in self.components)
|
|
75
|
+
def __radd__(self, other) -> Self:
|
|
76
|
+
match other:
|
|
77
|
+
case 0: return self
|
|
78
|
+
case _: return self.__add__(other)
|
|
79
|
+
def __rsub__(self, other) -> Self:
|
|
80
|
+
match other:
|
|
81
|
+
case 0: return self
|
|
82
|
+
case _: return self.__sub__(other)
|
|
83
|
+
def __rmul__(self, other) -> Self|Number:
|
|
84
|
+
match other:
|
|
85
|
+
case 0: return self
|
|
86
|
+
case _: return self.__mul__(other)
|
|
87
|
+
def __rtruediv__(self, other) -> Self:
|
|
88
|
+
match other:
|
|
89
|
+
case 0: return self
|
|
90
|
+
case _: return self.__truediv__(other)
|
|
91
|
+
def __rfloordiv__(self, other) -> Self:
|
|
92
|
+
match other:
|
|
93
|
+
case 0: return self
|
|
94
|
+
case _: return self.__floordiv__(other)
|
|
95
|
+
|
|
96
|
+
class Norm(Vector):
|
|
97
|
+
def __init__(self, *components):
|
|
98
|
+
components: tuple = organize_components(components)
|
|
99
|
+
Vector.__init__(self, *components)
|
|
100
|
+
gen = (x/abs(self) for x in self.components)
|
|
101
|
+
Vector.__init__(self, gen)
|
|
102
|
+
class Quaternion(Norm):
|
|
103
|
+
def __init__(self, angle, axis):
|
|
104
|
+
q0 = cos(angle/2)
|
|
105
|
+
self.axis = sin(angle/2)*Vector(axis)
|
|
106
|
+
self.axis *= sqrt(1-q0**2)/abs(self.axis)
|
|
107
|
+
Norm.__init__(self, q0, *self.axis.components)
|
|
108
|
+
|
|
109
|
+
class R2(Vector):
|
|
110
|
+
def __init__(self, *components):
|
|
111
|
+
self.components: tuple[Number] = organize_components(components)
|
|
112
|
+
self.x, self.y = self.components
|
|
113
|
+
Vector.__init__(self, *components)
|
|
114
|
+
self.magnitude = abs(self)
|
|
115
|
+
self.direction = Norm(*self.components) if self.magnitude>0 else None
|
|
116
|
+
def rotate(self, angle):
|
|
117
|
+
r = self.x + 1j*self.y
|
|
118
|
+
r_rotated = r * exp(1j*angle)
|
|
119
|
+
self.__init__(r_rotated.real, r_rotated.imag)
|
|
120
|
+
class R3(Vector):
|
|
121
|
+
def __init__(self, *components):
|
|
122
|
+
self.components: tuple[Number] = organize_components(components)
|
|
123
|
+
self.x, self.y, self.z = self.components
|
|
124
|
+
Vector.__init__(self, *self.components)
|
|
125
|
+
self.magnitude = abs(self)
|
|
126
|
+
self.direction = Norm(*self.components) if self.magnitude>0 else None
|
|
127
|
+
def from_spherical(radius: Number, azimuth: Number, latitude: Number) -> Self:
|
|
128
|
+
x = radius * cos(azimuth) * sin(latitude)
|
|
129
|
+
y = radius * sin(azimuth) * sin(latitude)
|
|
130
|
+
z = radius * cos(latitude)
|
|
131
|
+
return R3(x, y, z)
|
|
132
|
+
def rotate(self, angle, axis):
|
|
133
|
+
axis = array(axis)
|
|
134
|
+
qs = array([0, *self.components])
|
|
135
|
+
rotation = array(Norm([cos(angle/2), *(axis*sin(angle/2))]).components)
|
|
136
|
+
rotation_inverse = array([rotation[0], *-rotation[1:]])
|
|
137
|
+
rotated_vector = hamilton_product(hamilton_product(rotation, qs), rotation_inverse)[1:]
|
|
138
|
+
self.__init__(*rotated_vector)
|
|
139
|
+
|
|
140
|
+
class Matrix:
|
|
141
|
+
def __init__(self, array):
|
|
142
|
+
self.array = array
|
|
143
|
+
def __mul__(self, other):
|
|
144
|
+
match other:
|
|
145
|
+
case Vector():
|
|
146
|
+
res = [[self.array[i,j]*other.components[i] for i in range(len(other))] for j in range(len(other))]
|
kbasic-0.1.0/pyproject.toml
DELETED
|
File without changes
|
|
File without changes
|