ducky-python-module 0.1.5__tar.gz → 1.0.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ducky-python-module
3
- Version: 0.1.5
3
+ Version: 1.0.1
4
4
  Summary: Ducky Python module
5
5
  Author: Tom
6
6
  License: Creative Commons Attribution-NonCommercial 4.0 International Public
@@ -345,7 +345,7 @@ Requires-Dist: numpy>=1.26.0
345
345
  Dynamic: license-file
346
346
 
347
347
  # Ducky-Python-Module
348
- A collection of tools for python
348
+ A collection of QOL and specialised tools for python
349
349
  Still in progress and will be releasing fully soon
350
350
  Credit:
351
351
  Turtle library has been used and modified. I did not make it and I did include the text from the original library explaining rights to the Turtle program which I am not allowed to remove.
@@ -1,5 +1,5 @@
1
1
  # Ducky-Python-Module
2
- A collection of tools for python
2
+ A collection of QOL and specialised tools for python
3
3
  Still in progress and will be releasing fully soon
4
4
  Credit:
5
5
  Turtle library has been used and modified. I did not make it and I did include the text from the original library explaining rights to the Turtle program which I am not allowed to remove.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ducky-python-module"
7
- version = "0.1.5"
7
+ version = "1.0.1" #changed version number already. Last pypi release uses 1.0.0
8
8
  description = "Ducky Python module"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -0,0 +1,2 @@
1
+ from .main import *
2
+ from .backend import set_backend
@@ -0,0 +1,16 @@
1
+ import numpy as _numpy
2
+ _backend = _numpy
3
+ def set_backend(name: str):
4
+ global _backend
5
+ if name == "numpy":
6
+ import numpy
7
+ _backend = numpy
8
+ elif name == "cupy":
9
+ import cupy
10
+ _backend = cupy
11
+
12
+ else:
13
+ raise ValueError("Backend must be numpy or cupy")
14
+
15
+ def backend():
16
+ return _backend
@@ -1,16 +1,16 @@
1
- import random, time, os, nltk, tkinter as TK, types, math, inspect, sys, requests, json, numpy, ast, logging
1
+ import random, time, os, nltk, tkinter as TK, types, math, inspect, sys, requests, json, ast, logging, abc
2
2
  from os.path import isfile, split, join
3
3
  from copy import deepcopy
4
4
  from tkinter import simpledialog
5
5
  from turtle import Turtle
6
6
  from nltk.tokenize import word_tokenize
7
+ from ducky.backend import backend
7
8
  from nltk.stem import LancasterStemmer
8
- logging.basicConfig(level=logging.INFO)
9
9
 
10
10
  #matrix functions
11
11
 
12
12
  def matrix(x,y,fill=0):
13
- mat = [[fill for i in range(x)] for a in range(y)]
13
+ mat = [[fill for _ in range(x)] for _ in range(y)]
14
14
  return mat
15
15
 
16
16
  def draw(mat,x,y,val=1,errors=True): #draws a certain value to all specified positions
@@ -72,7 +72,34 @@ def render(mat):
72
72
  print("------------------------------------------------------")
73
73
 
74
74
  #my functions
75
-
75
+
76
+ def download_nltk():
77
+ nltk.download('punkt', quiet=True)
78
+
79
+ def download_to_path(url, path, file_name, quiet=False):
80
+ try:
81
+ r = requests.get(url)
82
+ r.raise_for_status()
83
+
84
+ with open(f"{path}/{file_name}", "wb") as f:
85
+ f.write(r.content)
86
+
87
+ except requests.exceptions.RequestException as e:
88
+ if not quiet:
89
+ print("Download failed:", e)
90
+
91
+ def low_ram_download(url,path,file_name,quiet=False):
92
+ try:
93
+ with requests.get(url, stream=True) as r:
94
+ r.raise_for_status()
95
+ with open(f"{path}/{file_name}", "wb") as f:
96
+ for chunk in r.iter_content(chunk_size=8192):
97
+ if chunk:
98
+ f.write(chunk)
99
+ except requests.exceptions.RequestException as e:
100
+ if not quiet:
101
+ print("Download failed:", e)
102
+
76
103
  def rand_int(minimum,maximum):
77
104
  return random.randint(minimum,maximum)
78
105
 
@@ -91,7 +118,7 @@ def current_minute():
91
118
  def current_second():
92
119
  return time.localtime()[5]
93
120
 
94
- def tokenize(string):
121
+ def tokenise(string):
95
122
  tokens = word_tokenize(string)
96
123
  return tokens
97
124
 
@@ -100,16 +127,16 @@ def stem(string):
100
127
  return lancaster.stem(string)
101
128
 
102
129
  def array(lst):
103
- return numpy.array(lst)
130
+ return backend().array(lst)
104
131
 
105
132
  def dot_product(mat1,mat2):
106
- return numpy.dot(mat1,mat2)
133
+ return backend().dot(mat1,mat2)
107
134
 
108
135
  def scale_matrices(mat,val):
109
136
  return mat*val
110
137
 
111
138
  def average_matrices(mat):
112
- return numpy.average(mat)
139
+ return backend().average(mat)
113
140
 
114
141
  def calc(param):
115
142
  try:
@@ -397,7 +424,7 @@ class Chatbot:
397
424
 
398
425
  def reply(self,reply_input):
399
426
  score = [0, 0, 0, 0, 0]
400
- tokens = tokenize(reply_input)
427
+ tokens = tokenise(reply_input)
401
428
  stemmed_words = []
402
429
  for token in tokens:
403
430
  stemmed_words.append((stem(token)))
@@ -432,6 +459,291 @@ class Chatbot:
432
459
  return 1
433
460
  return None
434
461
 
462
+
463
+ #AI class templates
464
+
465
+
466
+ import abc
467
+ import random
468
+
469
+ import ducky
470
+ import numpy
471
+ import numpy
472
+
473
+
474
+ class ActivationFunctions:
475
+ TANH = 0
476
+
477
+ @staticmethod
478
+ def __tanh(x: numpy.ndarray):
479
+ return numpy.tanh(x)
480
+
481
+ @staticmethod
482
+ def __tanh_derived(x: numpy.ndarray):
483
+ return 1 - (numpy.tanh(x) ** 2)
484
+
485
+ @staticmethod
486
+ def use_tanh():
487
+ return ActivationFunctions.TANH, ActivationFunctions.__tanh, ActivationFunctions.__tanh_derived
488
+
489
+
490
+ class CostFunctions:
491
+ MSE = 0
492
+
493
+ @staticmethod
494
+ def __mse(actual: numpy.ndarray, prediction: numpy.ndarray):
495
+ return numpy.mean(numpy.power(actual - prediction, 2))
496
+
497
+ @staticmethod
498
+ def __mse_derived(actual: numpy.ndarray, prediction: numpy.ndarray):
499
+ return (2 * (prediction - actual)) / actual.size
500
+
501
+ @staticmethod
502
+ def use_mse():
503
+ return CostFunctions.MSE, CostFunctions.__mse, CostFunctions.__mse_derived
504
+
505
+
506
+ class Layer(abc.ABC):
507
+ def __init__(self):
508
+ self.input = None
509
+ self.output = None
510
+
511
+ @abc.abstractmethod
512
+ def feed_forward(self, input_values: numpy.ndarray):
513
+ pass
514
+
515
+ @abc.abstractmethod
516
+ def back_propagate(self, output_error: numpy.ndarray, learning_rate: float):
517
+ pass
518
+
519
+ @abc.abstractmethod
520
+ def copy(self):
521
+ pass
522
+
523
+
524
+ class FullyConnectedLayer(Layer):
525
+ def __init__(self, input_size: int, output_size: int):
526
+ Layer.__init__(self)
527
+ random_bound = 1.0 / numpy.sqrt(input_size)
528
+ self.weights = numpy.random.uniform(-random_bound, random_bound, (input_size, output_size))
529
+ self.bias = numpy.zeros((1, output_size))
530
+ self.input_size = input_size
531
+ self.output_size = output_size
532
+
533
+ def feed_forward(self, input_values):
534
+ self.input = input_values
535
+ self.output = numpy.dot(self.input, self.weights) + self.bias
536
+ return self.output
537
+
538
+ def back_propagate(self, output_error, learning_rate):
539
+ input_error = numpy.dot(output_error, self.weights.T)
540
+ weights_error = numpy.dot(self.input.T, output_error)
541
+ self.weights -= (learning_rate * weights_error)
542
+ self.bias -= (learning_rate * numpy.mean(output_error, axis=0, keepdims=True))
543
+ return input_error
544
+
545
+ def copy(self):
546
+ copied_layer = FullyConnectedLayer(self.input_size, self.output_size)
547
+ weights = self.weights.copy()
548
+ bias = self.bias.copy()
549
+ copied_layer.weights = weights
550
+ copied_layer.bias = bias
551
+ return copied_layer
552
+
553
+
554
+ class ActivationLayer(Layer):
555
+ def __init__(self, activation_id, activation, activation_derived):
556
+ Layer.__init__(self)
557
+ self.activation_id = activation_id
558
+ self.activation = activation
559
+ self.activation_derived = activation_derived
560
+
561
+ def feed_forward(self, input_values):
562
+ self.input = input_values
563
+ self.output = self.activation(self.input)
564
+ return self.output
565
+
566
+ def back_propagate(self, output_error, learning_rate):
567
+ return self.activation_derived(self.input) * output_error
568
+
569
+ def copy(self):
570
+ copied_layer = ActivationLayer(self.activation_id, self.activation, self.activation_derived)
571
+ return copied_layer
572
+
573
+
574
+ class NeuralNetwork:
575
+ def __init__(self, cost_function_id, cost_function, cost_function_derived):
576
+ self.layers = []
577
+ self.cost_function_id = cost_function_id
578
+ self.cost_function = cost_function
579
+ self.cost_function_derived = cost_function_derived
580
+
581
+ def add_layer(self, layer):
582
+ self.layers.append(layer)
583
+
584
+ def predict(self, input_data):
585
+ output = input_data
586
+ for layer in self.layers:
587
+ output = layer.feed_forward(output)
588
+ return output
589
+
590
+ def train(self, train_input, train_output, n_epochs, batch_size, learning_rate):
591
+ for epoch in range(n_epochs):
592
+ batch_index = 0
593
+ while batch_index < train_input.shape[0]:
594
+ if batch_index + batch_size < train_input.shape[0]:
595
+ output = train_input[batch_index:batch_index + batch_size, ]
596
+ actual = train_output[batch_index:batch_index + batch_size, ]
597
+ else:
598
+ output = train_input[batch_index:, ]
599
+ actual = train_output[batch_index:, ]
600
+ for layer in self.layers:
601
+ output = layer.feed_forward(output)
602
+ cost = self.cost_function(actual, output)
603
+ error = self.cost_function_derived(actual, output)
604
+ for layer in reversed(self.layers):
605
+ error = layer.back_propagate(error, learning_rate)
606
+ print(f"Epoch {epoch + 1}/{n_epochs} | Batch = {(batch_index + 1) * batch_size} | Cost = {cost}")
607
+ batch_index += batch_size
608
+
609
+ def save(self, file_path):
610
+ with open(f"{file_path}.nn", "w") as save_file:
611
+ save_file.write(f"{self.cost_function_id}\n")
612
+ for index, layer in enumerate(self.layers):
613
+ if isinstance(layer, FullyConnectedLayer):
614
+ save_file.write(f"{index}|F|{layer.weights.shape}|{layer.bias.shape}\n")
615
+ numpy.save(f"{file_path}_{index}_w", layer.weights)
616
+ numpy.save(f"{file_path}_{index}_b", layer.bias)
617
+ elif isinstance(layer, ActivationLayer):
618
+ save_file.write(f"{index}|A|{layer.activation_id}\n")
619
+
620
+ @staticmethod
621
+ def load_from_file(file_path):
622
+ with open(f"{file_path}.nn", "r") as load_file:
623
+ lines = load_file.readlines()
624
+ cost_function_id = int(lines[0])
625
+ if cost_function_id == CostFunctions.MSE:
626
+ neural_network = NeuralNetwork(*CostFunctions.use_mse())
627
+ else:
628
+ neural_network = NeuralNetwork(*CostFunctions.use_mse())
629
+ for line in lines[1:]:
630
+ if line != "":
631
+ parts = line.split("|")
632
+ if parts[1] == "F":
633
+ weights = numpy.load(f"{file_path}_{int(parts[0])}_w.npy")
634
+ bias = numpy.load(f"{file_path}_{int(parts[0])}_b.npy")
635
+ layer = FullyConnectedLayer(weights.shape[0], weights.shape[1])
636
+ layer.weights = weights
637
+ layer.bias = bias
638
+ neural_network.add_layer(layer)
639
+ elif parts[1] == "A":
640
+ activation_id = int(parts[2])
641
+ if activation_id == ActivationFunctions.TANH:
642
+ neural_network.add_layer(ActivationLayer(*ActivationFunctions.use_tanh()))
643
+ else:
644
+ neural_network.add_layer(ActivationLayer(*ActivationFunctions.use_tanh()))
645
+ return neural_network
646
+
647
+ def copy_network(self):
648
+ copied_network = NeuralNetwork(self.cost_function_id, self.cost_function, self.cost_function_derived)
649
+ for layer in self.layers:
650
+ copied_network.add_layer(layer.copy())
651
+ return copied_network
652
+
653
+
654
+ class Agent:
655
+ def __init__(self):
656
+ self.gamma = 0.99
657
+
658
+
659
+ class QTable:
660
+ def __init__(self, state, actions):
661
+ self.table = numpy.zeros((state,actions))
662
+ self.actions = actions
663
+ self.state = state
664
+
665
+ def update_table(self, state_row, action, value):
666
+ self.table[state_row, action] += value
667
+
668
+ def get_action(self, state_row):
669
+ return numpy.argmax(self.table[state_row])
670
+
671
+
672
+ class QTableAgent(Agent):
673
+ def __init__(self, table, num_possible_values):
674
+ Agent.__init__(self)
675
+ self.table = table
676
+ self.num_possible_values = num_possible_values
677
+ self.epsilon = 1
678
+
679
+ def state_to_row(self,state):
680
+ return int("".join([str(s) for s in state.astype(int)]), self.num_possible_values)
681
+
682
+ def predict(self, state):
683
+ self.epsilon *= 0.99
684
+ if random.random() > self.epsilon:
685
+ return self.table.get_action(self.state_to_row(state))
686
+ else:
687
+ return random.randint(0,self.table.actions-1)
688
+
689
+ def train(self, state, action, reward):
690
+ self.table.update_table(self.state_to_row(state), action, reward)
691
+
692
+
693
+ class DeepQAgent(Agent):
694
+ def __init__(self, layer_sizes):
695
+ Agent.__init__(self)
696
+ self.memory = Memory()
697
+ self.counter = 0
698
+ try:
699
+ self.neural_network = NeuralNetwork.load_from_file("deepq-nn")
700
+ except FileNotFoundError:
701
+ self.neural_network = NeuralNetwork(*CostFunctions.use_mse())
702
+ for i, size in enumerate(layer_sizes[:-1]):
703
+ self.neural_network.add_layer(FullyConnectedLayer(size, layer_sizes[i+1]))
704
+ self.neural_network.add_layer(ActivationLayer(*ActivationFunctions.use_tanh()))
705
+
706
+ def predict(self, state):
707
+ return int(self.neural_network.predict(state)[0].argmax())
708
+
709
+ def train(self, discount_rate):
710
+ training_input_data = []
711
+ training_output_data = []
712
+ for i in range(len(self.memory.states)):
713
+ prediction = self.neural_network.predict(self.memory.states[i])
714
+ prediction[0] += self.memory.rewards[i - 1]
715
+ if i > 0:
716
+ prediction[0] += self.memory.rewards[i-1] * discount_rate
717
+ training_input_data = numpy.array(training_input_data)
718
+ training_output_data = numpy.array(training_output_data)
719
+ self.neural_network.train(training_input_data, training_output_data, n_epochs=500, batch_size=10, learning_rate=0.1)
720
+ if self.counter >= 10:
721
+ self.neural_network.save("deepq-nn")
722
+ self.counter = 0
723
+ else:
724
+ self.counter += 1
725
+
726
+ def update_memory(self, state, action, reward):
727
+ self.memory.update(state, action, reward)
728
+
729
+
730
+ class Memory:
731
+ def __init__(self):
732
+ self.states = []
733
+ self.actions = []
734
+ self.rewards = []
735
+
736
+ def update(self, state, action, reward):
737
+ self.states.append(state)
738
+ self.actions.append(action)
739
+ self.rewards.append(reward)
740
+
741
+ def clear(self):
742
+ self.states = []
743
+ self.actions = []
744
+ self.rewards = []
745
+
746
+
435
747
  #turtlel
436
748
 
437
749
  def dots(turt_name, number, random_colours=True, colours=None, minimum=20, maximum=100):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ducky-python-module
3
- Version: 0.1.5
3
+ Version: 1.0.1
4
4
  Summary: Ducky Python module
5
5
  Author: Tom
6
6
  License: Creative Commons Attribution-NonCommercial 4.0 International Public
@@ -345,7 +345,7 @@ Requires-Dist: numpy>=1.26.0
345
345
  Dynamic: license-file
346
346
 
347
347
  # Ducky-Python-Module
348
- A collection of tools for python
348
+ A collection of QOL and specialised tools for python
349
349
  Still in progress and will be releasing fully soon
350
350
  Credit:
351
351
  Turtle library has been used and modified. I did not make it and I did include the text from the original library explaining rights to the Turtle program which I am not allowed to remove.
@@ -2,6 +2,7 @@ LICENSE.txt
2
2
  README.md
3
3
  pyproject.toml
4
4
  src/ducky/__init__.py
5
+ src/ducky/backend.py
5
6
  src/ducky/main.py
6
7
  src/ducky_python_module.egg-info/PKG-INFO
7
8
  src/ducky_python_module.egg-info/SOURCES.txt
@@ -1,7 +0,0 @@
1
- from .main import *
2
-
3
- try:
4
- import nltk
5
- nltk.download('punkt', quiet=True)
6
- except Exception:
7
- pass