From 4b118aef69a5bae31e7f411c1d202f9acdd7cdda Mon Sep 17 00:00:00 2001 From: Carlos Sevilla Salcedo Date: Mon, 31 Oct 2022 12:26:41 +0100 Subject: [PATCH 1/9] Reupload integral plugin --- aidesign/DataProcessing/plugins/integral.py | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 aidesign/DataProcessing/plugins/integral.py diff --git a/aidesign/DataProcessing/plugins/integral.py b/aidesign/DataProcessing/plugins/integral.py new file mode 100644 index 00000000..58d04beb --- /dev/null +++ b/aidesign/DataProcessing/plugins/integral.py @@ -0,0 +1,47 @@ +from numpy import trapz as model +from aidesign._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"Integral": "default", + "integral": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "Other"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + +class Integral(DataProcessingT): + """ + Calculate integral of array using the composite trapezoidal rule + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + # self.proc = model() + + def configure(self, config: dict): + """Sets and parses plugin configurations options + :param config: dict of internal tags set in the XML config file + """ + # super().configure(config) + return + + def set_data_in(self, data_in): + """Sets and parses incoming data + :param data_in: saves data as class variable + expected type: aidesign.Data.Data_core.Data + """ + super().set_data_in(data_in) + + def fit(self): + # self.cleaned_options = self._clean_solver_options() + # self.proc.set_params(**cleaned_options) + # self.proc.fit(self.X) + return + + def transform(self,data): + data.append_data_column("X", pd.DataFrame(model(self.X))) + return data \ No newline at end of file From cf28a8bb6b0871569e8339f7755973f183df8f1a Mon Sep 17 00:00:00 2001 From: Carlos Sevilla Salcedo Date: Mon, 31 Oct 2022 18:00:11 +0100 Subject: [PATCH 2/9] argopt plugin --- aidesign/DataProcessing/plugins/argopt.py | 52 +++++++++++++++++++ .../examples/xml_files/crystalDesign_v2.xml | 33 ++++++++++++ .../examples/xml_files/crystalDesign_v3.xml | 33 ++++++++++++ aidesign/examples/xml_files/scalar_demo.xml | 6 ++- run_pipeline.py | 8 +-- 5 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 aidesign/DataProcessing/plugins/argopt.py create mode 100644 aidesign/examples/xml_files/crystalDesign_v2.xml create mode 100644 aidesign/examples/xml_files/crystalDesign_v3.xml diff --git a/aidesign/DataProcessing/plugins/argopt.py b/aidesign/DataProcessing/plugins/argopt.py new file mode 100644 index 00000000..e2c48e12 --- /dev/null +++ b/aidesign/DataProcessing/plugins/argopt.py @@ -0,0 +1,52 @@ +from distutils.command.config import config +from numpy import argmin, argmax +from aidesign._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"argopt": "default", + "argmax": "alias", + "argmin": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "Other"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {'min/max': "str"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + +class argopt(DataProcessingT): + """ + Calculate the optimum argument + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + # self.proc = model() + + def configure(self, config: dict): + """Sets and parses plugin configurations options + :param config: dict of internal tags set in the XML config file + """ + # super().configure(config) + self.config = config + + def set_data_in(self, data_in): + """Sets and parses incoming data + :param data_in: saves data as class variable + expected type: aidesign.Data.Data_core.Data + """ + super().set_data_in(data_in) + + def fit(self): + self.cleaned_options = self._clean_solver_options() + # self.proc.set_params(**cleaned_options) + # self.proc.fit(self.X) + return + + def transform(self,data): + if config['min/max'] == 'max': + data.append_data_column("X", pd.DataFrame(argmax(self.X))) + else: + data.append_data_column("X", pd.DataFrame(argmin(self.X))) + return data \ No newline at end of file diff --git a/aidesign/examples/xml_files/crystalDesign_v2.xml b/aidesign/examples/xml_files/crystalDesign_v2.xml new file mode 100644 index 00000000..4884286e --- /dev/null +++ b/aidesign/examples/xml_files/crystalDesign_v2.xml @@ -0,0 +1,33 @@ + + + + + + + [(350.0,50),0,{}] + + + + + + + + + [(350.0,350.0),2,{0:'d0-u2'}] + + + + X + + + + + + + + + + [(350.0,650),1,{2:'d2-u1'}] + + + diff --git a/aidesign/examples/xml_files/crystalDesign_v3.xml b/aidesign/examples/xml_files/crystalDesign_v3.xml new file mode 100644 index 00000000..d8152bef --- /dev/null +++ b/aidesign/examples/xml_files/crystalDesign_v3.xml @@ -0,0 +1,33 @@ + + + + + + + [(350.0,50),0,{}] + + + + + + + + + [(350.0,350.0),2,{0:'d0-u2'}] + + + + str + + + + + + + + + + [(350.0,650),1,{2:'d2-u1'}] + + + diff --git a/aidesign/examples/xml_files/scalar_demo.xml b/aidesign/examples/xml_files/scalar_demo.xml index e2e7e318..e3768515 100644 --- a/aidesign/examples/xml_files/scalar_demo.xml +++ b/aidesign/examples/xml_files/scalar_demo.xml @@ -21,7 +21,11 @@ [(300.0,300.0),2,{0:'d0-u2'}] - + + + str + + diff --git a/run_pipeline.py b/run_pipeline.py index b63bbaac..ed50a526 100644 --- a/run_pipeline.py +++ b/run_pipeline.py @@ -2,9 +2,9 @@ core = ai.Core() -# core.load_config_file( -# ("./examples", -# "xml_files", -# 'crystalDesign_v1.xml')) +core.load_config_file( + ("./examples", + "xml_files", + 'crystalDesign_v2.xml')) core.run() \ No newline at end of file From 0b2818dc851a5cd0af480777d219f895954d57f3 Mon Sep 17 00:00:00 2001 From: Carlos Sevilla Salcedo Date: Tue, 25 Apr 2023 11:54:22 +0200 Subject: [PATCH 3/9] Css user interaction for Bayesian Optimisation (#91) * Display for less than 3 parameters * Added current selection * DoubleClick for editing * Change plugin readable name In order to avoid confusions with the Optimisation plugin, changed naming to indicate it referes to the ui. * Added icon to GUI window * Fix more than 2 variables error * Minor aesthetic changes --- aidesign/GUI/GUI_core.py | 3 +- .../plugins/OptimisationInput.py | 301 ++++++++++++++++++ aidesign/examples/X.csv | 29 ++ aidesign/examples/optimisation/X.csv | 29 ++ aidesign/examples/xml_files/BO_demo.xml | 32 ++ run_pipeline.py | 2 +- 6 files changed, 394 insertions(+), 2 deletions(-) create mode 100644 aidesign/UserInteraction/plugins/OptimisationInput.py create mode 100644 aidesign/examples/X.csv create mode 100644 aidesign/examples/optimisation/X.csv create mode 100644 aidesign/examples/xml_files/BO_demo.xml diff --git a/aidesign/GUI/GUI_core.py b/aidesign/GUI/GUI_core.py index 3ed923a7..ddd329d4 100644 --- a/aidesign/GUI/GUI_core.py +++ b/aidesign/GUI/GUI_core.py @@ -11,7 +11,8 @@ class GUI(tk.Tk): """ TODO: This structure still needs serious overhaul. - TODO: By having the TKinter controller as in the main GUI module, we are locked to using TKinter which defeats the purpose of being modular. This class needs to only be calling the plugins themselves, not acting as a controller. + TODO: By having the TKinter controller as in the main GUI module, we are locked to using TKinter which defeats the purpose of being modular. + This class needs to only be calling the plugins themselves, not acting as a controller. """ def __init__(self, *args, **kwargs): diff --git a/aidesign/UserInteraction/plugins/OptimisationInput.py b/aidesign/UserInteraction/plugins/OptimisationInput.py new file mode 100644 index 00000000..2cba174c --- /dev/null +++ b/aidesign/UserInteraction/plugins/OptimisationInput.py @@ -0,0 +1,301 @@ +from aidesign._plugin_templates import UI +from aidesign._import_helper import get_lib_parent_dir +from aidesign._types import DictT, DataInterface, GUICoreInterface + +import os +import numpy as np +import pandas as pd +from typing import Tuple, List, Union +from PIL import Image, ImageTk, PngImagePlugin +import matplotlib.pyplot as plt +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg + +import tkinter as tk +from tkinter import messagebox, ttk +from tkinter.filedialog import asksaveasfile + +_PLUGIN_READABLE_NAMES = {"optimisationUI": "default", + "BOUI": "alias", + "BayesianOptimisationUI": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"layer_priority": 2, + "required_children": None} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"Bounds"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X"} # type:ignore + + +class OptimisationInput(tk.Frame, UI): # type:ignore + """Method of user interaction for optimisation problems""" + + def __init__(self, parent, controller, config: DictT): + self.parent = parent + super().__init__(parent, bg=self.parent['bg']) + self.controller: GUICoreInterface = controller + self.controller.title('Optimisation Interaction') + + self.dirpath = get_lib_parent_dir() + self.tk.call('wm', 'iconphoto', self.controller._w, ImageTk.PhotoImage( + file=os.path.join(os.path.join( + self.dirpath, + 'utils', + 'resources', + 'Assets', + 'VAILabsIcon.ico')))) + + self.assets_path = os.path.join(self.dirpath, 'utils', 'resources', 'Assets') + + self._data_in: DataInterface + self._config = config + self.save_path = '' + self.saved = True + + + def _load_values_from_data(self): + + self.frame1 = tk.Frame(self, bg=self.parent['bg']) + frame4 = tk.Frame(self, bg=self.parent['bg']) + frame5 = tk.Frame(self, bg=self.parent['bg']) + # frame6 = tk.Frame(self, bg=self.parent['bg']) + + self.opt_var = list(self._data_in["X"].columns.values) + if len(self.opt_var) < 3: + figure = plt.Figure(figsize=(5, 4), dpi=100) + self.ax = figure.add_subplot(111) + + self.plot_points(self._data_in["X"], self.opt_var) + + self.canvas = FigureCanvasTkAgg(figure, self.frame1) + plot_frame = self.canvas.get_tk_widget() + plot_frame.grid(column=0, row=0, pady=10, padx=10, sticky="nsew") + self.frame1.grid_rowconfigure(0, weight=1) + self.frame1.grid_columnconfigure(0, weight=1) + + # Inital window + self.N = len(self._data_in["X"]) + + # Buttons + self.button_save = tk.Button( + frame4, text='Save', fg='white', bg=self.parent['bg'], height=3, + width=20, command=self.save_file) + self.button_save.grid(column=0, row=0, sticky="news", pady=2, padx=[2,0]) + + tk.Button( + frame5, text="Done", + fg='white', bg=self.parent['bg'], + height=3, width=20, + command=self.check_quit).grid(column=0, row=0, sticky="news", pady=2, padx=[0,2]) + + self.frame1.grid(row=0, column=0, sticky="nsew") + frame4.grid(row=1, column=0, sticky="nsew") + frame5.grid(row=1, column=1, sticky="nsew") + + frame4.grid_columnconfigure(0, weight=1) + frame5.grid_columnconfigure(0, weight=1) + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(tuple(range(2)), weight=1) + + def plot_points(self, data, labels, x=[None]): + """Plots points in pre-existing axis. If some extra points are given, + these are plotted with a different colour. + :param data: dict, dictionary to be plotted + :param labels: list, column and axis labels + :param x: array, extra points to be plotted + """ + + self.ax.clear() # clear axes from previous plot + self.ax.scatter(data[labels[0]], data[labels[1]]) + self.ax.set_xlabel(labels[0]) + self.ax.set_ylabel(labels[1]) + # self.ax.set_xlim(min(data[labels[0]]), max(data[labels[0]])) + # self.ax.set_ylim(min(data[labels[0]]), max(data[labels[0]])) + self.ax.set_title('Suggested points') + if None not in x: + self.ax.scatter(x[0], x[1], color='r') + + def set_data_in(self, data_in): + req_check = [ + r for r in _PLUGIN_REQUIRED_DATA if r not in data_in.keys()] + if len(req_check) > 0: + raise Exception("Minimal Data Requirements not met" + + "\n\t{0} ".format("OptimisationInput") + + "requires data: {0}".format(_PLUGIN_REQUIRED_DATA) + + "\n\tThe following data is missing:" + + "\n\t\u2022 {}".format(",\n\t\u2022 ".join([*req_check]))) + self._data_in = data_in + self._load_values_from_data() + self._load_classes_from_data() + + def class_list(self): + """Getter for required _class_list variable + + :return: list of class labels + :rtype: list of strings + """ + return self._class_list + + def _load_classes_from_data(self): + """Setter for required _class_list variable + + :param value: class labels for binary classification + :type value: list of strings + """ + self.out_data = self._data_in["X"] + + self.button_cl = {} + + # Tree defintion. Output display + style = ttk.Style() + style.configure( + "Treeview", background='white', foreground='white', + rowheight=25, fieldbackground='white', + font=self.controller.pages_font) + style.configure("Treeview.Heading", font=self.controller.pages_font) + style.map('Treeview', background=[('selected', 'grey')]) + + tree_frame = tk.Frame(self) + if len(self.opt_var) < 3: + tree_frame.grid(row=0, column=1, sticky="nsew", pady=10, padx=10) + else: + tree_frame.grid(row=0, column=0, columnspan = 2, sticky="nsew", pady=10, padx=10) + + tree_scrollx = tk.Scrollbar(tree_frame, orient='horizontal') + tree_scrollx.pack(side=tk.BOTTOM, fill=tk.X) + tree_scrolly = tk.Scrollbar(tree_frame) + tree_scrolly.pack(side=tk.RIGHT, fill=tk.Y) + + self.tree = ttk.Treeview(tree_frame, + yscrollcommand=tree_scrolly.set, + xscrollcommand=tree_scrollx.set) + self.tree.pack(fill='both', expand=True) + + tree_scrollx.config(command=self.tree.xview) + tree_scrolly.config(command=self.tree.yview) + + self.tree['columns'] = self.opt_var + + # Format columns + self.tree.column("#0", width=80, + minwidth=50) + for n, cl in enumerate(self.opt_var): + self.tree.column( + cl, width=int(self.controller.pages_font.measure(str(cl)))+20, + minwidth=50, anchor=tk.CENTER) + # Headings + self.tree.heading("#0", text="Sample", anchor=tk.CENTER) + for cl in self.opt_var: + self.tree.heading(cl, text=cl, anchor=tk.CENTER) + self.tree.tag_configure('odd', foreground='black', + background='#E8E8E8') + self.tree.tag_configure('even', foreground='black', + background='#DFDFDF') + # Add data + for n, sample in enumerate(self.out_data.values): + if n % 2 == 0: + self.tree.insert(parent='', index='end', iid=n, text=n+1, + values=tuple(sample.astype(float)), tags=('even',)) + else: + self.tree.insert(parent='', index='end', iid=n, text=n+1, + values=tuple(sample.astype(float)), tags=('odd',)) + + # Select the current row + self.tree.selection_set(str(int(0))) + + # Define click on row action + if len(self.opt_var) < 3: + self.tree.bind('', self.OnClick) + + # Define double-click on row action + self.tree.bind("", self.OnDoubleClick) + + def OnClick(self, event): + "Displays the corresponding ." + + item = self.tree.selection()[0] + x = [float(i) for i in self.tree.item(item)['values']] + if len(self.opt_var) < 3: + self.plot_points(self.out_data, self.opt_var, x = x) + self.canvas.draw() + + def OnDoubleClick(self, event): + """ Executed when a row is double clicked. + Opens an entry box to edit a cell and updates the plot and the + stored data. """ + + self.treerow = int(self.tree.identify_row(event.y)) + self.treecol = self.tree.identify_column(event.x) + + # get column position info + x, y, width, height = self.tree.bbox(self.treerow, self.treecol) + + # y-axis offset + pady = height // 2 + # pady = 0 + + if hasattr(self, 'entry'): + self.entry.destroy() + + self.entry = tk.Entry(self.tree, justify='center') + + if int(self.treecol[1:]) > 0: + self.entry.insert( + 0, self.tree.item(self.treerow)['values'][int(str(self.treecol[1:]))-1]) + self.entry['exportselection'] = False + + self.entry.focus_force() + self.entry.bind("", self.OnReturn) + self.entry.bind("", lambda *ignore: self.entry.destroy()) + + self.entry.place(x=x, + y=y + pady, + anchor=tk.W, width=width) + + def OnReturn(self, event): + """ Updates the stored data with the values in the entry. """ + val = self.tree.item(self.treerow)['values'] + val = [float(i) for i in val] + val[int(self.treecol[1:])-1] = float(self.entry.get()) + self.tree.item(self.treerow, values=val) + self.entry.destroy() + self.saved = False + + self.out_data.loc[self.treerow] = val + + self.OnClick(0) + self.saved = False + + def check_quit(self): + + if not self.saved: + response = messagebox.askokcancel( + "Exit?", + "Do you want to leave the program without saving?") + if response: + self.controller.destroy() + else: + response = messagebox.askokcancel( + "Exit?", + "Are you sure you are finished?") + self.controller.destroy() + + def save_file_as(self): + + self.save_path = asksaveasfile(mode='w') + self.save_file() + + def save_file(self): + + if self.save_path == '': + self.save_path = asksaveasfile(defaultextension='.txt', + filetypes=[('Text file', '.txt'), + ('CSV file', '.csv'), + ('All Files', '*.*')]) + # asksaveasfile return `None` if dialog closed with "cancel". + if self.save_path is not None: + filedata = pd.DataFrame( + self.out_data, columns=self.opt_var).to_string() + self.save_path.seek(0) # Move to the first row to overwrite it + self.save_path.write(filedata) + self.save_path.flush() # Save without closing + # typically the above line would do. however this is used to ensure that the file is written + os.fsync(self.save_path.fileno()) + self.saved = True \ No newline at end of file diff --git a/aidesign/examples/X.csv b/aidesign/examples/X.csv new file mode 100644 index 00000000..7213bdf8 --- /dev/null +++ b/aidesign/examples/X.csv @@ -0,0 +1,29 @@ +,Cs (%),MA (%) +0,0.0,40.0 +1,78.0,84.0 +2,1.0,98.0 +3,36.0,54.0 +4,78.0,39.0 +5,28.0,54.0 +6,48.0,50.0 +7,7.0,77.0 +8,51.0,94.0 +9,48.0,11.0 +10,64.0,70.0 +11,85.0,82.0 +12,42.0,57.0 +13,71.0,89.0 +14,56.0,44.0 +15,34.0,0.0 +16,1.0,9.0 +17,49.0,70.0 +18,18.0,63.0 +19,11.0,44.0 +20,29.0,24.0 +21,36.0,53.0 +22,85.0,0.0 +23,76.0,60.0 +24,4.0,31.0 +25,58.0,10.0 +26,38.0,10.0 +27,26.0,34.0 diff --git a/aidesign/examples/optimisation/X.csv b/aidesign/examples/optimisation/X.csv new file mode 100644 index 00000000..bb105ddd --- /dev/null +++ b/aidesign/examples/optimisation/X.csv @@ -0,0 +1,29 @@ +Cs(%),MA(%) +3.0,21.0 +17.0,13.0 +88.0,41.0 +78.0,55.0 +94.0,13.0 +97.0,22.0 +46.0,45.0 +56.0,42.0 +64.0,71.0 +2.0,49.0 +68.0,36.0 +33.0,68.0 +87.0,65.0 +92.0,81.0 +74.0,50.0 +21.0,59.0 +27.0,0.0 +67.0,68.0 +41.0,8.0 +76.0,65.0 +69.0,23.0 +83.0,5.0 +41.0,98.0 +14.0,97.0 +51.0,31.0 +60.0,36.0 +87.0,24.0 +86.0,18.0 diff --git a/aidesign/examples/xml_files/BO_demo.xml b/aidesign/examples/xml_files/BO_demo.xml new file mode 100644 index 00000000..50af59a3 --- /dev/null +++ b/aidesign/examples/xml_files/BO_demo.xml @@ -0,0 +1,32 @@ + + + + + + + + + + [(350.0,50),0,{}] + + + + + + + + + [(350.0,350.0),2,{0:'d0-u2'}] + + + + + + + + + + [(350.0,650),1,{2:'d2-u1'}] + + + diff --git a/run_pipeline.py b/run_pipeline.py index ed50a526..52c67dfa 100644 --- a/run_pipeline.py +++ b/run_pipeline.py @@ -5,6 +5,6 @@ core.load_config_file( ("./examples", "xml_files", - 'crystalDesign_v2.xml')) + 'BO_demo.xml')) core.run() \ No newline at end of file From 659dcd87c246556e4a3839ff62e260486bc8cc7a Mon Sep 17 00:00:00 2001 From: Carlos Sevilla Salcedo <22427519+sevisal@users.noreply.github.com> Date: Tue, 9 May 2023 08:57:14 +0200 Subject: [PATCH 4/9] Css bayesian optimisation (#98) * added re-attempt of env and pybullet * ongoing pybullet/arg population changes * added pybullet stubs * added credit to stubs * added gui/headless choice function * added environment module tag to XML parser * added pybullet config file * renamed Environment module to remove plural * added types and ABC for env modules * removed renamed scripts * modified run pipeline for pybullet * moved relative to absolute converter to import helper * added half-cheetah model * pybullet loading and running working * added half cheetah example with mass * added option to set gravity * removed requirement for data * removed data loading for pybullet example script * added __getattr__ function to access all pybullet methods from ENV class * added option parsing * Change progressTracker to treeview * Correct icon * WIP: editing option parsing * option mods - not yet working * arg passing without kwords to pybul from xml * cleaned imports * renamed depreciated setup file * removed old setup file * cleaned up main __init__ file * added matplotlib to pyproject.toml * Add pytest structure (#56) Co-authored-by: Teemu Ruokolainen * Add cli (#57) * working cli * Update readme * Update readme Co-authored-by: Teemu Ruokolainen * Cm package restructuring (#54) * moved py files to src/vai_labs for packaging * added vscode config to gitignore * updated package name in run_pipeline * fixed pyproject matplotlib import * fixed path for import helper * updated project version for PYPI * updated readme and pyproject.toml * fixed README typo * removed depreciated setup script * fixed path in import_helper.py * Update README.md Updated repo name in README * moved README images * Update README image paths * Remove readme.md.bak (#58) Co-authored-by: Teemu Ruokolainen * Cm name updates (#59) * updated naming and paths for projects * merged README files * updated README naming * update import names in README * updated example links * updated missed readme names * find-replace aidesign refs -> vai_lab * renamed files containing old ai_design name * renamed test file * Css-gui_bugFixing (#60) * Adjust aidCanvas' buttons' size * Correctly resize lower buttons' sizes * Resize buttons, pad radio frame, correct click on in or out * Fix issue with data path * Fix upload data mattrix path * moved test script Co-authored-by: cam586 * Fix repository url in readme and pyproject.toml (#63) * added opencv & pytest (#62) * Clean up lingering/duplicate files. (#64) Co-authored-by: Teemu Ruokolainen * Added debug option (#65) * add debug option to core,GUI * Update test_launch.py Add test_launch() function for pytest discovery Co-authored-by: teemu * Added CI Test using Github Actions (#66) * add debug option to core,GUI * Update test_launch.py Add test_launch() function for pytest discovery * added initial actions yaml * testing now triggered on PR * removed testing for python3.7 * added option for manually running actions * added workflow_dispatch options * temp removal of PR trigger * testing using push trigger * removed incorrect pytest dir * added env variable for GUI testing * added virtual display for GUI testing * removed old yml tag * install virtual display with existing action * fixed yml syntax * fixed xvfb syntax * removed old xvfb tags * update to node.js 16 * test pip caching * removed cache test * fixed cov file path * removed testing for python3.7 * install virtual display with existing action * update to node.js 16 * fixed cov file path * changed trigger condition to pull request * renamed tests package * added programming language classifiers * changed trigger to PR * added test and version shields * fixed README typo * gitignore any venv * removed unneeded tk dep from toml * simplified test call * testing manual workflow trigger * added workflow_dispatch options * re-added pull_request trigger Co-authored-by: teemu * Remove flit from readme (#67) Co-authored-by: Teemu Ruokolainen * Cm fix readme img path (#68) * fixed readme image paths * updated version number * Css fix pluginarchitecture diagram (#69) * Include plugin diagram * Delete old verion * Update plugin diagram * Update plugin diagram * Add white filling to squares for dark mode * fixed contribute urls Co-authored-by: cam586 * Add rough sphinx documentation (#74) * Add autogen docsting sphinx docs * ReadTheDocs * Building version * Change index page title * Add github actions workflow * Update README * Add pip install . to documentation.yml * Add README.md as a page in documentation * Add sphinx-apidoc command to readme; gitignore generated rst files * Update readme sphinx commands * Update readme * Add comments * Update commands in documentation workflow * Fix typo in .gitignore * Remove _static folder from config to remove warning message * Specify python=3.10 for workflow * Remove pip install sphinx-apidoc, no such package * Update readme * added docs build badge to readme * fixed error-causing docstring Co-authored-by: Teemu Ruokolainen Co-authored-by: chris * Cm test docs (#76) * edited branch name for doc building * test actions * added jekyll options * removed settings * fixed gh pages deployment * Cm add docs (#77) * changed copyright year * updated readme headings * moved tutorials from readme to docs * removed tutorials from readme * added getting started page to index toc * Update python=3.8 to python=3.10 in readme * Cm feature add tests (#80) * bumped version number * add pilot example test * simplified core.py * improved naming and docs in core * progress tracker. -> * added example script iteration * fixed loop error msg in COre * updated progress tracker naming (#71) * fixed for loop logic and error handling in core (#72) * Cm fix tracker loops (#73) * fixed for loop logic and error handling in core * fixed tracker dict population for loops * Fix Progess Tracker naming Removing Initialiser name requirement from Progress Tracker * Remove Canvas if there are no ccordinates * Fixing last fix. Correctly read modules inside loop * Fix error with coordinates case * minor typo fix in core * renamed .s variable to .xml_handler for readability * fixed xml_handler naming in GUI_Core * Trying to fix coordinates case * moved avail_plugins to init * Display treeview for no-coordinates case (need fixes)) * FIX - Consistent input and output naming issue * Functioning progress tracker for coord and no coord * Fixed list plugin options issues * Minor fixes to test usability * changed avail plugins options to readable names * removed private and test files from avail_plugins * removed old xml files, mod pybullet * Fix parent issue * fixed pybullet and canvas gui-test issues * fixed pytest errors (except manual plugin) * changed sklearn->scikit-learn in toml * added pybullet to toml * Remove testing file * test fix with scalar * translate boolean literals * removed python 3.8,3.9 * added param to ridge example * removed closed check from core * switced core check to _debug --------- Co-authored-by: Carlos Sevilla Salcedo --------- Co-authored-by: Carlos Sevilla Salcedo * Fix examples path in README.md * Another bad examples path * update conda env part in README.md * fixed calls to undefined parent (#90) * Added Bayesian Optimisation plugin * Fix merge errors * Remove old run file * Modify plugin types * Addoptions to BO * Css feature trying to catch errors (#94) * Move Modelling plugins functions to template * Add clsutering methods to template * Change function doc * Move DataProcessing plugins functions to template * Update pipeline for testing * Update init * Fix conflicts * Add DecisionMaking plugin template * Fixing issues with Optimisation module * Fixed errors passing params to BO * Changed plugin Template to work for BO params Still need to fix issue when running the BO * BO suggests points correctly * Change xml_handler naming Change xml_handler naming to fit with the other UIs * Typo correction * Added bayes_opt optimisation plugin * Modify integral to simpson * Fixed issue with UI --------- Co-authored-by: cam586 Co-authored-by: teemu Co-authored-by: Teemu Ruokolainen Co-authored-by: Richard Darst --- .github/workflows/documentation.yml | 26 + .github/workflows/pythonpackage.yml | 47 + .gitignore | 6 +- README.md | 247 +---- aidesign/DataProcessing/plugins/binarizer.py | 47 - aidesign/DataProcessing/plugins/integral.py | 47 - .../plugins/kbinsdiscretizer.py | 47 - .../DataProcessing/plugins/labelbinarizer.py | 45 - .../DataProcessing/plugins/labelencoder.py | 46 - .../DataProcessing/plugins/maxabsscaler.py | 46 - .../DataProcessing/plugins/minmaxscaler.py | 47 - .../plugins/multilabelbinarizer.py | 46 - aidesign/DataProcessing/plugins/normalizer.py | 51 - .../DataProcessing/plugins/onehotencoder.py | 46 - .../DataProcessing/plugins/ordinalencoder.py | 48 - .../plugins/quantiletransformer.py | 49 - .../DataProcessing/plugins/standardscaler.py | 49 - aidesign/DecisionMaking/Modelling_core.py | 29 - .../Modelling/plugins/affinitypropagation.py | 61 -- aidesign/Modelling/plugins/bayesianridge.py | 61 -- aidesign/Modelling/plugins/birch.py | 62 -- .../plugins/decisiontreeclassifier.py | 62 -- .../plugins/decisiontreeregressor.py | 62 -- aidesign/Modelling/plugins/elasticnet.py | 62 -- aidesign/Modelling/plugins/gpclassifier.py | 64 -- aidesign/Modelling/plugins/gpregressor.py | 64 -- aidesign/Modelling/plugins/kernelridge.py | 65 -- aidesign/Modelling/plugins/kmeans.py | 62 -- aidesign/Modelling/plugins/knnclassifier.py | 63 -- aidesign/Modelling/plugins/knnregressor.py | 63 -- aidesign/Modelling/plugins/lasso.py | 60 -- .../Modelling/plugins/linearregression.py | 62 -- .../Modelling/plugins/logisticregression.py | 63 -- aidesign/Modelling/plugins/meanshift.py | 60 -- .../plugins/passiveaggressiveclassifier.py | 62 -- aidesign/Modelling/plugins/perceptron.py | 63 -- .../plugins/randomforestclassifier.py | 64 -- .../plugins/randomforestregressor.py | 64 -- aidesign/Modelling/plugins/ridgeregression.py | 62 -- aidesign/Modelling/plugins/svc.py | 65 -- aidesign/Modelling/plugins/svr.py | 65 -- aidesign/__init__.py | 7 - aidesign/examples/X.csv | 29 - aidesign/examples/optimisation/X.csv | 29 - .../xml_files/Hospital_use_case_visual.xml | 101 -- .../xml_files/Kevin_experimentalDesign.xml | 163 --- .../examples/xml_files/basic_operation.xml | 48 - docs/Makefile | 20 + docs/make.bat | 35 + docs/source/conf.py | 37 + docs/source/getting_started.rst | 280 +++++ docs/source/index.rst | 28 + docs/source/readme.rst | 5 + docs/templates/apidoc/module.rst_t | 9 + docs/templates/apidoc/package.rst_t | 53 + docs/templates/apidoc/toc.rst_t | 8 + .../VAIL_GUI_screenshot.png | Bin .../VAIL_banner_image.png | Bin .../VAIL_mainpage_screenshot.png | Bin imgs/VAIL_plugin_diagram.png | Bin 0 -> 87434 bytes pybullet.pyi | 971 ++++++++++++++++++ pyproject.toml | 30 +- run_pipeline.py | 10 - setup.py | 56 - .../vai_lab}/Core/Assets/UFAIcon.ico | Bin .../vai_lab}/Core/Assets/UFAIcon_name.png | Bin {aidesign => src/vai_lab}/Core/__init__.py | 2 +- src/vai_lab/Core/tests/__init__.py | 0 src/vai_lab/Core/tests/test_vai_lab_core.py | 11 + .../vai_lab/Core/vai_lab_core.py | 79 +- {aidesign => src/vai_lab}/Data/Data_core.py | 22 +- {aidesign => src/vai_lab}/Data/__init__.py | 0 src/vai_lab/Data/tests/__init__.py | 0 {aidesign => src/vai_lab}/Data/xml_handler.py | 52 +- .../DataProcessing/DataProcessing_core.py | 4 +- .../vai_lab}/DataProcessing/__init__.py | 0 .../vai_lab}/DataProcessing/plugins/argopt.py | 4 +- .../DataProcessing/plugins/binarizer.py | 23 + .../DataProcessing/plugins/integral.py | 32 + .../plugins/kbinsdiscretizer.py | 23 + .../DataProcessing/plugins/labelbinarizer.py | 21 + .../DataProcessing/plugins/labelencoder.py | 22 + .../DataProcessing/plugins/maxabsscaler.py | 22 + .../DataProcessing/plugins/minmaxscaler.py | 23 + .../plugins/multilabelbinarizer.py | 22 + .../DataProcessing/plugins/normalizer.py | 26 + .../DataProcessing/plugins/onehotencoder.py | 22 + .../DataProcessing/plugins/ordinalencoder.py | 23 + .../plugins/polynomialfeatures.py | 29 +- .../plugins/quantiletransformer.py | 24 + .../DataProcessing/plugins/standardscaler.py | 24 + src/vai_lab/DataProcessing/tests/__init__.py | 0 .../vai_lab}/DataStorage/DataStorage_core.py | 0 .../vai_lab}/DataStorage/__init__.py | 0 src/vai_lab/DataStorage/tests/__init__.py | 0 .../DecisionMaking/DecisionMaking_core.py | 36 + .../vai_lab/DecisionMaking}/__init__.py | 2 +- .../plugins/BayesianOptimisation(GPy).py | 61 ++ .../BayesianOptimisation(bayes_opt).py | 49 + src/vai_lab/DecisionMaking/tests/__init__.py | 0 src/vai_lab/Environment/Environment_core.py | 41 + .../Environment/plugins/PyBulletEnv.py | 101 ++ .../resources/models/half_cheetah.xml | 82 ++ .../models/half_cheetah_with_mass.xml | 88 ++ src/vai_lab/Environment/tests/__init__.py | 0 {aidesign => src/vai_lab}/GUI/GUI_core.py | 32 +- .../vai_lab}/GUI/User_Feedback_template.py | 4 +- {aidesign => src/vai_lab}/GUI/__init__.py | 0 .../vai_lab}/GUI/plugins/__init__.py | 0 src/vai_lab/GUI/tests/__init__.py | 0 .../vai_lab}/InputData/InputData_core.py | 6 +- .../vai_lab}/InputData/__init__.py | 0 src/vai_lab/InputData/tests/__init__.py | 0 .../vai_lab}/Modelling/Modelling_core.py | 2 +- .../vai_lab/Modelling}/__init__.py | 0 .../Modelling/plugins/affinitypropagation.py | 22 + .../Modelling/plugins/bayesianridge.py | 22 + src/vai_lab/Modelling/plugins/birch.py | 23 + .../plugins/decisiontreeclassifier.py | 23 + .../plugins/decisiontreeregressor.py | 23 + src/vai_lab/Modelling/plugins/elasticnet.py | 23 + src/vai_lab/Modelling/plugins/gpclassifier.py | 25 + src/vai_lab/Modelling/plugins/gpregressor.py | 25 + src/vai_lab/Modelling/plugins/kernelridge.py | 26 + src/vai_lab/Modelling/plugins/kmeans.py | 23 + .../Modelling/plugins/knnclassifier.py | 24 + src/vai_lab/Modelling/plugins/knnregressor.py | 24 + src/vai_lab/Modelling/plugins/lasso.py | 21 + .../Modelling/plugins/linearregression.py | 23 + .../Modelling/plugins/logisticregression.py | 24 + src/vai_lab/Modelling/plugins/meanshift.py | 21 + .../plugins/passiveaggressiveclassifier.py | 23 + src/vai_lab/Modelling/plugins/perceptron.py | 24 + .../plugins/randomforestclassifier.py | 25 + .../plugins/randomforestregressor.py | 25 + .../Modelling/plugins/ridgeregression.py | 23 + src/vai_lab/Modelling/plugins/svc.py | 26 + src/vai_lab/Modelling/plugins/svr.py | 26 + src/vai_lab/Modelling/tests/__init__.py | 0 .../UserInteraction/UserInteraction_core.py | 2 +- .../vai_lab}/UserInteraction/__init__.py | 0 .../UserInteraction/plugins/CanvasInput.py | 18 +- .../UserInteraction/plugins/ManualInput.py | 12 +- .../plugins/OptimisationInput.py | 14 +- .../UserInteraction/plugins/__init__.py | 0 .../plugins/resources/Assets/UFAIcon.ico | Bin .../plugins/resources/Assets/UFAIcon_name.png | Bin .../plugins/resources/Assets/back_arrow.png | Bin .../plugins/resources/Assets/forw_arrow.png | Bin src/vai_lab/UserInteraction/tests/__init__.py | 0 src/vai_lab/__init__.py | 1 + {aidesign => src/vai_lab}/_import_helper.py | 15 +- {aidesign => src/vai_lab}/_plugin_helpers.py | 40 +- .../vai_lab}/_plugin_templates.py | 193 +++- {aidesign => src/vai_lab}/_types.py | 18 + .../vai_lab}/examples/crystalDesign/00001.png | Bin .../vai_lab}/examples/crystalDesign/00002.png | Bin .../vai_lab}/examples/crystalDesign/00003.png | Bin .../vai_lab}/examples/crystalDesign/00005.png | Bin .../vai_lab}/examples/crystalDesign/00007.png | Bin .../vai_lab}/examples/crystalDesign/00009.png | Bin .../vai_lab}/examples/crystalDesign/00011.png | Bin .../vai_lab}/examples/crystalDesign/00013.png | Bin .../vai_lab}/examples/crystalDesign/00015.png | Bin .../crystalDesign/phasestability/.DS_Store | Bin 0 -> 6148 bytes .../phasestability/CsFA/.DS_Store | Bin 0 -> 6148 bytes .../CsFA/fulldata/CsFA_T300_above.csv | 1 + .../phasestability/CsMA/.DS_Store | Bin 0 -> 6148 bytes .../CsMA/fulldata/CsMA_T300_above.csv | 1 + .../phasestability/FAMA/.DS_Store | Bin 0 -> 6148 bytes .../FAMA/fulldata/FAMA_T300_above.csv | 1 + .../crystalDesign/phasestability/Readme | 16 + .../crystalDesign/phasestability/template.csv | 16 + .../examples/image_classification/Y_data.csv | 0 .../training_images/00028173_005.png | Bin .../training_images/00028179_000.png | Bin .../training_images/00028198_002.png | Bin .../training_images/00028208_035.png | Bin .../training_images/00028211_021.png | Bin .../training_images/00028253_000.png | Bin .../training_images/00028279_000.png | Bin .../vai_lab}/examples/state-action/X_data.csv | 0 .../supervised_classification/X_tr.csv | 0 .../supervised_classification/X_tst.csv | 0 .../supervised_classification/Y_tr.csv | 0 .../supervised_classification/Y_tst.csv | 0 .../randomInputData.mat | Bin .../randomInputDataClassification.py | 0 .../examples/supervised_regression/X_tr.csv | 0 .../examples/supervised_regression/X_tst.csv | 0 .../examples/supervised_regression/Y_tr.csv | 0 .../examples/supervised_regression/Y_tst.csv | 0 .../data_1D_simple/x_train.csv | 0 .../data_1D_simple/y_train.csv | 0 .../import_test user_interface.csv | 0 .../supervised_regression/import_test.csv | 0 .../supervised_regression/randomInputData.mat | Bin .../randomInputDataRegression.py | 0 .../vai_lab}/examples/xml_files/BO_demo.xml | 0 .../xml_files/DecisionMaking_demo.xml | 29 + .../xml_files/KNN-classification_demo.xml | 0 .../vai_lab}/examples/xml_files/SVR_demo.xml | 0 .../examples/xml_files/bayes_opt_demo.xml | 36 + .../examples/xml_files/canvas_demo.xml | 2 +- .../examples/xml_files/crystalDesign_v2.xml | 0 .../examples/xml_files/crystalDesign_v3.xml | 0 .../xml_files/k-mean_clustering_demo.xml | 0 .../xml_files/logistic_regression_demo.xml | 0 .../examples/xml_files/optimisation_demo.xml | 55 + .../xml_files/pybullet_env_example.xml | 48 + .../xml_files/random_forest_class_demo.xml | 10 +- .../examples/xml_files/regression_demo.xml | 0 .../xml_files/ridge-scalar-ridge_demo.xml | 2 +- .../xml_files/ridge_regression_demo.xml | 0 .../examples/xml_files/scalar_demo.xml | 8 +- .../examples/xml_files/scaler-lasso_demo.xml | 0 .../examples/xml_files/user_feedback_demo.xml | 2 +- src/vai_lab/run_pipeline.py | 68 ++ src/vai_lab/tests/__init__.py | 0 src/vai_lab/tests/test_examples.py | 18 + src/vai_lab/tests/test_launch.py | 9 + {aidesign => src/vai_lab}/utils/AID_core.py | 2 +- .../vai_lab}/utils/UserInterfaceClass.py | 4 +- {aidesign => src/vai_lab}/utils/__init__.py | 0 .../vai_lab}/utils/plugins/MainPage.py | 66 +- .../vai_lab}/utils/plugins/__init__.py | 0 .../vai_lab}/utils/plugins/aidCanvas.py | 87 +- .../vai_lab}/utils/plugins/dataLoader.py | 10 +- .../vai_lab}/utils/plugins/pluginCanvas.py | 159 +-- .../vai_lab}/utils/plugins/progressTracker.py | 560 +++++++--- .../vai_lab}/utils/plugins/startpage.py | 2 +- .../utils/resources/Assets/ADDIcon.ico | Bin .../utils/resources/Assets/ADDIcon_name.png | Bin .../utils/resources/Assets/AIDIcon.ico | Bin .../utils/resources/Assets/AIDIcon_name.png | Bin .../utils/resources/Assets/AIFRED.png | Bin .../utils/resources/Assets/Hospital.xml | 0 .../utils/resources/Assets/UFAIcon.ico | Bin .../utils/resources/Assets/UFAIcon_name.png | Bin .../vai_lab}/utils/resources/Assets/VAIL.ico | Bin .../utils/resources/Assets/VAILabs.png | Bin .../utils/resources/Assets/VAILabsIcon.ico | Bin .../utils/resources/Assets/back_arrow.png | Bin .../utils/resources/Assets/forw_arrow.png | Bin .../00028173_005.png | Bin .../00028179_000.png | Bin .../00028198_002.png | Bin .../00028208_035.png | Bin .../00028211_021.png | Bin .../00028253_000.png | Bin .../00028279_000.png | Bin src/vai_lab/utils/tests/__init__.py | 0 252 files changed, 4155 insertions(+), 3257 deletions(-) create mode 100644 .github/workflows/documentation.yml create mode 100644 .github/workflows/pythonpackage.yml delete mode 100644 aidesign/DataProcessing/plugins/binarizer.py delete mode 100644 aidesign/DataProcessing/plugins/integral.py delete mode 100644 aidesign/DataProcessing/plugins/kbinsdiscretizer.py delete mode 100644 aidesign/DataProcessing/plugins/labelbinarizer.py delete mode 100644 aidesign/DataProcessing/plugins/labelencoder.py delete mode 100644 aidesign/DataProcessing/plugins/maxabsscaler.py delete mode 100644 aidesign/DataProcessing/plugins/minmaxscaler.py delete mode 100644 aidesign/DataProcessing/plugins/multilabelbinarizer.py delete mode 100644 aidesign/DataProcessing/plugins/normalizer.py delete mode 100644 aidesign/DataProcessing/plugins/onehotencoder.py delete mode 100644 aidesign/DataProcessing/plugins/ordinalencoder.py delete mode 100644 aidesign/DataProcessing/plugins/quantiletransformer.py delete mode 100644 aidesign/DataProcessing/plugins/standardscaler.py delete mode 100644 aidesign/DecisionMaking/Modelling_core.py delete mode 100644 aidesign/Modelling/plugins/affinitypropagation.py delete mode 100644 aidesign/Modelling/plugins/bayesianridge.py delete mode 100644 aidesign/Modelling/plugins/birch.py delete mode 100644 aidesign/Modelling/plugins/decisiontreeclassifier.py delete mode 100644 aidesign/Modelling/plugins/decisiontreeregressor.py delete mode 100644 aidesign/Modelling/plugins/elasticnet.py delete mode 100644 aidesign/Modelling/plugins/gpclassifier.py delete mode 100644 aidesign/Modelling/plugins/gpregressor.py delete mode 100644 aidesign/Modelling/plugins/kernelridge.py delete mode 100644 aidesign/Modelling/plugins/kmeans.py delete mode 100644 aidesign/Modelling/plugins/knnclassifier.py delete mode 100644 aidesign/Modelling/plugins/knnregressor.py delete mode 100644 aidesign/Modelling/plugins/lasso.py delete mode 100644 aidesign/Modelling/plugins/linearregression.py delete mode 100644 aidesign/Modelling/plugins/logisticregression.py delete mode 100644 aidesign/Modelling/plugins/meanshift.py delete mode 100644 aidesign/Modelling/plugins/passiveaggressiveclassifier.py delete mode 100644 aidesign/Modelling/plugins/perceptron.py delete mode 100644 aidesign/Modelling/plugins/randomforestclassifier.py delete mode 100644 aidesign/Modelling/plugins/randomforestregressor.py delete mode 100644 aidesign/Modelling/plugins/ridgeregression.py delete mode 100644 aidesign/Modelling/plugins/svc.py delete mode 100644 aidesign/Modelling/plugins/svr.py delete mode 100644 aidesign/__init__.py delete mode 100644 aidesign/examples/X.csv delete mode 100644 aidesign/examples/optimisation/X.csv delete mode 100644 aidesign/examples/xml_files/Hospital_use_case_visual.xml delete mode 100644 aidesign/examples/xml_files/Kevin_experimentalDesign.xml delete mode 100644 aidesign/examples/xml_files/basic_operation.xml create mode 100644 docs/Makefile create mode 100644 docs/make.bat create mode 100644 docs/source/conf.py create mode 100644 docs/source/getting_started.rst create mode 100644 docs/source/index.rst create mode 100644 docs/source/readme.rst create mode 100644 docs/templates/apidoc/module.rst_t create mode 100644 docs/templates/apidoc/package.rst_t create mode 100644 docs/templates/apidoc/toc.rst_t rename {aidesign/utils/resources/Assets/readme_images => imgs}/VAIL_GUI_screenshot.png (100%) rename {aidesign/utils/resources/Assets/readme_images => imgs}/VAIL_banner_image.png (100%) rename {aidesign/utils/resources/Assets/readme_images => imgs}/VAIL_mainpage_screenshot.png (100%) create mode 100644 imgs/VAIL_plugin_diagram.png create mode 100644 pybullet.pyi delete mode 100644 run_pipeline.py delete mode 100644 setup.py rename {aidesign => src/vai_lab}/Core/Assets/UFAIcon.ico (100%) rename {aidesign => src/vai_lab}/Core/Assets/UFAIcon_name.png (100%) rename {aidesign => src/vai_lab}/Core/__init__.py (86%) create mode 100644 src/vai_lab/Core/tests/__init__.py create mode 100644 src/vai_lab/Core/tests/test_vai_lab_core.py rename aidesign/Core/AI_design_core.py => src/vai_lab/Core/vai_lab_core.py (70%) rename {aidesign => src/vai_lab}/Data/Data_core.py (86%) rename {aidesign => src/vai_lab}/Data/__init__.py (100%) create mode 100644 src/vai_lab/Data/tests/__init__.py rename {aidesign => src/vai_lab}/Data/xml_handler.py (95%) rename {aidesign => src/vai_lab}/DataProcessing/DataProcessing_core.py (90%) rename {aidesign => src/vai_lab}/DataProcessing/__init__.py (100%) rename {aidesign => src/vai_lab}/DataProcessing/plugins/argopt.py (93%) create mode 100644 src/vai_lab/DataProcessing/plugins/binarizer.py create mode 100644 src/vai_lab/DataProcessing/plugins/integral.py create mode 100644 src/vai_lab/DataProcessing/plugins/kbinsdiscretizer.py create mode 100644 src/vai_lab/DataProcessing/plugins/labelbinarizer.py create mode 100644 src/vai_lab/DataProcessing/plugins/labelencoder.py create mode 100644 src/vai_lab/DataProcessing/plugins/maxabsscaler.py create mode 100644 src/vai_lab/DataProcessing/plugins/minmaxscaler.py create mode 100644 src/vai_lab/DataProcessing/plugins/multilabelbinarizer.py create mode 100644 src/vai_lab/DataProcessing/plugins/normalizer.py create mode 100644 src/vai_lab/DataProcessing/plugins/onehotencoder.py create mode 100644 src/vai_lab/DataProcessing/plugins/ordinalencoder.py rename {aidesign => src/vai_lab}/DataProcessing/plugins/polynomialfeatures.py (51%) create mode 100644 src/vai_lab/DataProcessing/plugins/quantiletransformer.py create mode 100644 src/vai_lab/DataProcessing/plugins/standardscaler.py create mode 100644 src/vai_lab/DataProcessing/tests/__init__.py rename {aidesign => src/vai_lab}/DataStorage/DataStorage_core.py (100%) rename {aidesign => src/vai_lab}/DataStorage/__init__.py (100%) create mode 100644 src/vai_lab/DataStorage/tests/__init__.py create mode 100644 src/vai_lab/DecisionMaking/DecisionMaking_core.py rename {aidesign/Modelling => src/vai_lab/DecisionMaking}/__init__.py (80%) create mode 100644 src/vai_lab/DecisionMaking/plugins/BayesianOptimisation(GPy).py create mode 100644 src/vai_lab/DecisionMaking/plugins/BayesianOptimisation(bayes_opt).py create mode 100644 src/vai_lab/DecisionMaking/tests/__init__.py create mode 100644 src/vai_lab/Environment/Environment_core.py create mode 100644 src/vai_lab/Environment/plugins/PyBulletEnv.py create mode 100644 src/vai_lab/Environment/resources/models/half_cheetah.xml create mode 100644 src/vai_lab/Environment/resources/models/half_cheetah_with_mass.xml create mode 100644 src/vai_lab/Environment/tests/__init__.py rename {aidesign => src/vai_lab}/GUI/GUI_core.py (89%) rename {aidesign => src/vai_lab}/GUI/User_Feedback_template.py (63%) rename {aidesign => src/vai_lab}/GUI/__init__.py (100%) rename {aidesign => src/vai_lab}/GUI/plugins/__init__.py (100%) create mode 100644 src/vai_lab/GUI/tests/__init__.py rename {aidesign => src/vai_lab}/InputData/InputData_core.py (90%) rename {aidesign => src/vai_lab}/InputData/__init__.py (100%) create mode 100644 src/vai_lab/InputData/tests/__init__.py rename {aidesign => src/vai_lab}/Modelling/Modelling_core.py (95%) rename {aidesign/DecisionMaking => src/vai_lab/Modelling}/__init__.py (100%) create mode 100644 src/vai_lab/Modelling/plugins/affinitypropagation.py create mode 100644 src/vai_lab/Modelling/plugins/bayesianridge.py create mode 100644 src/vai_lab/Modelling/plugins/birch.py create mode 100644 src/vai_lab/Modelling/plugins/decisiontreeclassifier.py create mode 100644 src/vai_lab/Modelling/plugins/decisiontreeregressor.py create mode 100644 src/vai_lab/Modelling/plugins/elasticnet.py create mode 100644 src/vai_lab/Modelling/plugins/gpclassifier.py create mode 100644 src/vai_lab/Modelling/plugins/gpregressor.py create mode 100644 src/vai_lab/Modelling/plugins/kernelridge.py create mode 100644 src/vai_lab/Modelling/plugins/kmeans.py create mode 100644 src/vai_lab/Modelling/plugins/knnclassifier.py create mode 100644 src/vai_lab/Modelling/plugins/knnregressor.py create mode 100644 src/vai_lab/Modelling/plugins/lasso.py create mode 100644 src/vai_lab/Modelling/plugins/linearregression.py create mode 100644 src/vai_lab/Modelling/plugins/logisticregression.py create mode 100644 src/vai_lab/Modelling/plugins/meanshift.py create mode 100644 src/vai_lab/Modelling/plugins/passiveaggressiveclassifier.py create mode 100644 src/vai_lab/Modelling/plugins/perceptron.py create mode 100644 src/vai_lab/Modelling/plugins/randomforestclassifier.py create mode 100644 src/vai_lab/Modelling/plugins/randomforestregressor.py create mode 100644 src/vai_lab/Modelling/plugins/ridgeregression.py create mode 100644 src/vai_lab/Modelling/plugins/svc.py create mode 100644 src/vai_lab/Modelling/plugins/svr.py create mode 100644 src/vai_lab/Modelling/tests/__init__.py rename {aidesign => src/vai_lab}/UserInteraction/UserInteraction_core.py (83%) rename {aidesign => src/vai_lab}/UserInteraction/__init__.py (100%) rename {aidesign => src/vai_lab}/UserInteraction/plugins/CanvasInput.py (98%) rename {aidesign => src/vai_lab}/UserInteraction/plugins/ManualInput.py (98%) rename {aidesign => src/vai_lab}/UserInteraction/plugins/OptimisationInput.py (96%) rename {aidesign => src/vai_lab}/UserInteraction/plugins/__init__.py (100%) rename {aidesign => src/vai_lab}/UserInteraction/plugins/resources/Assets/UFAIcon.ico (100%) rename {aidesign => src/vai_lab}/UserInteraction/plugins/resources/Assets/UFAIcon_name.png (100%) rename {aidesign => src/vai_lab}/UserInteraction/plugins/resources/Assets/back_arrow.png (100%) rename {aidesign => src/vai_lab}/UserInteraction/plugins/resources/Assets/forw_arrow.png (100%) create mode 100644 src/vai_lab/UserInteraction/tests/__init__.py create mode 100644 src/vai_lab/__init__.py rename {aidesign => src/vai_lab}/_import_helper.py (65%) rename {aidesign => src/vai_lab}/_plugin_helpers.py (85%) rename {aidesign => src/vai_lab}/_plugin_templates.py (50%) rename {aidesign => src/vai_lab}/_types.py (90%) rename {aidesign => src/vai_lab}/examples/crystalDesign/00001.png (100%) rename {aidesign => src/vai_lab}/examples/crystalDesign/00002.png (100%) rename {aidesign => src/vai_lab}/examples/crystalDesign/00003.png (100%) rename {aidesign => src/vai_lab}/examples/crystalDesign/00005.png (100%) rename {aidesign => src/vai_lab}/examples/crystalDesign/00007.png (100%) rename {aidesign => src/vai_lab}/examples/crystalDesign/00009.png (100%) rename {aidesign => src/vai_lab}/examples/crystalDesign/00011.png (100%) rename {aidesign => src/vai_lab}/examples/crystalDesign/00013.png (100%) rename {aidesign => src/vai_lab}/examples/crystalDesign/00015.png (100%) create mode 100644 src/vai_lab/examples/crystalDesign/phasestability/.DS_Store create mode 100644 src/vai_lab/examples/crystalDesign/phasestability/CsFA/.DS_Store create mode 100644 src/vai_lab/examples/crystalDesign/phasestability/CsFA/fulldata/CsFA_T300_above.csv create mode 100644 src/vai_lab/examples/crystalDesign/phasestability/CsMA/.DS_Store create mode 100644 src/vai_lab/examples/crystalDesign/phasestability/CsMA/fulldata/CsMA_T300_above.csv create mode 100644 src/vai_lab/examples/crystalDesign/phasestability/FAMA/.DS_Store create mode 100644 src/vai_lab/examples/crystalDesign/phasestability/FAMA/fulldata/FAMA_T300_above.csv create mode 100644 src/vai_lab/examples/crystalDesign/phasestability/Readme create mode 100644 src/vai_lab/examples/crystalDesign/phasestability/template.csv rename {aidesign => src/vai_lab}/examples/image_classification/Y_data.csv (100%) rename {aidesign => src/vai_lab}/examples/image_classification/training_images/00028173_005.png (100%) rename {aidesign => src/vai_lab}/examples/image_classification/training_images/00028179_000.png (100%) rename {aidesign => src/vai_lab}/examples/image_classification/training_images/00028198_002.png (100%) rename {aidesign => src/vai_lab}/examples/image_classification/training_images/00028208_035.png (100%) rename {aidesign => src/vai_lab}/examples/image_classification/training_images/00028211_021.png (100%) rename {aidesign => src/vai_lab}/examples/image_classification/training_images/00028253_000.png (100%) rename {aidesign => src/vai_lab}/examples/image_classification/training_images/00028279_000.png (100%) rename {aidesign => src/vai_lab}/examples/state-action/X_data.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_classification/X_tr.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_classification/X_tst.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_classification/Y_tr.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_classification/Y_tst.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_classification/randomInputData.mat (100%) rename {aidesign => src/vai_lab}/examples/supervised_classification/randomInputDataClassification.py (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/X_tr.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/X_tst.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/Y_tr.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/Y_tst.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/data_1D_simple/x_train.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/data_1D_simple/y_train.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/import_test user_interface.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/import_test.csv (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/randomInputData.mat (100%) rename {aidesign => src/vai_lab}/examples/supervised_regression/randomInputDataRegression.py (100%) rename {aidesign => src/vai_lab}/examples/xml_files/BO_demo.xml (100%) create mode 100644 src/vai_lab/examples/xml_files/DecisionMaking_demo.xml rename {aidesign => src/vai_lab}/examples/xml_files/KNN-classification_demo.xml (100%) rename {aidesign => src/vai_lab}/examples/xml_files/SVR_demo.xml (100%) create mode 100644 src/vai_lab/examples/xml_files/bayes_opt_demo.xml rename {aidesign => src/vai_lab}/examples/xml_files/canvas_demo.xml (93%) rename {aidesign => src/vai_lab}/examples/xml_files/crystalDesign_v2.xml (100%) rename {aidesign => src/vai_lab}/examples/xml_files/crystalDesign_v3.xml (100%) rename {aidesign => src/vai_lab}/examples/xml_files/k-mean_clustering_demo.xml (100%) rename {aidesign => src/vai_lab}/examples/xml_files/logistic_regression_demo.xml (100%) create mode 100644 src/vai_lab/examples/xml_files/optimisation_demo.xml create mode 100644 src/vai_lab/examples/xml_files/pybullet_env_example.xml rename {aidesign => src/vai_lab}/examples/xml_files/random_forest_class_demo.xml (73%) rename {aidesign => src/vai_lab}/examples/xml_files/regression_demo.xml (100%) rename {aidesign => src/vai_lab}/examples/xml_files/ridge-scalar-ridge_demo.xml (98%) rename {aidesign => src/vai_lab}/examples/xml_files/ridge_regression_demo.xml (100%) rename {aidesign => src/vai_lab}/examples/xml_files/scalar_demo.xml (91%) rename {aidesign => src/vai_lab}/examples/xml_files/scaler-lasso_demo.xml (100%) rename {aidesign => src/vai_lab}/examples/xml_files/user_feedback_demo.xml (93%) create mode 100644 src/vai_lab/run_pipeline.py create mode 100644 src/vai_lab/tests/__init__.py create mode 100644 src/vai_lab/tests/test_examples.py create mode 100644 src/vai_lab/tests/test_launch.py rename {aidesign => src/vai_lab}/utils/AID_core.py (80%) rename {aidesign => src/vai_lab}/utils/UserInterfaceClass.py (62%) rename {aidesign => src/vai_lab}/utils/__init__.py (100%) rename {aidesign => src/vai_lab}/utils/plugins/MainPage.py (88%) rename {aidesign => src/vai_lab}/utils/plugins/__init__.py (100%) rename {aidesign => src/vai_lab}/utils/plugins/aidCanvas.py (95%) rename {aidesign => src/vai_lab}/utils/plugins/dataLoader.py (94%) rename {aidesign => src/vai_lab}/utils/plugins/pluginCanvas.py (91%) rename {aidesign => src/vai_lab}/utils/plugins/progressTracker.py (51%) rename {aidesign => src/vai_lab}/utils/plugins/startpage.py (99%) rename {aidesign => src/vai_lab}/utils/resources/Assets/ADDIcon.ico (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/ADDIcon_name.png (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/AIDIcon.ico (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/AIDIcon_name.png (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/AIFRED.png (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/Hospital.xml (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/UFAIcon.ico (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/UFAIcon_name.png (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/VAIL.ico (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/VAILabs.png (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/VAILabsIcon.ico (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/back_arrow.png (100%) rename {aidesign => src/vai_lab}/utils/resources/Assets/forw_arrow.png (100%) rename {aidesign => src/vai_lab}/utils/resources/example_radiography_images/00028173_005.png (100%) rename {aidesign => src/vai_lab}/utils/resources/example_radiography_images/00028179_000.png (100%) rename {aidesign => src/vai_lab}/utils/resources/example_radiography_images/00028198_002.png (100%) rename {aidesign => src/vai_lab}/utils/resources/example_radiography_images/00028208_035.png (100%) rename {aidesign => src/vai_lab}/utils/resources/example_radiography_images/00028211_021.png (100%) rename {aidesign => src/vai_lab}/utils/resources/example_radiography_images/00028253_000.png (100%) rename {aidesign => src/vai_lab}/utils/resources/example_radiography_images/00028279_000.png (100%) create mode 100644 src/vai_lab/utils/tests/__init__.py diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 00000000..e592d3dd --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,26 @@ +name: Docs +on: [push, pull_request, workflow_dispatch] +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install . + python -m pip install sphinx sphinx-rtd-theme myst-parser + - name: Sphinx build + run: | + sphinx-apidoc --templatedir docs/templates/apidoc -o docs/source src/vai_lab + sphinx-build -M html docs/source docs/build + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + with: + publish_branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs/build/html + force_orphan: true \ No newline at end of file diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml new file mode 100644 index 00000000..e7337f16 --- /dev/null +++ b/.github/workflows/pythonpackage.yml @@ -0,0 +1,47 @@ +name: CI Tests + +on: + pull_request: + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + required: true + default: 'warning' + tags: + description: 'Test scenario tags' + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.10", "3.11"] + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + architecture: x64 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install . + - name: Test with pytest + # uses: GabrielBB/xvfb-action@v1.6 + uses: hankolsen/xvfb-action@dcb076c1c3802845f73bb6fe14a009d8d3377255 + with: + run: | + pip install pytest + pip install pytest-cov + pytest --cov=./src/vai_lab -v --cov-report=xml:./coverage.xml --cov-report term-missing + + + # Note: hankolsen/xvfb-action@dcb076c1c3802845f73bb6fe14a009d8d3377255 is a + # PR waiting to be merged to GabrielBB/xvfb-action@v1.6 to update xvfb-action + # to node.js 16. After the merge, switch back to GabrielBB/xvfb-action@v1.6 \ No newline at end of file diff --git a/.gitignore b/.gitignore index b59e92f6..13f4543d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ **/__pycache__ **/.vscode +*.code-workspace **/*.pyc *.py[cod] *$py.class @@ -72,7 +73,9 @@ instance/ .scrapy # Sphinx documentation -docs/_build/ +docs/build/ +docs/source/vai_lab*.rst +docs/source/modules.rst # PyBuilder .pybuilder/ @@ -119,6 +122,7 @@ celerybeat.pid .venv env/ venv/ +venv*/ ENV/ env.bak/ venv.bak/ diff --git a/README.md b/README.md index 6c3ed770..e8f19667 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,14 @@ -# Virtual Artificially Intelligent Laboratories (VAI Labs) -![AIDBANNER](./aidesign/utils/resources/Assets/readme_images/VAIL_banner_image.png) +[![CI Tests](https://img.shields.io/github/actions/workflow/status/AaltoPML/VAI-Lab/pythonpackage.yml?branch=main&label=CI%20Test&logo=github)](https://github.com/AaltoPML/VAI-Lab/actions/workflows/documentation.yml)[![Docs Test](https://img.shields.io/github/actions/workflow/status/AaltoPML/VAI-Lab/documentation.yml?branch=main&label=Docs&logo=github)](https://github.com/AaltoPML/VAI-Lab/actions/workflows/documentation.yml) +[![PyPI Version](https://img.shields.io/pypi/v/vai-lab?color=blue)](https://pypi.org/project/vai-lab/) [![Python Versions](https://img.shields.io/pypi/pyversions/vai-lab?color=blue)](https://pypi.org/project/vai-lab/) [![Wheel](https://img.shields.io/pypi/wheel/vai-lab)](https://pypi.org/project/vai-lab/) [![License](https://img.shields.io/pypi/l/vai-lab)](https://pypi.org/project/vai-lab/) -VAIL is a modular, easy-to-use framework for Virtual Laboraties for science and design, where Artifical Intelligence assists the user in their goals. + + +# Virtual Artificially Intelligent Laboratories (VAI-Lab) + +![VAILBANNER](https://raw.githubusercontent.com/AaltoPML/VAI-Lab/main/imgs/VAIL_banner_image.png) + +VAI-Lab is a modular, easy-to-use framework for Virtual Laboraties for science and design, where Artifical Intelligence assists the user in their goals. > **Warning** > This project is currently a work in progress and is intended for wider use when a full release is made. @@ -15,244 +21,69 @@ VAIL is a modular, easy-to-use framework for Virtual Laboraties for science and # How to Contribute -The aim of this framework is to be a community effort that will benefit the science, engineering and more. +The aim of this framework is to be a community effort that will benefit science, engineering and more. We are actively seeking contribution in the form of users, testers, developers, and anyone else who would like to contribute. - - If you have methods which can be added to the framework, [get in touch](#Get-in-Touch)! - - If you think this framework will be useful to your research, [get in touch](#Get-in-Touch)! - - If want to get invovled in development, [get in touch](#Get-in-Touch)! - - Noticed a bug or other issue? [get in touch](#Get-in-Touch)! + - If you have methods which can be added to the framework, [get in touch](https://github.com/AaltoPML/VAI-Lab#get-in-touch)! + - If you think this framework will be useful to your research, [get in touch](https://github.com/AaltoPML/VAI-Lab#get-in-touch)! + - If want to get invovled in development, [get in touch](https://github.com/AaltoPML/VAI-Lab#get-in-touch)! + - Noticed a bug or other issue? [get in touch](https://github.com/AaltoPML/VAI-Lab#get-in-touch)! # How it Works -The VAIL framework uses a modular, plugin-based architecture. +The VAI-Lab framework uses a modular, plugin-based architecture. -```mermaid -stateDiagram-v2 - - VAIL --> Module1 - VAIL --> Module2 - VAIL --> Module_n - Module1 --> plugin_1.1 - Module1 --> plugin_1.2 - Module1 --> plugin_1.m - Module2 --> plugin_2.1 - Module2 --> plugin_2.2 - Module2 --> plugin_2.m - Module_n --> plugin_m.1 - Module_n --> plugin_m.2 - Module_n --> plugin_m.n -``` +![PLUGINDIAGRAM](https://raw.githubusercontent.com/AaltoPML/VAI-Lab/main/imgs/VAIL_plugin_diagram.png) Each module represents a process (e.g. Modelling) and each plugin is a specific implementation of that process (e.g. linear regression). Modules can be chained, looped and modified in real-time to create a highly customisable framework for the user's requirements. -## Installation +# Installation + +## Installing from package + +To install the latest pip release: + +``` +pip install vai-lab +``` + +## Installing from source Clone this repository via HTTPS: ```bash -git clone https://github.com/AaltoPML/VAI-labs.git +git clone https://github.com/AaltoPML/VAI-lab.git ``` OR SSH: ```bash -git clone git@github.com:AaltoPML/VAI-labs.git +git clone git@github.com:AaltoPML/VAI-lab.git ``` Change directory ```bash -cd VAI-labs +cd VAI-lab ``` Create a virtual environment and activate it using venv ```bash python3 -m venv venv && source venv/bin/activate ``` -or using conda -```bash -conda create --name aidesign python=3.8 && conda activate aidesign -``` -Upgrade pip and install flit +or, alternatively, using conda environment ```bash -python3 -m pip install -U pip flit +conda create --channel conda-forge --name vai_lab python=3.10 gcc cxx-compiler && conda activate vai_lab ``` -Install the package +Upgrade pip and install the package ```bash -flit install -``` - - - -## Launching and Examples - -To launch the framework with the GUI: - -```python -import aidesign as ai - -core = ai.Core() -core.run() -``` - -or to execute an existing config file: - -```python -import aidesign as ai - -core = ai.Core() -core.load_config_file("") -core.run() -``` - -### Examples - -Pre-made [examples](https://github.com/AaltoPML/ai-assisted-framework-design/tree/main/aidesign/examples/xml_files) show the syntax and form of the config files and pipeline as a whole, and are the best way to get started. - -Some basic use-cases are provided among many others: - - [user_feedback_demo.xml](https://github.com/AaltoPML/ai-assisted-framework-design/blob/main/aidesign/examples/xml_files/user_feedback_demo.xml) Demonstrates manual image classification of chest X-rays - - [canvas_demo.xml](https://github.com/AaltoPML/ai-assisted-framework-design/blob/main/aidesign/examples/xml_files/canvas_demo.xml) Launches the canvas state-action pair visualiser and editor - - [regression_demo.xml](https://github.com/AaltoPML/ai-assisted-framework-design/blob/main/aidesign/examples/xml_files/regression_demo.xml) Demonstrates simple linear regression on a small set of sample data - -#### Launching examples: - -To demonstrate the syntax for launching examples using `user_feedback_demo.xml`: - -```python -import aidesign as ai - -core = ai.Core() -core.load_config_file(("./examples","xml_files",'user_feedback_demo.xml')) -core.run() +python3 -m pip install -U pip && python3 -m pip install . ``` -> Config file paths can be passed as path stings, a list or tuple of directory paths. +# Running Unit Tests -Absolute paths, as well paths relative to the library's base directory can be used. -For library-relative paths, starting a path definition with `"./"` defaults to the directory of `/ai-assisted-framework-design/aidesign/` - - -# Defining Pipelines in GUI - -The VAIL module allows to define a pipeline and the relations within by drawing a flowchart on a canvas. This canvas always starts with an `initialiser` module and an `output` module and allows to define any number of modules between these two. To do so, the user needs to define the modules and the relations between them. - -![PipelineLoop](./aidesign/utils/resources/Assets/readme_images/VAIL_GUI_screenshot.png) - -### Modules -At this moment, there are 7 possible modules for VAIL. `initialiser` and `output` are compulsory for the pipeline, the rest of them can be freely placed in the canvas. These are: - - `Data processing`. - - `Modelling`. - - `Decision making`. - - `User Feedback Adaptation`. - - `Input data`. - -If you click on a module and drag it you can modify its position on the canvas. -Finally, they can be deleted by clicking on the desired module and then clicking on the `Delete selection` button. - -### Module joining -Each module object has a number of circles that can be used to join two modules. The initially clicked circle will be identified as the parent and the second one as the child (the output of the father is fed to the input of the child). There can be only one connection from each circle. As of this version, if you need to edit an existing connection you need to delete one of the connected modules. - -### Loops -If you click on the canvas and drag, you can draw a rectangle that defines which modules are inside the loop. Upon releasing the button you are requested to input what type of loop you want and what condition should be fulfilled to end the loop. - -## Pipeline uploading via XML file - -The pipeline can also be defined uploading an existing XML file. The structure of the XML file is described in the Back-end section. - -# Plugin Examples - -### `manual_input` -Requires the user to indicate to which class the specified data corresponds to. -In the current example, the model needs to classify images and the model requires expert knowledge for specific images. -The user needs to indicate which classes correspond to the image and save the results to send them to the model. - -### `canvas_input` -Requires the user to give feedback to state-action pairs. -It opens a tab for each interactable object in the model and either requires adding new state-action samples or to modify the existing ones. -In the current example, the model has two interactable objects that require feedback in two forms: (1) an _angle_ for the state and for the action or (2) a tuple of _Cartesian coordinates_ for the state and for the action. It has been adapted to be able to give feedback to any number of objects. These, at the same time, can be either `sliding` or `rotating` objects. Specifically, `sliding` refers to objects that need Cartesian feedback in a two-dimensional space, while `rotating` refers to objects that require an angle. In order to give feedback, you can choose to either move the corresponding state-action pairs on the canvas or directly edit the tree view display. This last option results in an automatic update on the canvas of the state-action location. - -## Defining a pipeline in XML - -The pipeline structure is defined between the `pipeline` tags: -```XML - - ... - -``` - -### Initialise -The `Initialise` tag is the dedicated entry point to the pipeline. No other entry points can be declared. - -Current options: - - `name`: attribute for user defined name - - `initial_data`: element for declaring directory for initial data - - `relationships`: User defined names of modules this one is connected to - -Example from [canvas_demo.xml](https://github.com/AaltoPML/ai-assisted-framework-design/blob/main/aidesign/examples/xml_files/canvas_demo.xml) -```XML - - - - - - - - -``` - -### Loops -Loop tags are used to iterate over a given set of modules until a condition is met. Loops can be nested and named. - -See [basic_operation.py](https://github.com/AaltoPML/ai-assisted-framework-design/blob/main/aidesign/examples/xml_files/basic_operation.xml) for full example. -Current options: - - `type`: what variety of loop will this be: `for`, `while`, `manual`(user defined stopping condition on-the-fly) - - `condition`: Termination condition for the loop. I'm not sure how to deal with the criteria for `while` loops - - `name`: User defined name for loop -```XML - - ... - -``` - -### Modules -Modules are declared by tags matching their names, e.g. the GUI module is loaded with the `GUI` tag: - -Required: - - `name`: Unique user defined name for module, so can be referenced later - - `plugin`: The type of plugin to be loaded into the module, along with associated options. - - `relationships`: User-defined names of the `parent` modules which this module receives data from and `child` modules that this module passes data to. - -Example from [ridge_regression_demo.xml](https://github.com/AaltoPML/ai-assisted-framework-design/blob/main/aidesign/examples/xml_files/ridge_regression_demo.xml): -```XML - - - - - - - - 0.02 - - - -``` - -## Data Definition - -Data is loaded from existing files in either the `Initialiser` or `Input Data` modules and is specified using the `inputdata` tags. - -Example from [ridge_regression_demo.xml](https://github.com/AaltoPML/ai-assisted-framework-design/blob/main/aidesign/examples/xml_files/ridge_regression_demo.xml): - -```XML - - - - - - +Unit tests are run with pytest with +```bash +pytest ``` -### Writing Data -Two methods are given to add data to the XML file. One for modules (`append_pipeline_module_to_file`) and one for data structures (`append_data_structure_field_to_file`). - # Feature and Release Schedule :calendar: - [ ] October/ November 2022: Public repo, API fixing, Testing @@ -264,4 +95,4 @@ Two methods are given to add data to the XML file. One for modules (`append_pipe If you would like contribute, test, give feedback, or ask questions about this framework, we'd like to hear from you! Email us at: - Chris McGreavy, chris.mcgreavy [at] aalto.fi -- Carlos Sevilla-Salcedo, carlos.sevillasalcedo [at] aalto.fi +- Carlos Sevilla-Salcedo, carlos.sevillasalcedo [at] aalto.fi \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/binarizer.py b/aidesign/DataProcessing/plugins/binarizer.py deleted file mode 100644 index e4761698..00000000 --- a/aidesign/DataProcessing/plugins/binarizer.py +++ /dev/null @@ -1,47 +0,0 @@ -from aidesign._plugin_templates import DataProcessingT - -import pandas as pd -from sklearn.preprocessing import Binarizer as model - -_PLUGIN_READABLE_NAMES = {"Binarizer":"default","binarizer":"alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"threshold": "float"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore - -class Binarizer(DataProcessingT): - """ - Binarize data (set feature values to 0 or 1) according to a threshold - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self,data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame(self.proc.transform(self.X_tst))) - return data \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/integral.py b/aidesign/DataProcessing/plugins/integral.py deleted file mode 100644 index 58d04beb..00000000 --- a/aidesign/DataProcessing/plugins/integral.py +++ /dev/null @@ -1,47 +0,0 @@ -from numpy import trapz as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = {"Integral": "default", - "integral": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "Other"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore - -class Integral(DataProcessingT): - """ - Calculate integral of array using the composite trapezoidal rule - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - # self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - # super().configure(config) - return - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - # self.cleaned_options = self._clean_solver_options() - # self.proc.set_params(**cleaned_options) - # self.proc.fit(self.X) - return - - def transform(self,data): - data.append_data_column("X", pd.DataFrame(model(self.X))) - return data \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/kbinsdiscretizer.py b/aidesign/DataProcessing/plugins/kbinsdiscretizer.py deleted file mode 100644 index 3e2e26ab..00000000 --- a/aidesign/DataProcessing/plugins/kbinsdiscretizer.py +++ /dev/null @@ -1,47 +0,0 @@ -from aidesign._plugin_templates import DataProcessingT - -import pandas as pd -from sklearn.preprocessing import KBinsDiscretizer as model - -_PLUGIN_READABLE_NAMES = {"KBinsDiscretizer":"default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"n_bins": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore - -class KBinsDiscretizer(DataProcessingT): - """ - Bin continuous data into interval - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self,data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame(self.proc.transform(self.X_tst))) - return data \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/labelbinarizer.py b/aidesign/DataProcessing/plugins/labelbinarizer.py deleted file mode 100644 index a9908045..00000000 --- a/aidesign/DataProcessing/plugins/labelbinarizer.py +++ /dev/null @@ -1,45 +0,0 @@ -from sklearn.preprocessing import LabelBinarizer as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = {"LabelBinarizer":"default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"neg_label": "int", "pos_label": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore - -class LabelBinarizer(DataProcessingT): - """ - Binarize labels in a one-vs-all fashion - """ - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self,data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame(self.proc.transform(self.X_tst))) - return data \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/labelencoder.py b/aidesign/DataProcessing/plugins/labelencoder.py deleted file mode 100644 index 594c4c20..00000000 --- a/aidesign/DataProcessing/plugins/labelencoder.py +++ /dev/null @@ -1,46 +0,0 @@ -from sklearn.preprocessing import LabelEncoder as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = {"LabelEncoder":"default","LE":"alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore - -class LabelEncoder(DataProcessingT): - """ - Encode target labels with value between 0 and n_classes-1 - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self,data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame(self.proc.transform(self.X_tst))) - return data \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/maxabsscaler.py b/aidesign/DataProcessing/plugins/maxabsscaler.py deleted file mode 100644 index c513aee0..00000000 --- a/aidesign/DataProcessing/plugins/maxabsscaler.py +++ /dev/null @@ -1,46 +0,0 @@ -from sklearn.preprocessing import MaxAbsScaler as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = {"MaxAbsScaler":"default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "scaler"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore - -class MaxAbsScaler(DataProcessingT): - """ - Scale each feature by its maximum absolute value - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self,data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame(self.proc.transform(self.X_tst))) - return data \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/minmaxscaler.py b/aidesign/DataProcessing/plugins/minmaxscaler.py deleted file mode 100644 index a156adee..00000000 --- a/aidesign/DataProcessing/plugins/minmaxscaler.py +++ /dev/null @@ -1,47 +0,0 @@ -from sklearn.preprocessing import MinMaxScaler as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = {"MinMaxScaler":"default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "scaler"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"feature_range": "tuple"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore - -class MinMaxScaler(DataProcessingT): - """ - This estimator scales and translates each feature individually such that it\n - is in the given range on the training set, e.g. between zero and one - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self,data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame(self.proc.transform(self.X_tst))) - return data \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/multilabelbinarizer.py b/aidesign/DataProcessing/plugins/multilabelbinarizer.py deleted file mode 100644 index 0c66f8eb..00000000 --- a/aidesign/DataProcessing/plugins/multilabelbinarizer.py +++ /dev/null @@ -1,46 +0,0 @@ -from sklearn.preprocessing import MultiLabelBinarizer as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = {"MultiLabelBinarizer":"default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"classes": "array-like"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore - -class MultiLabelBinarizer(DataProcessingT): - """ - Transform between iterable of iterables and a multilabel format - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self,data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame(self.proc.transform(self.X_tst))) - return data \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/normalizer.py b/aidesign/DataProcessing/plugins/normalizer.py deleted file mode 100644 index b8273e5d..00000000 --- a/aidesign/DataProcessing/plugins/normalizer.py +++ /dev/null @@ -1,51 +0,0 @@ -from aidesign._plugin_templates import DataProcessingT -from aidesign._types import DataInterface - -from sklearn.preprocessing import Normalizer as model -import pandas as pd - -_PLUGIN_READABLE_NAMES = {"Normalizer": "default", - "Norm": "alias", "normalizer": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "scaler"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"norm": "str"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X", "Y", "X_tst", 'Y_tst'} # type:ignore - - -class Normalizer(DataProcessingT): - """ - Normalize samples individually to unit norm - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self, data: DataInterface) -> DataInterface: - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame( - self.proc.transform(self.X_tst))) - return data diff --git a/aidesign/DataProcessing/plugins/onehotencoder.py b/aidesign/DataProcessing/plugins/onehotencoder.py deleted file mode 100644 index 127735f2..00000000 --- a/aidesign/DataProcessing/plugins/onehotencoder.py +++ /dev/null @@ -1,46 +0,0 @@ -from sklearn.preprocessing import OneHotEncoder as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = {"OneHotEncoder":"default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"categories": "array-like"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore - -class OneHotEncoder(DataProcessingT): - """ - Encode categorical features as a one-hot numeric array - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self,data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame(self.proc.transform(self.X_tst))) - return data \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/ordinalencoder.py b/aidesign/DataProcessing/plugins/ordinalencoder.py deleted file mode 100644 index f1d2c347..00000000 --- a/aidesign/DataProcessing/plugins/ordinalencoder.py +++ /dev/null @@ -1,48 +0,0 @@ -from sklearn.preprocessing import OrdinalEncoder as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = {"OrdinalEncoder": "default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"categories": "array-like"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X", "Y", "X_tst", 'Y_tst'} # type:ignore - - -class OrdinalEncoder(DataProcessingT): - """ - Encode categorical features as an integer array - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self, data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame( - self.proc.transform(self.X_tst))) - return data diff --git a/aidesign/DataProcessing/plugins/quantiletransformer.py b/aidesign/DataProcessing/plugins/quantiletransformer.py deleted file mode 100644 index 472f7358..00000000 --- a/aidesign/DataProcessing/plugins/quantiletransformer.py +++ /dev/null @@ -1,49 +0,0 @@ -from sklearn.preprocessing import QuantileTransformer as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = { - "QuantileTransformer": "default", "Quantile": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"n_quantiles": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X", "Y", "X_tst", 'Y_tst'} # type:ignore - - -class QuantileTransformer(DataProcessingT): - """ - Transform features using quantiles information - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self, data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame( - self.proc.transform(self.X_tst))) - return data diff --git a/aidesign/DataProcessing/plugins/standardscaler.py b/aidesign/DataProcessing/plugins/standardscaler.py deleted file mode 100644 index 34904d0e..00000000 --- a/aidesign/DataProcessing/plugins/standardscaler.py +++ /dev/null @@ -1,49 +0,0 @@ -from sklearn.preprocessing import StandardScaler as model -from aidesign._plugin_templates import DataProcessingT -import pandas as pd - -_PLUGIN_READABLE_NAMES = { - "StandardScaler": "default", "standardscaler": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "scaler"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"with_mean": "bool"} # type:ignore -_PLUGIN_REQUIRED_DATA = {} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X", "Y", "X_tst", 'Y_tst'} # type:ignore - - -class StandardScaler(DataProcessingT): - """ - Standardize features by removing the mean and scaling to unit variance - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self, data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame( - self.proc.transform(self.X_tst))) - return data diff --git a/aidesign/DecisionMaking/Modelling_core.py b/aidesign/DecisionMaking/Modelling_core.py deleted file mode 100644 index 97a81d24..00000000 --- a/aidesign/DecisionMaking/Modelling_core.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -class Modelling(object): - def __init__(self): - self.node_name = None - self.plugin_name = None - self.output_data = None - - def set_input_data(self, data): - self.input_data = data - - def set_target_data(self, data): - self.target_data = data - - def _load_plugin(self): - pass - - def set_plugin_name(self, plugin_name): - self.plugin_name = plugin_name - self._load_plugin() - - def set_options(self, options): - self.options = options - - def solve(self): - # send the input data to the modelling plugin and assign it to an output property - pass - - def get_result(self): - return self.output_data diff --git a/aidesign/Modelling/plugins/affinitypropagation.py b/aidesign/Modelling/plugins/affinitypropagation.py deleted file mode 100644 index 2f0b529e..00000000 --- a/aidesign/Modelling/plugins/affinitypropagation.py +++ /dev/null @@ -1,61 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.cluster import AffinityPropagation as model - -_PLUGIN_READABLE_NAMES = {"Birch": "default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "clustering"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"damping": "float"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"Y", "X_tst", 'Y_tst'} # type:ignore - - -class AffinityPropagation(ModellingPluginT): - """ - Perform Affinity Propagation Clustering of data - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/bayesianridge.py b/aidesign/Modelling/plugins/bayesianridge.py deleted file mode 100644 index a4e29dbb..00000000 --- a/aidesign/Modelling/plugins/bayesianridge.py +++ /dev/null @@ -1,61 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.linear_model import BayesianRidge as model - -_PLUGIN_READABLE_NAMES = {"BayesianRidge": "default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"n_iter": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class BayesianRidge(ModellingPluginT): - """ - Bayesian ridge regression - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/Modelling/plugins/birch.py b/aidesign/Modelling/plugins/birch.py deleted file mode 100644 index 4b426d3c..00000000 --- a/aidesign/Modelling/plugins/birch.py +++ /dev/null @@ -1,62 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.cluster import Birch as model - -_PLUGIN_READABLE_NAMES = {"Birch": "default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "clustering"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"n_clusters": "int", # type:ignore - "threshold": "float"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"Y", "X_tst", 'Y_tst'} # type:ignore - - -class Birch(ModellingPluginT): - """ - Implements the BIRCH clustering algorithm - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/decisiontreeclassifier.py b/aidesign/Modelling/plugins/decisiontreeclassifier.py deleted file mode 100644 index 33d4f140..00000000 --- a/aidesign/Modelling/plugins/decisiontreeclassifier.py +++ /dev/null @@ -1,62 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.tree import DecisionTreeClassifier as model - -_PLUGIN_READABLE_NAMES = {"DecissionTreeClassifier": "default", - "DTClassifier": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"max_depth": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class DecisionTreeClassifier(ModellingPluginT): - """ - A decision tree classifier - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/decisiontreeregressor.py b/aidesign/Modelling/plugins/decisiontreeregressor.py deleted file mode 100644 index 64cb7d80..00000000 --- a/aidesign/Modelling/plugins/decisiontreeregressor.py +++ /dev/null @@ -1,62 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.tree import DecisionTreeRegressor as model - -_PLUGIN_READABLE_NAMES = {"DecisionTreeRegressor": "default", - "DTregressor": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"max_depth": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class DecisionTreeRegressor(ModellingPluginT): - """ - A decision tree regressor - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/elasticnet.py b/aidesign/Modelling/plugins/elasticnet.py deleted file mode 100644 index 1621561e..00000000 --- a/aidesign/Modelling/plugins/elasticnet.py +++ /dev/null @@ -1,62 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.linear_model import ElasticNet as model - -_PLUGIN_READABLE_NAMES = {"ElasticNet": "default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float", - "l1_ratio": "float"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class ElasticNet(ModellingPluginT): - """ - Linear regression with combined L1 and L2 priors as regularizer - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X_tst, Y_tst, sample_weight=None): - """Return the coefficient of determination - :param X_tst : array-like of shape (n_samples, n_features) - :param Y_tst : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X_tst, Y_tst, sample_weight) diff --git a/aidesign/Modelling/plugins/gpclassifier.py b/aidesign/Modelling/plugins/gpclassifier.py deleted file mode 100644 index 307bf85d..00000000 --- a/aidesign/Modelling/plugins/gpclassifier.py +++ /dev/null @@ -1,64 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.gaussian_process import GaussianProcessClassifier as model - -_PLUGIN_READABLE_NAMES = {"GPClassifier": "default", - "GPC": "alias", - "GaussianProcessClassifier": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"n_restarts_optimizer": "int", - "random_state": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class GPClassifier(ModellingPluginT): - """ - Gaussian process Classifier (GPC) based on Laplace approximation - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/gpregressor.py b/aidesign/Modelling/plugins/gpregressor.py deleted file mode 100644 index c07b3f06..00000000 --- a/aidesign/Modelling/plugins/gpregressor.py +++ /dev/null @@ -1,64 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.gaussian_process import GaussianProcessRegressor as model - -_PLUGIN_READABLE_NAMES = {"GPRegressor": "default", - "GPR": "alias", - "GaussianProcessRegressor": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"n_restarts_optimizer": "int", - "random_state": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class GPRegressor(ModellingPluginT): - """ - Gaussian process regressor - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/kernelridge.py b/aidesign/Modelling/plugins/kernelridge.py deleted file mode 100644 index eb73e53f..00000000 --- a/aidesign/Modelling/plugins/kernelridge.py +++ /dev/null @@ -1,65 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.kernel_ridge import KernelRidge as model - -_PLUGIN_READABLE_NAMES = {"KernelRidge": "default", - "KR": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float", - "kernel": "str", - "gamma": "float", - "degree": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class KernelRidge(ModellingPluginT): - """ - Kernel ridge regression. - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/kmeans.py b/aidesign/Modelling/plugins/kmeans.py deleted file mode 100644 index 0879145b..00000000 --- a/aidesign/Modelling/plugins/kmeans.py +++ /dev/null @@ -1,62 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.cluster import KMeans as model - -_PLUGIN_READABLE_NAMES = {"KMeans": "default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "clustering"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"n_clusters": "int", - "n_init": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"Y", "X_tst", 'Y_tst'} # type:ignore - - -class KMeans(ModellingPluginT): - """ - K-Means clustering - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/knnclassifier.py b/aidesign/Modelling/plugins/knnclassifier.py deleted file mode 100644 index 40dc3610..00000000 --- a/aidesign/Modelling/plugins/knnclassifier.py +++ /dev/null @@ -1,63 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.neighbors import KNeighborsClassifier as model - -_PLUGIN_READABLE_NAMES = {"KNNClassifier": "default", - "KNN-C": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"n_neighbors": "int", - "weights": "str"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class KNNclassifier(ModellingPluginT): - """ - Classifier implementing the k-nearest neighbors vote - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/knnregressor.py b/aidesign/Modelling/plugins/knnregressor.py deleted file mode 100644 index 78e4c78a..00000000 --- a/aidesign/Modelling/plugins/knnregressor.py +++ /dev/null @@ -1,63 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.neighbors import KNeighborsRegressor as model - -_PLUGIN_READABLE_NAMES = {"KNNRegressor": "default", - "KNN-R": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"n_neighbors": "int", - "weights": "str"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class KNNRegressor(ModellingPluginT): - """ - Regression based on k-nearest neighbors - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/Modelling/plugins/lasso.py b/aidesign/Modelling/plugins/lasso.py deleted file mode 100644 index abc8d026..00000000 --- a/aidesign/Modelling/plugins/lasso.py +++ /dev/null @@ -1,60 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.linear_model import Lasso as model - -_PLUGIN_READABLE_NAMES = {"Lasso":"default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X","Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - -class Lasso(ModellingPluginT): - """ - Linear Model trained with L1 prior as regularizer - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/linearregression.py b/aidesign/Modelling/plugins/linearregression.py deleted file mode 100644 index fbb6232d..00000000 --- a/aidesign/Modelling/plugins/linearregression.py +++ /dev/null @@ -1,62 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.linear_model import LinearRegression as model - -_PLUGIN_READABLE_NAMES = {"LinearRegression": "default", - "LR": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class LinearRegression(ModellingPluginT): - """ - Ordinary least squares Linear Regression - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/Modelling/plugins/logisticregression.py b/aidesign/Modelling/plugins/logisticregression.py deleted file mode 100644 index 7ad50423..00000000 --- a/aidesign/Modelling/plugins/logisticregression.py +++ /dev/null @@ -1,63 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.linear_model import LogisticRegression as model - -_PLUGIN_READABLE_NAMES = {"LogisticRegression": "default", - "logit": "alias", - "MaxEnt": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"penalty": "str", "C": "float"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class LogisticRegression(ModellingPluginT): - """ - Logistic regression classification. - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/Modelling/plugins/meanshift.py b/aidesign/Modelling/plugins/meanshift.py deleted file mode 100644 index a2994637..00000000 --- a/aidesign/Modelling/plugins/meanshift.py +++ /dev/null @@ -1,60 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.cluster import MeanShift as model - -_PLUGIN_READABLE_NAMES = {"MeanShift":"default"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "clustering"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"bandwidth": "float"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"Y", "X_tst", 'Y_tst'} # type:ignore - -class MeanShift(ModellingPluginT): - """ - Mean shift clustering using a flat kernel. - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/passiveaggressiveclassifier.py b/aidesign/Modelling/plugins/passiveaggressiveclassifier.py deleted file mode 100644 index 289efa3b..00000000 --- a/aidesign/Modelling/plugins/passiveaggressiveclassifier.py +++ /dev/null @@ -1,62 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.linear_model import PassiveAggressiveClassifier as model - -_PLUGIN_READABLE_NAMES = {"PassiveAggressiveClassifier": "default", - "PassiveAgressive": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"C": "float"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class PassiveAggressiveClassifier(ModellingPluginT): - """ - Passive aggressive classifier - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/Modelling/plugins/perceptron.py b/aidesign/Modelling/plugins/perceptron.py deleted file mode 100644 index 4683321c..00000000 --- a/aidesign/Modelling/plugins/perceptron.py +++ /dev/null @@ -1,63 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.linear_model import Perceptron as model - -_PLUGIN_READABLE_NAMES = {"Perceptron": "default", - "LinearPerceptron": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float", - "penalty": "str"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class Perceptron(ModellingPluginT): - """ - Linear perceptron classification - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/Modelling/plugins/randomforestclassifier.py b/aidesign/Modelling/plugins/randomforestclassifier.py deleted file mode 100644 index dcfdc0c9..00000000 --- a/aidesign/Modelling/plugins/randomforestclassifier.py +++ /dev/null @@ -1,64 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.ensemble import RandomForestClassifier as model - -_PLUGIN_READABLE_NAMES = {"RandomForestClassifier": "default", - "RFclassifier": "alias", - "RFC": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"max_depth": "int", - "n_estimators": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class RandomForestClassifier(ModellingPluginT): - """ - A random forest classifier - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/Modelling/plugins/randomforestregressor.py b/aidesign/Modelling/plugins/randomforestregressor.py deleted file mode 100644 index 77bd729f..00000000 --- a/aidesign/Modelling/plugins/randomforestregressor.py +++ /dev/null @@ -1,64 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.ensemble import RandomForestRegressor as model - -_PLUGIN_READABLE_NAMES = {"RandomForestRegressor": "default", - "RFRegressor": "alias", - "RFR": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"max_depth": "int", - "n_estimators": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class RandomForestRegressor(ModellingPluginT): - """ - A random forest regression - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/Modelling/plugins/ridgeregression.py b/aidesign/Modelling/plugins/ridgeregression.py deleted file mode 100644 index 273834eb..00000000 --- a/aidesign/Modelling/plugins/ridgeregression.py +++ /dev/null @@ -1,62 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.linear_model import Ridge as model - -_PLUGIN_READABLE_NAMES = {"Ridge": "default", - "RidgeRegression": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class RidgeRegression(ModellingPluginT): - """ - Linear least squares with l2 regularization - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) \ No newline at end of file diff --git a/aidesign/Modelling/plugins/svc.py b/aidesign/Modelling/plugins/svc.py deleted file mode 100644 index cce3c59c..00000000 --- a/aidesign/Modelling/plugins/svc.py +++ /dev/null @@ -1,65 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.svm import SVC as model - -_PLUGIN_READABLE_NAMES = {"SVC": "default", - "SupportVectorClassification": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"C": "float", - "kernel": "str", - "gamma": "float", - "degree": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class SVC(ModellingPluginT): - """ - C-Support Vector Classification - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/Modelling/plugins/svr.py b/aidesign/Modelling/plugins/svr.py deleted file mode 100644 index fc1a9ff9..00000000 --- a/aidesign/Modelling/plugins/svr.py +++ /dev/null @@ -1,65 +0,0 @@ -from aidesign._plugin_templates import ModellingPluginT -from sklearn.svm import SVR as model - -_PLUGIN_READABLE_NAMES = {"SVR": "default", - "SupportVectorRegression": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore -_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"C": "float", - "kernel": "str", - "gamma": "float", - "degree": "int"} # type:ignore -_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore -_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore - - -class SVR(ModellingPluginT): - """ - Epsilon-Support Vector Regression - """ - - def __init__(self): - """Initialises parent class. - Passes `globals` dict of all current variables - """ - super().__init__(globals()) - self.clf = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def solve(self): - """Sends params to solver, then runs solver""" - self.clf.set_params(**self._config["options"]) - self.clf.fit(self.X, self.Y) - - def predict(self, data): - """Uses fitted model to predict output of a given Y - :param data: array-like or sparse matrix, shape (n_samples, n_features) - Samples - expected type: aidesign.Data.Data_core.Data - :returns: array, shape (n_samples,) - Returns predicted values. - """ - return self.clf.predict(data) - - def score(self, X, Y, sample_weight=None): - """Return the coefficient of determination - :param X : array-like of shape (n_samples, n_features) - :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) - :param sample_weight : array-like of shape (n_samples,), default=None - Sample weights. - - :returns: score : float R^2` of ``self.predict(X)`` wrt. `y`. - """ - return self.clf.score(X, Y, sample_weight=None) diff --git a/aidesign/__init__.py b/aidesign/__init__.py deleted file mode 100644 index 954d4c59..00000000 --- a/aidesign/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from .Core import Core -# from .Settings import Settings -# from .utils import AID -# from. import utils -# from .UserFeedback import UserFeedback -# from .Modelling import Modelling -# from .GUI import GUI \ No newline at end of file diff --git a/aidesign/examples/X.csv b/aidesign/examples/X.csv deleted file mode 100644 index 7213bdf8..00000000 --- a/aidesign/examples/X.csv +++ /dev/null @@ -1,29 +0,0 @@ -,Cs (%),MA (%) -0,0.0,40.0 -1,78.0,84.0 -2,1.0,98.0 -3,36.0,54.0 -4,78.0,39.0 -5,28.0,54.0 -6,48.0,50.0 -7,7.0,77.0 -8,51.0,94.0 -9,48.0,11.0 -10,64.0,70.0 -11,85.0,82.0 -12,42.0,57.0 -13,71.0,89.0 -14,56.0,44.0 -15,34.0,0.0 -16,1.0,9.0 -17,49.0,70.0 -18,18.0,63.0 -19,11.0,44.0 -20,29.0,24.0 -21,36.0,53.0 -22,85.0,0.0 -23,76.0,60.0 -24,4.0,31.0 -25,58.0,10.0 -26,38.0,10.0 -27,26.0,34.0 diff --git a/aidesign/examples/optimisation/X.csv b/aidesign/examples/optimisation/X.csv deleted file mode 100644 index bb105ddd..00000000 --- a/aidesign/examples/optimisation/X.csv +++ /dev/null @@ -1,29 +0,0 @@ -Cs(%),MA(%) -3.0,21.0 -17.0,13.0 -88.0,41.0 -78.0,55.0 -94.0,13.0 -97.0,22.0 -46.0,45.0 -56.0,42.0 -64.0,71.0 -2.0,49.0 -68.0,36.0 -33.0,68.0 -87.0,65.0 -92.0,81.0 -74.0,50.0 -21.0,59.0 -27.0,0.0 -67.0,68.0 -41.0,8.0 -76.0,65.0 -69.0,23.0 -83.0,5.0 -41.0,98.0 -14.0,97.0 -51.0,31.0 -60.0,36.0 -87.0,24.0 -86.0,18.0 diff --git a/aidesign/examples/xml_files/Hospital_use_case_visual.xml b/aidesign/examples/xml_files/Hospital_use_case_visual.xml deleted file mode 100644 index f8076273..00000000 --- a/aidesign/examples/xml_files/Hospital_use_case_visual.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - (314.5, 75.0) - - - - - - - - - - (117, 180) - - - - - - - - - - (117, 273) - - - - - - - - - - - - - - - (202.0, 154.0, 585.0, 461.0) - - - - - - - - - - - - (257, 326) - - - - - - - - - - (322, 207) - - - - - - - - - - - (454, 325) - - - - - - - - - - - - (354, 419) - - - - - - - - - - (314.5, 525.0) - - - \ No newline at end of file diff --git a/aidesign/examples/xml_files/Kevin_experimentalDesign.xml b/aidesign/examples/xml_files/Kevin_experimentalDesign.xml deleted file mode 100644 index bdb45db6..00000000 --- a/aidesign/examples/xml_files/Kevin_experimentalDesign.xml +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - - [(350.0,50),0,{}] - - - - - - - - - - - [(173,184),2,{0:'d0-u2'}] - - - - - - - - - - - [(173,294),3,{2:'d2-u3'}] - - - - - - - - - - - - [(175,409),4,{3:'d3-u4',5:'l5-r4'}] - - - - - - - - - - - [(543,414),5,{6:'r6-d5'}] - - - - - - - - - - - - [(351,522),6,{4:'d4-l6'}] - - - - - - - - - - [(350.0,650),1,{6:'d6-u1'}] - - - - - - - - - - - - - (48.0,110.0,651.0,572.0) - - - - - - - - - - - [(173,184),2,{0:'d0-u2'}] - - - - - - - - - - - - [(173,294),3,{2:'d2-u3'}] - - - - - - - - - - - - - [(173,404),4,{3:'d3-u4',5:'l5-r4'}] - - - - - - - - - - - - [(536,410),5,{6:'r6-d5'}] - - - - - - - - - - - - - [(351,522),6,{4:'d4-l6'}] - - - - - - - - - - - [(350.0,650),1,{6:'d6-u1'}] - - - - diff --git a/aidesign/examples/xml_files/basic_operation.xml b/aidesign/examples/xml_files/basic_operation.xml deleted file mode 100644 index 82219316..00000000 --- a/aidesign/examples/xml_files/basic_operation.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - ['State_a','Action_a'] - ['State_x','State_y','Action_x','Action_y'] - ['State_x','State_y','Action_x','Action_y'] - - - - - - - - - - - - - - all - - - - - - - - diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..747ffb7b --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..26283a34 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,37 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +import pathlib +import sys + +project = 'VAI-Lab' +copyright = '2023, Chris McGreavy, Carlos Sevilla-Salcedo' +author = 'Chris McGreavy, Carlos Sevilla-Salcedo' + + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.autodoc', # For API + 'sphinx.ext.autosummary', # For API + 'myst_parser', # For importing README.md + ] + +templates_path = ['_templates'] +exclude_patterns = [] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = [] + + diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst new file mode 100644 index 00000000..316d9abe --- /dev/null +++ b/docs/source/getting_started.rst @@ -0,0 +1,280 @@ +Getting Started +=============== + + +Launching +--------- + +From Command Line +^^^^^^^^^^^^^^^^^ + +To launch the framework with the GUI: + +.. code-block:: bash + + vai_lab + + +To launch the framework with an existing config file: + +.. code-block:: bash + + vai_lab --file + + +From Code +^^^^^^^^^ + +To launch the framework with the GUI: + +.. code-block:: python + :linenos: + + import vai_lab as ai + + core = ai.Core() + core.run() + +or to execute an existing config file: + +.. code-block:: python + :linenos: + + import vai_lab as ai + + core = ai.Core() + core.load_config_file("") + core.run() + + +Examples +-------- + +Pre-made `examples `_ show the syntax and form of the config files and pipeline as a whole, and are the best way to get started. + +Some basic use-cases are provided among many others: + - `user_feedback_demo.xml `_ Demonstrates manual image classification of chest X-rays + - `canvas_demo.xml `_ Launches the canvas state-action pair visualiser and editor + - `regression_demo.xml `_ Demonstrates simple linear regression on a small set of sample data + +Launching examples: +^^^^^^^^^^^^^^^^^^^ + +To demonstrate the syntax for launching examples using ``user_feedback_demo.xml``. + +From Command Line +""""""""""""""""" + +.. code-block:: bash + + vai_lab --file ./src/vai_lab/examples/xml_files/user_feedback_demo.xml + +From code +""""""""" + +.. code-block:: python + :linenos: + + import vai_lab as ai + + core = ai.Core() + core.load_config_file(("./examples/xml_files/user_feedback_demo.xml")) + core.run() + +Absolute paths, as well paths relative to the library's base directory can be used. +For library-relative paths, starting a path definition with ``./`` defaults to the directory ``/vai_lab/`` + +In addition to path strings, the config file paths can be passed as lists or tuples of directory paths. Therefore, the above command/code are equivalent to + +.. code-block:: bash + + vai_lab --file ./src/vai_lab/examples xml_files user_feedback_demo.xml + +and + +.. code-block:: python + :linenos: + + import vai_lab as ai + + core = ai.Core() + core.load_config_file(("./examples","xml_files","user_feedback_demo.xml")) + core.run() + +Defining Pipelines in GUI +------------------------- + +The VAIL module allows to define a pipeline and the relations within by drawing a flowchart on a canvas. This canvas always starts with an ``initialiser`` module and an ``output`` module and allows to define any number of modules between these two. To do so, the user needs to define the modules and the relations between them. + +.. image::../../imgs/VAIL_GUI_screenshot.png + :alt: VAIL GUI screenshot + + +Modules +^^^^^^^ + +At this moment, there are 7 possible modules for VAIL. ``initialiser`` and ``output`` are compulsory for the pipeline, the rest of them can be freely placed in the canvas. These are: + - ``Data processing`` + - ``Modelling`` + - ``Decision making`` + - ``User Feedback Adaptation`` + - ``Input data`` + +If you click on a module and drag it you can modify its position on the canvas. + +Finally, they can be deleted by clicking on the desired module and then clicking on the ``Delete selection`` button. + +Connecting Modules +^^^^^^^^^^^^^^^^^^ + +Each module object has a number of circles that can be used to join two modules. The initially clicked circle will be identified as the parent and the second one as the child (the output of the father is fed to the input of the child). There can be only one connection from each circle. As of this version, if you need to edit an existing connection you need to delete one of the connected modules. + +Loops +^^^^^ + +If you click on the canvas and drag, you can draw a rectangle that defines which modules are inside the loop. Upon releasing the button you are requested to input what type of loop you want and what condition should be fulfilled to end the loop. + +Loading from XML +---------------- + +The pipeline can also be defined uploading an existing XML file. The structure of the XML file is described in the Back-end section. + +Plugin Examples +--------------- + +manual_input +^^^^^^^^^^^^ + +Requires the user to indicate to which class the specified data corresponds to. +In the current example, the model needs to classify images and the model requires expert knowledge for specific images. +The user needs to indicate which classes correspond to the image and save the results to send them to the model. + +canvas_input +^^^^^^^^^^^^ + +Requires the user to give feedback to state-action pairs. +It opens a tab for each interactable object in the model and either requires adding new state-action samples or to modify the existing ones. +In the current example, the model has two interactable objects that require feedback in two forms: (1) an *angle* for the state and for the action or (2) a tuple of Cartesian coordinates for the state and for the action. It has been adapted to be able to give feedback to any number of objects. These, at the same time, can be either ``sliding`` or ``rotating`` objects. Specifically, ``sliding`` refers to objects that need Cartesian feedback in a two-dimensional space, while ``rotating`` refers to objects that require an angle. In order to give feedback, you can choose to either move the corresponding state-action pairs on the canvas or directly edit the tree view display. This last option results in an automatic update on the canvas of the state-action location. + +Defining a pipeline in XML +-------------------------- + +The pipeline structure is defined between the ``pipeline`` tags: + +.. code-block:: xml + :linenos: + + + ... + + +Initialising +^^^^^^^^^^^^ + +The ``Initialise`` tag is the dedicated entry point to the pipeline. No other entry points can be declared. + +Current options: + - ``name``: attribute for user defined name + - ``initial_data``: element for declaring directory for initial data + - ``relationships``: User defined names of modules this one is connected to + +Example from `canvas_demo.xml `_: + +.. code-block:: xml + :linenos: + + + + + + + + + + +Loops +^^^^^ + +Loop tags are used to iterate over a given set of modules until a condition is met. Loops can be nested and named. + +See `basic_operation.xml `_ for full example. +Current options: + + - ``type``: what variety of loop will this be: ``for``, ``while``, ``manual`` (user defined stopping condition on-the-fly) + - ``condition``: Termination condition for the loop. I'm not sure how to deal with the criteria for ``while`` loops + - ``name``: User defined name for loop + +.. code-block:: xml + :linenos: + + + ... + + + +Modules +^^^^^^^ + +Modules are declared by tags matching their names, e.g. the GUI module is loaded with the ``GUI`` tag: + +Required: + - ``name``: Unique user defined name for module, so can be referenced later + - ``plugin``: The type of plugin to be loaded into the module, along with associated options. + - ``relationships``: User-defined names of the ``parent`` modules which this module receives data from and ``child`` modules that this module passes data to. + +Example from `ridge_regression_demo.xml `_: + +.. code-block:: xml + :linenos: + + + + + + + + + 0.02 + + + + +Data Definition +^^^^^^^^^^^^^^^ + +Data is loaded from existing files in either the ``Initialiser`` or ``Input Data`` modules and is specified using the ``inputdata`` tags. + +Example from `ridge_regression_demo.xml `_`: + +.. code-block:: xml + :linenos: + + + + + + + + +Writing Data +^^^^^^^^^^^^ + +Two methods are given to add data to the XML file. One for modules (``append_pipeline_module_to_file``) and one for data structures (``append_data_structure_field_to_file``). + +Building Documentation +---------------------- + +To build documentation locally from source, install the required tools + +.. code-block:: bash + + python3 -m pip install sphinx sphinx-rtd-theme myst-parser + +and run + +.. code-block:: bash + + sphinx-apidoc --templatedir docs/templates/apidoc -o docs/source src/vai_lab + sphinx-build -M html docs/source docs/build + +The generated HTML pages are in ``docs/build/html``. \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..1987d344 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,28 @@ +.. VAI-Lab documentation master file, created by + sphinx-quickstart on Thu Dec 8 15:28:40 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to VAI-Lab's Documentation! +=================================== + +VAI-Lab is a modular, easy-to-use framework for Virtual Laboraties for science and design, where Artifical Intelligence assists the user in their goals. + + +Contents +-------- + +.. toctree:: + :maxdepth: 1 + + Intro and Installation + Getting Started + API + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/docs/source/readme.rst b/docs/source/readme.rst new file mode 100644 index 00000000..de92cbb2 --- /dev/null +++ b/docs/source/readme.rst @@ -0,0 +1,5 @@ +Intro & Installation +==================== + +.. include:: ../../README.md + :parser: myst_parser.sphinx_ diff --git a/docs/templates/apidoc/module.rst_t b/docs/templates/apidoc/module.rst_t new file mode 100644 index 00000000..24902785 --- /dev/null +++ b/docs/templates/apidoc/module.rst_t @@ -0,0 +1,9 @@ +{%- if show_headings %} +{{- [basename, "module"] | join(' ') | e | heading }} + +{% endif -%} +.. automodule:: {{ qualname }} +{%- for option in automodule_options %} + :{{ option }}: +{%- endfor %} + diff --git a/docs/templates/apidoc/package.rst_t b/docs/templates/apidoc/package.rst_t new file mode 100644 index 00000000..aa1fd459 --- /dev/null +++ b/docs/templates/apidoc/package.rst_t @@ -0,0 +1,53 @@ +{%- macro automodule(modname, options) -%} +.. automodule:: {{ modname }} +{%- for option in options %} + :{{ option }}: +{%- endfor %} +{%- endmacro %} + +{%- macro toctree(docnames) -%} +.. toctree:: + :maxdepth: {{ maxdepth }} +{% for docname in docnames %} + {{ docname }} +{%- endfor %} +{%- endmacro %} + +{%- if is_namespace %} +{{- [pkgname, "namespace"] | join(" ") | e | heading }} +{% else %} +{{- [pkgname, "package"] | join(" ") | e | heading }} +{% endif %} + +{%- if is_namespace %} +.. py:module:: {{ pkgname }} +{% endif %} + +{%- if modulefirst and not is_namespace %} +{{ automodule(pkgname, automodule_options) }} +{% endif %} + +{%- if subpackages %} + +{{ toctree(subpackages) }} +{% endif %} + +{%- if submodules %} + +{% if separatemodules %} +{{ toctree(submodules) }} +{% else %} +{%- for submodule in submodules %} +{% if show_headings %} +{{- [submodule, "module"] | join(" ") | e | heading(2) }} +{% endif %} +{{ automodule(submodule, automodule_options) }} +{% endfor %} +{%- endif %} +{%- endif %} + +{%- if not modulefirst and not is_namespace %} + + +{{ automodule(pkgname, automodule_options) }} +{% endif %} diff --git a/docs/templates/apidoc/toc.rst_t b/docs/templates/apidoc/toc.rst_t new file mode 100644 index 00000000..f0877eeb --- /dev/null +++ b/docs/templates/apidoc/toc.rst_t @@ -0,0 +1,8 @@ +{{ header | heading }} + +.. toctree:: + :maxdepth: {{ maxdepth }} +{% for docname in docnames %} + {{ docname }} +{%- endfor %} + diff --git a/aidesign/utils/resources/Assets/readme_images/VAIL_GUI_screenshot.png b/imgs/VAIL_GUI_screenshot.png similarity index 100% rename from aidesign/utils/resources/Assets/readme_images/VAIL_GUI_screenshot.png rename to imgs/VAIL_GUI_screenshot.png diff --git a/aidesign/utils/resources/Assets/readme_images/VAIL_banner_image.png b/imgs/VAIL_banner_image.png similarity index 100% rename from aidesign/utils/resources/Assets/readme_images/VAIL_banner_image.png rename to imgs/VAIL_banner_image.png diff --git a/aidesign/utils/resources/Assets/readme_images/VAIL_mainpage_screenshot.png b/imgs/VAIL_mainpage_screenshot.png similarity index 100% rename from aidesign/utils/resources/Assets/readme_images/VAIL_mainpage_screenshot.png rename to imgs/VAIL_mainpage_screenshot.png diff --git a/imgs/VAIL_plugin_diagram.png b/imgs/VAIL_plugin_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..2b43ec6d73882802c960fc57849e0125a3042d73 GIT binary patch literal 87434 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

09>@lX20T6Sm^>y-EAQ{bTcSa4nS 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 literal 0 HcmV?d00001 diff --git a/pybullet.pyi b/pybullet.pyi new file mode 100644 index 00000000..029c8601 --- /dev/null +++ b/pybullet.pyi @@ -0,0 +1,971 @@ +# Adapted from temporary pybullet stubs at: +# https://github.com/rohit-kumar-j/temporary_pybullet_stub/blob/main/pybullet.pyi + + +ACTIVATION_STATE_DISABLE_SLEEPING = 2 +ACTIVATION_STATE_DISABLE_WAKEUP = 32 + +ACTIVATION_STATE_ENABLE_SLEEPING = 1 +ACTIVATION_STATE_ENABLE_WAKEUP = 16 + +ACTIVATION_STATE_SLEEP = 8 + +ACTIVATION_STATE_WAKE_UP = 4 + +AddFileIOAction = 1024 + +B3G_ALT = 65308 +B3G_BACKSPACE = 65305 +B3G_CONTROL = 65307 +B3G_DELETE = 65304 + +B3G_DOWN_ARROW = 65298 + +B3G_END = 65301 +B3G_F1 = 65280 +B3G_F10 = 65289 +B3G_F11 = 65290 +B3G_F12 = 65291 +B3G_F13 = 65292 +B3G_F14 = 65293 +B3G_F15 = 65294 +B3G_F2 = 65281 +B3G_F3 = 65282 +B3G_F4 = 65283 +B3G_F5 = 65284 +B3G_F6 = 65285 +B3G_F7 = 65286 +B3G_F8 = 65287 +B3G_F9 = 65288 +B3G_HOME = 65302 +B3G_INSERT = 65303 + +B3G_LEFT_ARROW = 65295 + +B3G_PAGE_DOWN = 65300 +B3G_PAGE_UP = 65299 + +B3G_RETURN = 65309 + +B3G_RIGHT_ARROW = 65296 + +B3G_SHIFT = 65306 +B3G_SPACE = 32 + +B3G_UP_ARROW = 65297 + +CNSFileIO = 3 + +CONSTRAINT_SOLVER_LCP_DANTZIG = 3 +CONSTRAINT_SOLVER_LCP_PGS = 2 +CONSTRAINT_SOLVER_LCP_SI = 1 + +CONTACT_RECOMPUTE_CLOSEST = 1 + +CONTACT_REPORT_EXISTING = 0 + +COV_ENABLE_DEPTH_BUFFER_PREVIEW = 14 + +COV_ENABLE_GUI = 1 + +COV_ENABLE_KEYBOARD_SHORTCUTS = 9 + +COV_ENABLE_MOUSE_PICKING = 10 + +COV_ENABLE_PLANAR_REFLECTION = 16 + +COV_ENABLE_RENDERING = 7 + +COV_ENABLE_RGB_BUFFER_PREVIEW = 13 + +COV_ENABLE_SEGMENTATION_MARK_PREVIEW = 15 + +COV_ENABLE_SHADOWS = 2 + +COV_ENABLE_SINGLE_STEP_RENDERING = 17 + +COV_ENABLE_TINY_RENDERER = 12 + +COV_ENABLE_VR_PICKING = 5 + +COV_ENABLE_VR_RENDER_CONTROLLERS = 6 + +COV_ENABLE_VR_TELEPORTING = 4 + +COV_ENABLE_WIREFRAME = 3 + +COV_ENABLE_Y_AXIS_UP = 11 + +DIRECT = 2 + +ER_BULLET_HARDWARE_OPENGL = 131072 + +ER_NO_SEGMENTATION_MASK = 4 + +ER_SEGMENTATION_MASK_OBJECT_AND_LINKINDEX = 1 + +ER_TINY_RENDERER = 65536 + +ER_USE_PROJECTIVE_TEXTURE = 2 + +GEOM_BOX = 3 +GEOM_CAPSULE = 7 + +GEOM_CONCAVE_INTERNAL_EDGE = 2 + +GEOM_CYLINDER = 4 + +GEOM_FORCE_CONCAVE_TRIMESH = 1 + +GEOM_HEIGHTFIELD = 9 +GEOM_MESH = 5 +GEOM_PLANE = 6 +GEOM_SPHERE = 2 + +GRAPHICS_CLIENT = 14 +GRAPHICS_SERVER = 15 + +GRAPHICS_SERVER_MAIN_THREAD = 17 + +GRAPHICS_SERVER_TCP = 16 + +GUI = 1 + +GUI_MAIN_THREAD = 8 + +GUI_SERVER = 7 + +IK_DLS = 0 + +IK_HAS_JOINT_DAMPING = 128 + +IK_HAS_NULL_SPACE_VELOCITY = 64 + +IK_HAS_TARGET_ORIENTATION = 32 +IK_HAS_TARGET_POSITION = 16 + +IK_SDLS = 1 + +JOINT_FEEDBACK_IN_JOINT_FRAME = 2 + +JOINT_FEEDBACK_IN_WORLD_SPACE = 1 + +JOINT_FIXED = 4 +JOINT_GEAR = 6 +JOINT_PLANAR = 3 +JOINT_POINT2POINT = 5 +JOINT_PRISMATIC = 1 +JOINT_REVOLUTE = 0 +JOINT_SPHERICAL = 2 + +KEY_IS_DOWN = 1 + +KEY_WAS_RELEASED = 4 +KEY_WAS_TRIGGERED = 2 + +LINK_FRAME = 1 + +MAX_RAY_INTERSECTION_BATCH_SIZE = 16384 + +MESH_DATA_SIMULATION_MESH = 1 + +MJCF_COLORS_FROM_FILE = 512 + +PD_CONTROL = 3 + +POSITION_CONTROL = 2 + +PosixFileIO = 1 + +RemoveFileIOAction = 1025 + +RESET_USE_DEFORMABLE_WORLD = 1 + +RESET_USE_DISCRETE_DYNAMICS_WORLD = 2 + +RESET_USE_SIMPLE_BROADPHASE = 4 + +SENSOR_FORCE_TORQUE = 1 + +SHARED_MEMORY = 3 + +SHARED_MEMORY_GUI = 14 +SHARED_MEMORY_KEY = 12347 +SHARED_MEMORY_KEY2 = 12348 +SHARED_MEMORY_SERVER = 9 + +STABLE_PD_CONTROL = 4 + +STATE_LOGGING_ALL_COMMANDS = 7 + +STATE_LOGGING_CONTACT_POINTS = 5 + +STATE_LOGGING_CUSTOM_TIMER = 9 + +STATE_LOGGING_GENERIC_ROBOT = 1 + +STATE_LOGGING_MINITAUR = 0 + +STATE_LOGGING_PROFILE_TIMINGS = 6 + +STATE_LOGGING_VIDEO_MP4 = 3 + +STATE_LOGGING_VR_CONTROLLERS = 2 + +STATE_LOG_JOINT_MOTOR_TORQUES = 1 + +STATE_LOG_JOINT_TORQUES = 3 + +STATE_LOG_JOINT_USER_TORQUES = 2 + +STATE_REPLAY_ALL_COMMANDS = 8 + +TCP = 5 + +TORQUE_CONTROL = 1 + +UDP = 4 + +URDF_ENABLE_CACHED_GRAPHICS_SHAPES = 1024 + +URDF_ENABLE_SLEEPING = 2048 +URDF_ENABLE_WAKEUP = 262144 + +URDF_GLOBAL_VELOCITIES_MB = 256 + +URDF_GOOGLEY_UNDEFINED_COLORS = 8388608 + +URDF_IGNORE_COLLISION_SHAPES = 2097152 + +URDF_IGNORE_VISUAL_SHAPES = 1048576 + +URDF_INITIALIZE_SAT_FEATURES = 4096 + +URDF_MAINTAIN_LINK_ORDER = 131072 + +URDF_MERGE_FIXED_LINKS = 524288 + +URDF_PRINT_URDF_INFO = 4194304 + +URDF_USE_IMPLICIT_CYLINDER = 128 + +URDF_USE_INERTIA_FROM_FILE = 2 + +URDF_USE_MATERIAL_COLORS_FROM_MTL = 32768 + +URDF_USE_MATERIAL_TRANSPARANCY_FROM_MTL = 65536 + +URDF_USE_SELF_COLLISION = 8 + +URDF_USE_SELF_COLLISION_EXCLUDE_ALL_PARENTS = 32 + +URDF_USE_SELF_COLLISION_EXCLUDE_PARENT = 16 + +URDF_USE_SELF_COLLISION_INCLUDE_PARENT = 8192 + +VELOCITY_CONTROL = 0 + +VISUAL_SHAPE_DATA_TEXTURE_UNIQUE_IDS = 1 + +VISUAL_SHAPE_DOUBLE_SIDED = 4 + +VR_BUTTON_IS_DOWN = 1 + +VR_BUTTON_WAS_RELEASED = 4 +VR_BUTTON_WAS_TRIGGERED = 2 + +VR_CAMERA_TRACK_OBJECT_ORIENTATION = 1 + +VR_DEVICE_CONTROLLER = 1 + +VR_DEVICE_GENERIC_TRACKER = 4 + +VR_DEVICE_HMD = 2 + +VR_MAX_BUTTONS = 64 +VR_MAX_CONTROLLERS = 8 + +WORLD_FRAME = 2 + +ZipFileIO = 2 + +# functions + +def addUserData(bodyUniqueId:int, key, value, linkIndex=-1, visualShapeIndex=-1, physicsClientId=0): # real signature unknown; restored from __doc__ + """ + addUserData(bodyUniqueId, key, value, linkIndex=-1, visualShapeIndex=-1, physicsClientId=0) + Adds or updates a user data entry. Returns user data identifier. + """ + pass + +def addUserDebugLine(lineFromXYZ:list[float],lineToXYZ:list[float],lineColorRGB:list[float],lineWidth:float,lifeTime:float,parentObjectUniqueId:int,parentLinkIndex:int,replaceItemUniqueId:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Add a user debug draw line with lineFrom[3], lineTo[3], lineColorRGB[3], lineWidth, lifeTime. A lifeTime of 0 means permanent until removed. Returns a unique id for the user debug item. """ + pass + +def addUserDebugParameter(paramName:str,rangeMin:float,rangeMax:float,startValue:float,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Add a user debug parameter, such as a slider, that can be controlled using a GUI. """ + pass + +def addUserDebugText(text:str,textPosition:list[float],textColorRGB:list[float],textSize:float,lifeTime:float,textOrientation:list[float],parentObjectUniqueId:int,parentLinkIndex:int,replaceItemUniqueId:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Add a user debug draw line with text, textPosition[3], textSize and lifeTime in seconds A lifeTime of 0 means permanent until removed. Returns a unique id for the user debug item. """ + pass + +def applyExternalForce(objectUniqueId:int,linkIndex:int,forceObj:list[float],posObj:list[float],flags:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ for objectUniqueId, linkIndex (-1 for base/root link), apply a force [x,y,z] at the a position [x,y,z], flag to select FORCE_IN_LINK_FRAME or WORLD_FRAME coordinates """ + pass + +def applyExternalTorque(objectUniqueId:int,linkIndex:int,forceObj:list[float],posObj:list[float],flags:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ for objectUniqueId, linkIndex (-1 for base/root link), apply a force [x,y,z] at the a position [x,y,z], flag to select FORCE_IN_LINK_FRAME or WORLD_FRAME coordinates """ + pass + +def calculateInverseDynamics(bodyUniqueId:int,objPositions:list[float],objVelocities:list[float],objAccelerations:list[float],physicsClientId:int,*args, **kwargs): # real signature unknown + """ Given an object id, joint positions, joint velocities and joint accelerations, compute the joint forces using Inverse Dynamics """ + pass + +def calculateInverseKinematics(bodyUniqueId:int,endEffectorLinkIndex:int,targetPosition:list[float],targetOrientation:list[float],lowerLimits:list[float],upperLimits:list[float],jointRanges:list[float],restPoses:list[float],jointDamping:list[float],solver:int,currentPosition:list[float],maxNumIterations:int,residualThreshold:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Inverse Kinematics bindings: Given an object id, current joint positions and target position for the end effector,compute the inverse kinematics and return the new joint state """ + pass + +def calculateInverseKinematics2(bodyUniqueId:int,endEffectorLinkIndices:list[int],targetPositions:list[float],targetOrientation:list[float],lowerLimits:list[float],upperLimits:list[float],jointRanges:list[float],restPoses:list[float],jointDamping:list[float],solver:int,currentPosition:list[float],maxNumIterations:int,residualThreshold:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Inverse Kinematics bindings: Given an object id, current joint positions and target positions for the end effectors,compute the inverse kinematics and return the new joint state """ + pass + +def calculateJacobian(bodyUniqueId:int, linkIndex:int, localPosition:list[float], objPositions:list[float], objVelocities:list[float], objAccelerations:list[float], physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + linearJacobian, angularJacobian = calculateJacobian(bodyUniqueId, linkIndex, localPosition, objPositions, objVelocities, objAccelerations, physicsClientId=0) + Compute the jacobian for a specified local position on a body and its kinematics. + Args: + bodyIndex - a scalar defining the unique object id. + linkIndex - a scalar identifying the link containing the local point. + localPosition - a list of [x, y, z] of the coordinates defined in the link frame. + objPositions - a list of the joint positions. + objVelocities - a list of the joint velocities. + objAccelerations - a list of the joint accelerations. + Returns: + linearJacobian - a list of the partial linear velocities of the jacobian. + angularJacobian - a list of the partial angular velocities of the jacobian. + """ + pass + +def calculateMassMatrix(bodyUniqueId:int, objPositions:list[float], physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + massMatrix = calculateMassMatrix(bodyUniqueId, objPositions, physicsClientId=0) + Compute the mass matrix for an object and its chain of bodies. + Args: + bodyIndex - a scalar defining the unique object id. + objPositions - a list of the joint positions. + Returns: + massMatrix - a list of lists of the mass matrix components. + """ + pass + +def calculateVelocityQuaternion(*args, **kwargs): # real signature unknown + """ Compute the angular velocity given start and end quaternion and delta time. """ + pass + +def changeConstraint(parentBodyUniqueId:int,parentLinkIndex:int,childBodyUniqueId:int,childLinkIndex:int,jointType:int,jointAxis:list[float],parentFramePosition:list[float],childFramePosition:list[float],parentFrameOrientation:list[float],childFrameOrientation:list[float],physicsClientId:int,*args, **kwargs): # real signature unknown + """ Change some parameters of an existing constraint, such as the child pivot or child frame orientation, using its unique id. """ + pass + +def changeDynamics(bodyUniqueId:int,linkIndex:int,mass:float,lateralFriction:float,spinningFriction:float,rollingFriction:float,restitution:float,linearDamping:float,angularDamping:float,contactStiffness:float,contactDamping:float,frictionAnchor:int,localInertiaDiagnoal:list,ccdSweptSphereRadius:float,contactProcessingThreshold:float,activationState:int,jointDamping:float,anisotropicFriction:float,maxJointVelocity:float,collisionMargin:float,jointLowerLimit:float,jointUpperLimit:float,jointLimitForce:float,physicsClientId:int,*args, **kwargs): # real signature unknown + """ change dynamics information such as mass, lateral friction coefficient. """ + pass + +def changeTexture(*args, **kwargs): # real signature unknown + """ Change a texture file. """ + pass + +def changeVisualShape(objectUniqueId:int,linkIndex:int,shapeIndex:int,textureUniqueId:int,rgbaColor:list[float],specularColor:list[float],physicsClientId:int,*args, **kwargs): # real signature unknown + """ Change part of the visual shape information for one object. """ + pass + +def computeDofCount(*args, **kwargs): # real signature unknown + """ computeDofCount returns the number of degrees of freedom, including 7 degrees of freedom for the base in case of floating base """ + pass + +def computeProjectionMatrix(left:float,right:float,bottom:float,up:float,near:float,far:float,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Compute a camera projection matrix from screen left/right/bottom/top/near/far values """ + pass + +def computeProjectionMatrixFOV(fov:float,aspect:float,nearVal:float,farVal:float,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Compute a camera projection matrix from fov, aspect ratio, near, far values """ + pass + +def computeViewMatrix(cameraEyePosition:list[float],cameraTargetPosition:list[float],cameraUpVector:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Compute a camera viewmatrix from camera eye, target position and up vector """ + pass + +def computeViewMatrixFromYawPitchRoll(cameraTargetPosition:list[float],distance:float,yaw:float,pitch:float,roll:float,upAxisIndex:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Compute a camera viewmatrix from camera eye, target position and up vector """ + pass + +def configureDebugVisualizer(flag:int,enable:int,lightPosition:list[float],shadowMapResolution:int,shadowMapWorldSize:int,shadowMapIntensity:float,rgbBackground:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ For the 3D OpenGL Visualizer, enable/disable GUI, shadows. """ + pass + +def connect(method, key=None, options=''): # real signature unknown; restored from __doc__ + """ + connect(method, key=SHARED_MEMORY_KEY, options='') + options='--background_color_red=1.0 --background_color_green=0.0 --background_color_blue=0.0' + connect(method, hostname='localhost', port=1234, options='') + Connect to an existing physics server (using shared memory by default). + """ + pass + +def createCollisionShape(shapeType:int,radius:float,halfExtents:list[float],height:float,fileName:list[float],meshScale:list[float],planeNormal:list[float],flags:int,collisionFramePosition:list[float],collisionFrameOrientationosition:list[float],vertices:list[float],indices:list[int],heightfieldTextureScaling:float,numHeightfieldRows:int,numHeightfieldColumns:int,replaceHeightfieldIndex:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Create a collision shape. Returns a non-negative (int) unique id, if successfull, negative otherwise. """ + pass + +def createCollisionShapeArray(*args, **kwargs): # real signature unknown + """ Create collision shapes. Returns a non-negative (int) unique id, if successfull, negative otherwise. """ + pass + +def createConstraint(parentBodyUniqueId:int,parentLinkIndex:int,childBodyUniqueId:int,childLinkIndex:int,jointType:int,jointAxis:list[float],parentFramePosition:list[float],childFramePosition:list[float],parentFrameOrientation:list[float],childFrameOrientation:list[float],physicsClientId:int,*args, **kwargs): # real signature unknown + """ Create a constraint between two bodies. Returns a (int) unique id, if successfull. """ + pass + +def createMultiBody(baseMass:float,baseCollisionShapeIndex:int,baseVisualShapeIndex:int,basePosition:list[float],baseOrientation:list[float],baseInertialFramePosition:list[float],baseInertialFrameOrientation:list[float],linkMasses:list[float],linkCollisionShapeIndices:list[int],linkVisualShapeIndices:list[int],linkPositions:list[float],linkOrientations:list[float],linkInertialFramePositions:list[float],linkInertialFrameOrientations:list[float],linkParentIndices:list[int],linkJointTypes:list[int],linkJointAxis:list[float],useMaximalCoordinates:int,flags:int,batchPositions:list[float],physicsClientId:int=0,*args, **kwargs) -> int : # real signature unknown + """ Create a multi body. Returns a non-negative (int) unique id, if successfull, negative otherwise. """ + pass + +def createSoftBodyAnchor(*args, **kwargs): # real signature unknown + """ Create an anchor (attachment) between a soft body and a rigid or multi body. """ + pass + +def createVisualShape(shapeType:int,radius:float,halfExtents:list[float],height:float,fileName:list[float],meshScale:list[float],planeNormal:list[float],flags:int,visualFramePosition:list[float],visualFrameOrientationosition:list[float],vertices:list[float],indices:list[int],heightfieldTextureScaling:float,numHeightfieldRows:int,numHeightfieldColumns:int,replaceHeightfieldIndex:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Create a visual shape. Returns a non-negative (int) unique id, if successfull, negative otherwise. """ + pass + +def createVisualShapeArray(*args, **kwargs): # real signature unknown + """ Create visual shapes. Returns a non-negative (int) unique id, if successfull, negative otherwise. """ + pass + +def disconnect(physicsClientId=0): # real signature unknown; restored from __doc__ + """ + disconnect(physicsClientId=0) + Disconnect from the physics server. + """ + pass + +def enableJointForceTorqueSensor(bodyUniqueId:int,jointIndex:int,enableSensor:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Enable or disable a joint force/torque sensor measuring the joint reaction forces. """ + pass + +def executePluginCommand(pluginUniqueId:int,textArgument:str,intArgs:list[int],floatArgs:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Execute a command, implemented in a plugin. """ + pass + +def getAABB(bodyUniqueId:int,linkIndex:int,physicsClientId:int=0,*args, **kwargs) -> list[float]: # real signature unknown + """ Get the axis aligned bound box min and max coordinates in world space. """ + pass + +def getAPIVersion(physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get version of the API. Compatibility exists for connections using the same API version. Make sure both client and server use the same number of bits (32-bit or 64bit). """ + pass + +def getAxisAngleFromQuaternionquaternion(quaternion:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Compute the quaternion from axis and angle representation. """ + pass + +def getAxisDifferenceQuaternion(*args, **kwargs): # real signature unknown + """ Compute the velocity axis difference from two quaternions. """ + pass + +def getBasePositionAndOrientation(objectUniqueId:int,physicsClientId:int,*args, **kwargs) -> list[float]: # real signature unknown + """ Get the world position and orientation of the base of the object. (x,y,z) position vector and (x,y,z,w) quaternion orientation. """ + pass + +def getBaseVelocity(objectUniqueId:int,physicsClientId:int,*args, **kwargs) -> list[float]: # real signature unknown + """ Get the linear and angular velocity of the base of the object in world space coordinates. (x,y,z) linear velocity vector and (x,y,z) angular velocity vector. """ + pass + +def getBodyInfo(*args, **kwargs): # real signature unknown + """ Get the body info, given a body unique id. """ + pass + +def getBodyUniqueId(*args, **kwargs): # real signature unknown + """ getBodyUniqueId is used after connecting to server with existing bodies.Get the unique id of the body, given a integer range [0.. number of bodies). """ + pass + +def getCameraImage(width:int,height:int,viewMatrix:float,projectionMatrix:float,lightDirection:list[float],lightColor:list[float],lightDistance:float,shadow:int,lightAmbientCoeff:float,lightDiffuseCoeff:float,lightSpecularCoeff:float,renderer:int,flags:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Render an image (given the pixel resolution width, height, camera viewMatrix , projectionMatrix, lightDirection, lightColor, lightDistance, shadow, lightAmbientCoeff, lightDiffuseCoeff, lightSpecularCoeff, and renderer), and return the 8-8-8bit RGB pixel data and floating point depth values """ + pass + +def getClosestPoints(bodyA:int,bodyB:int,distance:float,linkIndexA:int,linkIndexB:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Compute the closest points between two objects, if the distance is below a given threshold.Input is two objects unique ids and distance threshold. """ + pass + +def getCollisionShapeData(objectUniqueId:int,linkIndex:int,physicsClientId:int,*args, **kwargs): # real signature unknown + """ Return the collision shape information for one object. """ + pass + +def getConnectionInfo(physicsClientId=0): # real signature unknown; restored from __doc__ + """ + getConnectionInfo(physicsClientId=0) + Return if a given client id is connected, and using what method. + """ + pass + +def getConstraintInfo(constraintUniqueId:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get the user-created constraint info, given a constraint unique id. """ + pass + +def getConstraintState(constraintUniqueId:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get the user-created constraint state (applied forces), given a constraint unique id. """ + pass + +def getConstraintUniqueId(*args, **kwargs): # real signature unknown + """ Get the unique id of the constraint, given a integer index in range [0.. number of constraints). """ + pass + +def getContactPoints(*args, **kwargs): # real signature unknown + """ Return existing contact points after the stepSimulation command. Optional arguments one or two object unique ids, that need to be involved in the contact. """ + pass + +def getDebugVisualizerCamera(*args, **kwargs): # real signature unknown + """ Get information about the 3D visualizer camera, such as width, height, view matrix, projection matrix etc. """ + pass + +def getDifferenceQuaternion(quaternionStart:list[float],quaternionEnd:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Compute the quaternion difference from two quaternions. """ + pass + +def getDynamicsInfo(bodyUniqueId:int,linkIndex:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get dynamics information such as mass, lateral friction coefficient. """ + pass + +def getEulerFromQuaternion(quaternion:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Convert quaternion [x,y,z,w] to Euler [roll, pitch, yaw] as in URDF/SDF convention """ + pass + +def getJointInfo(bodyUniqueId:int,jointIndex:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get the name and type info for a joint on a body. """ + pass + +def getJointState(bodyUniqueId:int,jointIndex:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get the state (position, velocity etc) for a joint on a body. """ + pass + +def getJointStateMultiDof(bodyUniqueId:int,jointIndex:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get the state (position, velocity etc) for a joint on a body. (supports planar and spherical joints) """ + pass + +def getJointStates(bodyUniqueId:int,jointIndices:list[int],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get the state (position, velocity etc) for multiple joints on a body. """ + pass + +def getJointStatesMultiDof(bodyUniqueId:int,jointIndex:list[int],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get the states (position, velocity etc) for multiple joint on a body. (supports planar and spherical joints) """ + pass + +def getKeyboardEvents(physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get keyboard events, keycode and state (KEY_IS_DOWN, KEY_WAS_TRIGGERED, KEY_WAS_RELEASED) """ + pass + +def getLinkState(bodyUniqueId:int, linkIndex:int, computeLinkVelocity:int=0, computeForwardKinematics:int=0, physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + position_linkcom_world, world_rotation_linkcom, + position_linkcom_frame, frame_rotation_linkcom, + position_frame_world, world_rotation_frame, + linearVelocity_linkcom_world, angularVelocity_linkcom_world + = getLinkState(bodyUniqueId, linkIndex, computeLinkVelocity=0, + computeForwardKinematics=0, physicsClientId=0) + Provides extra information such as the Cartesian world coordinates center of mass (COM) of the link, relative to the world reference frame. + """ + pass + +def getLinkStates(bodyUniqueId:int, linkIndices:list[int], computeLinkVelocity:int=0, computeForwardKinematics:int=0, physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ same as getLinkState except it takes a list of linkIndices """ + pass + +def getMatrixFromQuaternion(quaternion:list[float],*args, **kwargs): # real signature unknown + """ Compute the 3x3 matrix from a quaternion, as a list of 9 values (row-major) """ + pass + +def getMeshData(bodyUniqueId:int,linkIndex:int,collisionShapeIndex:int,flags:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get mesh data. Returns vertices etc from the mesh. """ + pass + +def getMouseEvents(physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get mouse events, event type and button state (KEY_IS_DOWN, KEY_WAS_TRIGGERED, KEY_WAS_RELEASED) """ + pass + +def getNumBodies(*args, **kwargs): # real signature unknown + """ Get the number of bodies in the simulation. """ + pass + +def getNumConstraints(*args, **kwargs): # real signature unknown + """ Get the number of user-created constraints in the simulation. """ + pass + +def getNumJoints(bodyUniqueId:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get the number of joints for an object. """ + pass + +def getNumUserData(bodyUniqueId_physicsClientId=0): # real signature unknown; restored from __doc__ + """ + getNumUserData(bodyUniqueId physicsClientId=0) + Retrieves the number of user data entries in a body. + """ + pass + +def getOverlappingObjects(aabbMin:list[float],aabbMax:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Return all the objects that have overlap with a given axis-aligned bounding box volume (AABB).Input are two vectors defining the AABB in world space [min_x,min_y,min_z],[max_x,max_y,max_z]. """ + pass + +def getPhysicsEngineParameters(*args, **kwargs): # real signature unknown + """ Get the current values of internal physics engine parameters """ + pass + +def getQuaternionFromAxisAngle(*args, **kwargs): # real signature unknown + """ Compute the quaternion from axis and angle representation. """ + pass + +def getQuaternionFromEuler(eulerAngle:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Convert Euler [roll, pitch, yaw] as in URDF/SDF convention, to quaternion [x,y,z,w] """ + pass + +def getQuaternionSlerp(*args, **kwargs): # real signature unknown + """ Compute the spherical interpolation given a start and end quaternion and an interpolation value in range [0..1] """ + pass + +def getUserData(userDataId, physicsClientId=0): # real signature unknown; restored from __doc__ + """ + getUserData(userDataId, physicsClientId=0) + Returns the user data value. + """ + pass + +def getUserDataId(bodyUniqueId, key, linkIndex=-1, visualShapeIndex=-1, physicsClientId=0): # real signature unknown; restored from __doc__ + """ + getUserDataId(bodyUniqueId, key, linkIndex=-1, visualShapeIndex=-1, physicsClientId=0) + Retrieves the userDataId given the key and optionally link and visual shape index. + """ + pass + +def getUserDataInfo(bodyUniqueId, userDataIndex, physicsClientId=0): # real signature unknown; restored from __doc__ + """ + getUserDataInfo(bodyUniqueId, userDataIndex, physicsClientId=0) + Retrieves the key and the identifier of a user data as (userDataId, key, bodyUniqueId, linkIndex, visualShapeIndex). + """ + pass + +def getVisualShapeData(objectUniqueId:int,flags:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Return the visual shape information for one object. """ + pass + +def getVREvents(deviceTypeFilter:int,allAnalogAxes:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Get Virtual Reality events, for example to track VR controllers position/buttons """ + pass + +def invertTransform(position:list[float],orientation:list[float],*args, **kwargs): # real signature unknown + """ Invert a transform, provided as [position], [quaternion]. """ + pass + +def isConnected(physicsClientId=0): # real signature unknown; restored from __doc__ + """ + isConnected(physicsClientId=0) + Return if a given client id is connected. + """ + pass + +def isNumpyEnabled(*args, **kwargs): # real signature unknown + """ return True if PyBullet was compiled with NUMPY support. This makes the getCameraImage API faster """ + pass + +def loadBullet(*args, **kwargs): # real signature unknown + """ Load a world from a .bullet file. """ + pass + +def loadMJCF(fileName:str,useMaximalCoordinates:int,globalScaling:float,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Load multibodies from an MJCF file. """ + pass + +def loadPlugin(pluginPath:str,postFix:str,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Load a plugin, could implement custom commands etc. """ + pass + +def loadSDF(fileName:str,useMaximalCoordinates:int,globalScaling:float,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Load multibodies from an SDF file. """ + pass + +def loadSoftBody(fileName:str,basePosition:list[float],baseOrientation:list[float],scale:float,mass:float,collisionMargin,useMassSpring:bool,useBendingSprings:bool,useNeoHookean:bool,springElasticStiffness:float,springDampingStiffness:float,springDampingAllDirections:float,springBendingStiffness:float,NeoHookeanMu:float,NeoHookeanLambda:float,NeoHookeanDamping:float,frictionCoeff:float,useFaceContact:bool,useSelfCollision:bool,repulsionStiffness:float,*args, **kwargs): # real signature unknown + """ Load a softbody from an obj file. """ + pass + +def loadTexture(*args, **kwargs): # real signature unknown + """ Load texture file. """ + pass + +def loadURDF(fileName:str, basePosition:list[float]=None, baseOrientation:list[float]=None, useMaximalCoordinates:int=0, useFixedBase:bool=False, flags:int=0, globalScaling:float=1.0, physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + bodyUniqueId = loadURDF(fileName, basePosition=[0.,0.,0.], baseOrientation=[0.,0.,0.,1.], useMaximalCoordinates=0, useFixedBase=0, flags=0, globalScaling=1.0, physicsClientId=0) + Create a multibody by loading a URDF file. + """ + pass + +def multiplyTransforms(positionA:list[float],orientationA:list[float],positionB:list[float],orientationB:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Multiply two transform, provided as [position], [quaternion]. """ + pass + +def performCollisionDetection(physicsClientId=0): # real signature unknown; restored from __doc__ + """ + performCollisionDetection(physicsClientId=0) + Update AABBs, compute overlapping pairs and contact points. stepSimulation also includes this already. + """ + pass + +def rayTest(rayFromPosition:list[float],rayToPosition:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Cast a ray and return the first object hit, if any. Takes two arguments (from_position [x,y,z] and to_position [x,y,z] in Cartesian world coordinates """ + pass + +def rayTestBatch(rayFromPosition:list[float],rayToPosition:list[float],parentObjectUniqueId:int,parentLinkIndex:int,numThreads:int,reportHitNumber:int,collisionFilterMask:int,fractionEpsilon:float,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Cast a batch of rays and return the result for each of the rays (first object hit, if any. or -1) Takes two required arguments (list of from_positions [x,y,z] and a list of to_positions [x,y,z] in Cartesian world coordinates) and one optional argument numThreads to specify the number of threads to use to compute the ray intersections for the batch. Specify 0 to let Bullet decide, 1 (default) for single core execution, 2 or more to select the number of threads to use. """ + pass + +def readUserDebugParameter(itemUniqueId:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Read the current value of a user debug parameter, given the user debug item unique id. """ + pass + +def removeAllUserDebugItems(*args, **kwargs): # real signature unknown + """ remove all user debug draw items """ + pass + +def removeAllUserParameters(physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ remove all user debug parameters (sliders, buttons) """ + pass + +def removeBody(bodyUniqueId:int,*args, **kwargs): # real signature unknown + """ Remove a body by its body unique id. """ + pass + +def removeCollisionShape(*args, **kwargs): # real signature unknown + """ Remove a collision shape. Only useful when the collision shape is not used in a body (to perform a getClosestPoint query). """ + pass + +def removeConstraint(userConstraintUniqueId:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Remove a constraint using its unique id. """ + pass + +def removeState(*args, **kwargs): # real signature unknown + """ Remove a state created using saveState by its state unique id. """ + pass + +def removeUserData(userDataId, physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + removeUserData(userDataId, physicsClientId=0) + Removes a user data entry. + """ + pass + +def removeUserDebugItem(itemUniqueId:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ remove a user debug draw item, giving its unique id """ + pass + +def renderImage(*args, **kwargs): # real signature unknown + """ obsolete, please use getCameraImage and getViewProjectionMatrices instead """ + pass + +def resetBasePositionAndOrientation(bodyUniqueId:int,posObj:list[float],ornObj:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Reset the world position and orientation of the base of the object instantaneously, not through physics simulation. (x,y,z) position vector and (x,y,z,w) quaternion orientation. """ + pass + +def resetBaseVelocity(objectUniqueId:int,linearVelocity:list[float],angularVelocity:list[float],physicsClientId:int,*args, **kwargs): # real signature unknown + """ Reset the linear and/or angular velocity of the base of the object in world space coordinates. linearVelocity (x,y,z) and angularVelocity (x,y,z). """ + pass + +def resetDebugVisualizerCamera(cameraDistance:float,cameraYaw:float,cameraPitch:float,cameraTargetPosition:list[float],physicsClientId:int=0) -> None: # real signature unknown + """ For the 3D OpenGL Visualizer, set the camera distance, yaw, pitch and target position. + Example: pybullet.resetDebugVisualizerCamera( cameraDistance=3, cameraYaw=30,cameraPitch=52, cameraTargetPosition=[0,0,0])""" + pass + +def resetJointState(objectUniqueId:int, jointIndex:int, targetValue:float, targetVelocity:int=0, physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + resetJointState(objectUniqueId, jointIndex, targetValue, targetVelocity=0, physicsClientId=0) + Reset the state (position, velocity etc) for a joint on a body instantaneously, not through physics simulation. + """ + pass + +def resetJointStateMultiDof(objectUniqueId:int, jointIndex:int, targetValue:int, targetVelocity=0, physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + resetJointStateMultiDof(objectUniqueId, jointIndex, targetValue, targetVelocity=0, physicsClientId=0) + Reset the state (position, velocity etc) for a joint on a body instantaneously, not through physics simulation. + """ + pass + +def resetJointStatesMultiDof(objectUniqueId:int, jointIndices:list[int], targetValues:list[int], targetVelocities:list[int], physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + resetJointStatesMultiDof(objectUniqueId, jointIndices, targetValues, targetVelocities=0, physicsClientId=0) + Reset the states (position, velocity etc) for multiple joints on a body instantaneously, not through physics simulation. + """ + pass + +def resetMeshData(*args, **kwargs): # real signature unknown + """ Reset mesh data. Only implemented for deformable bodies. """ + pass + +def resetSimulation(flags:int,physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + resetSimulation(physicsClientId=0) + Reset the simulation: remove all objects and start from an empty world. + """ + pass + +def resetVisualShapeData(*args, **kwargs): # real signature unknown + """ Obsolete method, kept for backward compatibility, use changeVisualShapeData instead. """ + pass + +def restoreState(fileName:str,stateId:int,clientServerId:int,*args, **kwargs): # real signature unknown + """ Restore the full state of an existing world. """ + pass + +def rotateVector(*args, **kwargs): # real signature unknown + """ Rotate a vector using a quaternion. """ + pass + +def saveBullet(fileName:str,stateId:int,clientServerId:int,*args, **kwargs): # real signature unknown + """ Save the full state of the world to a .bullet file. """ + pass + +def saveState(fileName:str,stateId:int,clientServerId:int,*args, **kwargs): # real signature unknown + """ Save the full state of the world to memory. """ + pass + +def saveWorld(filename:str,clientServerId:int): # real signature unknown; restored from __doc__ + """ Save a approximate Python file to reproduce the current state of the world: saveWorld(filename). (very preliminary and approximately) """ + pass + +def setAdditionalSearchPath(*args, **kwargs): # real signature unknown + """ Set an additional search path, used to load URDF/SDF files. """ + pass + +def setCollisionFilterGroupMask(bodyUniqueId:int,linkIndexA:int,collisionFilterGroup:int,collisionFilterMask:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Set the collision filter group and the mask for a body. """ + pass + +def setCollisionFilterPair(bodyUniqueIdA:int,bodyUniqueIdB:int,linkIndexA:int,linkIndexB:int,enableCollision:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Enable or disable collision detection between two object links.Input are two object unique ids and two link indices and an enumto enable or disable collisions. """ + pass + +def setDebugObjectColor(objectUniqueId:int,linkIndex:int,objectDebugColorRGB:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Override the wireframe debug drawing color for a particular object unique id / link index.If you ommit the color, the custom color will be removed. """ + pass + +def setDefaultContactERP(defaultContactERP, physicsClientId=0): # real signature unknown; restored from __doc__ + """ + setDefaultContactERP(defaultContactERP, physicsClientId=0) + Set the amount of contact penetration Error Recovery Paramater (ERP) in each time step. This is an tuning parameter to control resting contact stability. This value depends on the time step. + """ + pass + +def setGravity(gravX:float, gravY:float, gravZ:float, physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + setGravity(gravX, gravY, gravZ, physicsClientId=0) + Set the gravity acceleration (x,y,z). + """ + pass + +def setInternalSimFlags(*args, **kwargs): # real signature unknown + """ This is for experimental purposes, use at own risk, magic may or not happen """ + pass + +def setJointMotorControl(this_is_obsolete_use_motorctrl_2,*args, **kwargs): # real signature unknown + """ This (obsolete) method cannot select non-zero physicsClientId, use setJointMotorControl2 instead.Set a single joint motor control mode and desired target value. There is no immediate state change, stepSimulation will process the motors. """ + pass + +def setJointMotorControl2(bodyIndex:int,jointIndex:int,controlMode:int,targetPosition:float,targetVelocity:float,force:float,positionGain:float,velocityGain:float,maxVelocity:float,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Set a single joint motor control mode and desired target value. There is no immediate state change, stepSimulation will process the motors. """ + pass + +def setJointMotorControlArray(bodyIndex:int,jointIndices:list[int],controlMode:int,targetPositions:list[float],targetVelocities:list[float],forces:list[float],positionGains:list[float],velocityGains:list[float],maxVelocities:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Set an array of motors control mode and desired target value. There is no immediate state change, stepSimulation will process the motors.This is similar to setJointMotorControl2, with jointIndices as a list, and optional targetPositions, targetVelocities, forces, kds and kps as listsUsing setJointMotorControlArray has the benefit of lower calling overhead. """ + pass + +def setJointMotorControlMultiDof(bodyUniqueId:int,jointIndex:int,controlMode:int,targetPosition:list[float],targetVelocity:list[float],force:list[float],positionGain:float,velocityGain:float,maxVelocity:float,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Set a single joint motor control mode and desired target value. There is no immediate state change, stepSimulation will process the motors.This method sets multi-degree-of-freedom motor such as the spherical joint motor. """ + pass + +def setJointMotorControlMultiDofArray(bodyUniqueId:int,jointIndices:list[int],controlMode:int,targetPositions:list[float],targetVelocities:list[float],forces:list[float],positionGains:list[float],velocityGains:list[float],maxVelocities:list[float],physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Set control mode and desired target values for multiple motors. There is no immediate state change, stepSimulation will process the motors.This method sets multi-degree-of-freedom motor such as the spherical joint motor. """ + pass + +def setPhysicsEngineParameter(fixedTimeStep:float,numSolverIterations:int,useSplitImpulse:int,splitImpulsePenetrationThreshold:float,numSubSteps:int,collisionFilterMode:int,contactBreakingThreshold:float,maxNumCmdPer1ms:int,enableFileCaching:int,restitutionVelocityThreshold:float,erp:float,contactERP:float,frictionERP:float,enableConeFriction:int,deterministicOverlappingPairs:int,allowedCcdPenetration:float,jointFeedbackMode:int,solverResidualThreshold:float,contactSlop:float,enableSAT:int,constraintSolverType:int,globalCFM:float,minimumSolverIslandSize:int,reportSolverAnalytics:int,warmStartingFactor:float,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Set some internal physics engine parameter, such as cfm or erp etc. """ + pass + +def setRealTimeSimulation(enableRealTimeSimulation:int, physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + setRealTimeSimulation(enableRealTimeSimulation, physicsClientId=0) + Enable or disable real time simulation (using the real time clock, RTC) in the physics server. Expects one integer argument, 0 or 1 + """ + pass + +def setTimeOut(*args, **kwargs): # real signature unknown + """ Set the timeOut in seconds, used for most of the API calls. """ + pass + +def setTimeStep(timestep:float, physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + setTimeStep(timestep, physicsClientId=0) + Set the amount of time to proceed at each call to stepSimulation. (unit is seconds, typically range is 0.01 or 0.001) + """ + pass + +def setVRCameraState(rootPosition:list[float],rootOrientation:list[float],trackObject:list[float],trackObjectFlag:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Set properties of the VR Camera such as its root transform for teleporting or to track objects (camera inside a vehicle for example). """ + pass + +def startStateLogging(loggingType:int,fileName:str,objectUniqueIds:list[int],maxLogDof:int,bodyUniqueIdA:int,bodyUniqueIdB:int,linkIndexA:int,linkIndexB:int,deviceTypeFilter:int,logFlags:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Start logging of state, such as robot base position, orientation, joint positions etc. Specify loggingType (STATE_LOGGING_MINITAUR, STATE_LOGGING_GENERIC_ROBOT, STATE_LOGGING_VR_CONTROLLERS, STATE_LOGGING_CONTACT_POINTS, etc), fileName, optional objectUniqueId, maxLogDof, bodyUniqueIdA, bodyUniqueIdB, linkIndexA, linkIndexB. Function returns int loggingUniqueId """ + pass + +def stepSimulation(physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + stepSimulation(physicsClientId=0) + Step the simulation using forward dynamics. + """ + pass + +def stopStateLogging(*args, **kwargs): # real signature unknown + """ Stop logging of robot state, given a loggingUniqueId. """ + pass + +def submitProfileTiming(*args, **kwargs): # real signature unknown + """ Add a custom profile timing that will be visible in performance profile recordings on the physics server.On the physics server (in GUI and VR mode) you can press 'p' to start and/or stop profile recordings """ + pass + +def syncBodyInfo(physicsClientId=0): # real signature unknown; restored from __doc__ + """ + syncBodyInfo(physicsClientId=0) + Update body and constraint/joint information, in case other clients made changes. + """ + pass + +def syncUserData(bodyUniqueIds=list[int], physicsClientId:int=0): # real signature unknown; restored from __doc__ + """ + syncUserData(bodyUniqueIds=[], physicsClientId=0) + Update user data, in case other clients made changes. + """ + pass + +def unloadPlugin(*args, **kwargs): # real signature unknown + """ Unload a plugin, given the pluginUniqueId. """ + pass + +def unsupportedChangeScaling(*args, **kwargs): # real signature unknown + """ Change the scaling of the base of an object.Warning: unsupported rudimentary feature that has many limitations. """ + pass + +def vhacd(fileNameIn:str,fileNameOut:str,fileNameLog:str,concavity:float,alpha:float,beta:float,gamma:float,minVolumePerCH:float,resolution:int,maxNumVerticesPerCH:int,depth:int,planeDownsampling:int,convexhullDownsampling:int,pca:int,mode:int,convexhullApproximation:int,physicsClientId:int=0,*args, **kwargs): # real signature unknown + """ Compute volume hierarchical convex decomposition of an OBJ file. """ + pass + +# classes + +class error(Exception): + # no doc + def __init__(self, *args, **kwargs): # real signature unknown + pass + + __weakref__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default + """list of weak references to the object (if defined)""" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index bf0ee307..a3b61abd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = ["flit_core >=3.2,<4"] build-backend = "flit_core.buildapi" [project] -name = "aidesign" +name = "vai_lab" authors = [ {name = "Chris McGreavy", email = "chris.mcgreavy@aalto.fi"}, {name = "Carlos Sevilla Salcedo", email = "carlos.sevillasalcedo@aalto.fi"} @@ -12,19 +12,31 @@ authors = [ readme = "README.md" classifiers = [ "License :: OSI Approved :: MIT License", + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11' ] requires-python = ">=3.8" -version = "1.0" +version = "0.0.dev4" description = "AI assisted Virtual Laboratory framework." dependencies = [ - "numpy", - "tk", # package name of tkinter - "pillow", # package name of PIL - "pandas", - "sklearn", - "ttkwidgets" + "numpy >= 1.20.0", + "pillow >= 9.0.0", # package name of PIL + "pandas >= 1.4.0", + "scikit-learn >= 0.0", + "ttkwidgets >= 0.12.0", + "matplotlib >= 3.5.0", + "opencv-python >= 4.6.0.65", + "pytest >= 7.2.0", + "pybullet >= 3.2.5" ] [project.urls] -Source = "https://github.com/AaltoPML/VAI-labs" +Source = "https://github.com/AaltoPML/VAI-lab" + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-ra -q" + +[project.scripts] +vai_lab = "vai_lab.run_pipeline:main" \ No newline at end of file diff --git a/run_pipeline.py b/run_pipeline.py deleted file mode 100644 index 52c67dfa..00000000 --- a/run_pipeline.py +++ /dev/null @@ -1,10 +0,0 @@ -import aidesign as ai - -core = ai.Core() - -core.load_config_file( - ("./examples", - "xml_files", - 'BO_demo.xml')) - -core.run() \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index a4530e96..00000000 --- a/setup.py +++ /dev/null @@ -1,56 +0,0 @@ -import sys -import subprocess -import pkg_resources - -class SetupPrequisties(object): - def __init__(self): - self.required_packages = { - 'numpy', - 'tk', #package name of tkinter - 'pillow', #package name of PIL - 'pandas', - 'sklearn', - 'ttkwidgets', - 'matplotlib', - } - self.python_version = sys.executable - - def check_installed_packages(self): - print ("\nChecking for required packages...") - self.installed = {pkg.key for pkg in pkg_resources.working_set} - self.missing = self.required_packages - self.installed - - def install_missing(self): - # subprocess.check_call([self.python_version, '-m', 'pip', 'install', *self.missing], stdout=subprocess.DEVNULL) - subprocess.check_call([self.python_version, '-m', 'pip', 'install', *self.missing]) - - def get_user_confirmation(self): - print ("The following required packages are not installed: ") - print (" - {}".format(",\n - ".join([*self.missing]))) - return input ("Install missing packages with pip for {}? [Y/N] \n".format(self.python_version)).lower() - - def denied_installation(self): - print ("No packages installed.") - - def nothing_missing(self): - print ("\nAll required packages already installed, no action needed.\n") - - def __call__(self): - self.check_installed_packages() - if self.missing: - user_input = self.get_user_confirmation() - if user_input.lower() == 'y': - self.install_missing() - else: - self.denied_installation() - else: - self.nothing_missing() - - -setup = SetupPrequisties() -setup() -# print (missing) -# print (sys.executable) -# if missing: -# python = sys.executable -# subprocess.check_call([python, '-m', 'pip', 'install', *missing], stdout=subprocess.DEVNULL) \ No newline at end of file diff --git a/aidesign/Core/Assets/UFAIcon.ico b/src/vai_lab/Core/Assets/UFAIcon.ico similarity index 100% rename from aidesign/Core/Assets/UFAIcon.ico rename to src/vai_lab/Core/Assets/UFAIcon.ico diff --git a/aidesign/Core/Assets/UFAIcon_name.png b/src/vai_lab/Core/Assets/UFAIcon_name.png similarity index 100% rename from aidesign/Core/Assets/UFAIcon_name.png rename to src/vai_lab/Core/Assets/UFAIcon_name.png diff --git a/aidesign/Core/__init__.py b/src/vai_lab/Core/__init__.py similarity index 86% rename from aidesign/Core/__init__.py rename to src/vai_lab/Core/__init__.py index 3750ece6..9ff26fa3 100644 --- a/aidesign/Core/__init__.py +++ b/src/vai_lab/Core/__init__.py @@ -4,5 +4,5 @@ Let's look for a more automatic way of doing this when we have more modules """ -from .AI_design_core import Core +from .vai_lab_core import Core # from .. import * \ No newline at end of file diff --git a/src/vai_lab/Core/tests/__init__.py b/src/vai_lab/Core/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/vai_lab/Core/tests/test_vai_lab_core.py b/src/vai_lab/Core/tests/test_vai_lab_core.py new file mode 100644 index 00000000..ad00500d --- /dev/null +++ b/src/vai_lab/Core/tests/test_vai_lab_core.py @@ -0,0 +1,11 @@ +""" +Tests for vai_lab.Core.vai_lab_core.py +""" + +from vai_lab import Core + +def test_core_init(): + + core = Core() + + assert core diff --git a/aidesign/Core/AI_design_core.py b/src/vai_lab/Core/vai_lab_core.py similarity index 70% rename from aidesign/Core/AI_design_core.py rename to src/vai_lab/Core/vai_lab_core.py index 6af28e28..3d721ca7 100644 --- a/aidesign/Core/AI_design_core.py +++ b/src/vai_lab/Core/vai_lab_core.py @@ -1,29 +1,34 @@ -from aidesign._import_helper import import_module -from aidesign.Data.xml_handler import XML_handler -from aidesign.GUI.GUI_core import GUI -from aidesign.Data.Data_core import Data -from aidesign._plugin_helpers import PluginSpecs -from aidesign._types import PluginSpecsInterface, ModuleInterface import time from sys import exit - from os.path import join -from typing import Dict, Tuple, Union, List +from typing import Dict, List, Tuple, Union + +from vai_lab._import_helper import import_module +from vai_lab._plugin_helpers import PluginSpecs +from vai_lab._types import ModuleInterface, PluginSpecsInterface +from vai_lab.GUI.GUI_core import GUI +from vai_lab.Data.Data_core import Data +from vai_lab.Data.xml_handler import XML_handler + + class Core: def __init__(self) -> None: + self.data = Data() self._xml_handler = XML_handler() self._avail_plugins: PluginSpecsInterface = PluginSpecs() - self.data = Data() + self.loop_level: int = 0 - self.setup_complete: bool = False + self._initialised: bool = False self.status_logger:Dict = {} + self._debug = False def _launch(self): gui_app = GUI() + gui_app._debug = self._debug gui_app.set_avail_plugins(self._avail_plugins) gui_app.set_gui_as_startpage() gui_output = gui_app.launch() - if not gui_app.closed: + if not self._debug: try: self.load_config_file(gui_output["xml_filename"]) except: @@ -31,14 +36,18 @@ def _launch(self): self._load_data() def load_config_file(self, filename: Union[str,List,Tuple]): + """Loads XML file into XML_handler object. + Parses filename first, if needed. + """ if type(filename) == list or type(filename) == tuple: filedir:str = join(*filename) else: filedir = str(filename) self._xml_handler.load_XML(filedir) - self.setup_complete = True + self._initialised = True def _load_data(self) -> None: + """Loads data from XML file into Data object""" init_data_fn = self._xml_handler.data_to_load self.data.import_data_from_config(init_data_fn) @@ -49,31 +58,32 @@ def _execute_module(self, specs): :param specs: dict of module to be executed """ mod: ModuleInterface = import_module(globals(), specs["module_type"]).__call__() + mod._debug = self._debug mod.set_avail_plugins(self._avail_plugins) mod.set_data_in(self.data) mod.set_options(specs) print("\t"*self.loop_level + specs["module_type"] - + " module: \"{}\"".format(specs["name"]) + + " module: \"{}\" ".format(specs["name"]) + "processing..." ) mod.launch() self.data = mod.get_result() def _execute_loop(self, specs): - try: + if hasattr(self,"_execute_{}_loop".format(specs["type"])): print("\t"*self.loop_level + + "Starting " + specs["type"] - + " loop: " - + "\"{}\"".format(specs["name"]) - + " starting...") + + " loop: \"{}\"".format(specs["name"]) + + " ...") self.loop_level += 1 getattr(self, "_execute_{}_loop".format(specs["type"]))(specs) self.loop_level -= 1 - except KeyError: + else: print("\nError: Invalid Loop Type.") print("Loop \"{0}\" with type \"{1}\" not recognised".format( - specs.key, specs["type"])) + specs["name"], specs["type"])) def _execute_entry_point(self, specs): """Placeholder: Will parse the initialiser module when ready""" @@ -83,7 +93,7 @@ def _execute_exit_point(self, specs): """Placeholder: Will parse the Output module when ready""" pass - def _parse_condition(self, condition): + def _parse_loop_condition(self, condition): try: condition = int(condition) @@ -95,20 +105,24 @@ def _parse_condition(self, condition): "Other formats in in development. Only ranged for loops are working currently") def _execute_for_loop(self, specs): - condition = self._parse_condition(specs["condition"]) + condition = self._parse_loop_condition(specs["condition"]) for c in condition: self._execute(specs) - def _runTracker(self): + def _show_updated_tracker(self): self.gui_app = GUI() + self.gui_app._debug = self._debug self.gui_app.set_avail_plugins(self._avail_plugins) - self.gui_app.set_gui(self.status_logger) + self.gui_app.set_gui_as_progress_tracker(self.status_logger) self.gui_app._append_to_output("xml_filename", self._xml_handler.filename) + self.gui_app._load_plugin("progressTracker") return self.gui_app.launch() def _init_status(self, modules): for key in [key for key, val in modules.items() if type(val) == dict]: self.status_logger[modules[key]['name']] = {} + if modules[key]['class'] == "loop": + self._init_status(modules[key]) def _add_status(self, module, key, value): self.status_logger[module['name']][key] = value @@ -119,7 +133,6 @@ def _progress_start(self, module): def _progress_finish(self, module): self._add_status(module, 'finish', self._t) - @property def _t(self): return time.strftime('%H:%M:%S', time.localtime()) @@ -138,18 +151,24 @@ def _execute(self, specs): self._progress_finish(specs[key]) if specs[key]["class"] == 'module': - term = self._runTracker() - if not term['close']: + _tracker = self._show_updated_tracker() + + if not _tracker['terminate']: self.load_config_file(self._xml_handler.filename) + # pass else: print('Pipeline terminated') exit() + def _initialise_with_gui(self): + """Launches GUI when no XML file is specified""" + print("Loading GUI") + print("To load existing config, run core.load_config_file()") + self._launch() + def run(self): - if not self.setup_complete: - print("No pipeline specified. Running GUI.") - print("To load existing config, run core.load_config_file()") - self._launch() + if not self._initialised: + self._initialise_with_gui() print("Running pipeline...") self._load_data() diff --git a/aidesign/Data/Data_core.py b/src/vai_lab/Data/Data_core.py similarity index 86% rename from aidesign/Data/Data_core.py rename to src/vai_lab/Data/Data_core.py index 50645a50..327ba0fe 100644 --- a/aidesign/Data/Data_core.py +++ b/src/vai_lab/Data/Data_core.py @@ -1,5 +1,5 @@ -"""Adds aidesign root folder to pythonpath assuming this script is 2 levels down. -Hacky patch For testing only, normally you'd have aidesign in your +"""Adds vai_lab root folder to pythonpath assuming this script is 2 levels down. +Hacky patch For testing only, normally you'd have vai_lab in your OS's PYTHONPATH""" from os import path @@ -10,8 +10,9 @@ from typing import Dict, KeysView, TypeVar -from aidesign._import_helper import get_lib_parent_dir -from aidesign.Data.xml_handler import XML_handler +from vai_lab._import_helper import get_lib_parent_dir +from vai_lab.Data.xml_handler import XML_handler +from vai_lab._import_helper import rel_to_abs import pandas as pd # type: ignore import numpy as np @@ -72,17 +73,6 @@ def _import_dir(self: DataT, for f in files: self.import_data(f, data_name) - def _rel_to_abs(self: DataT, filename: str) -> str: - """Checks if path is relative or absolute - If absolute, returns original path - If relative, converts path to absolute by appending to base directory - """ - if filename[0] == ".": - filename = path.join(self._lib_base_path, filename) - elif filename[0] == "/" or (filename[0].isalpha() and filename[0].isupper()): - filename = filename - return filename - def _get_ext(self: DataT, path_dir: str) -> str: """Extracts extension from path_dir, or check if is dir :param path_dir: str, path_dir to be checked @@ -102,7 +92,7 @@ def import_data(self: DataT, :param filename: str, filename of file to be loaded :param data_name: str, name of class variable data will be loaded to """ - filename = self._rel_to_abs(filename).replace( + filename = rel_to_abs(filename).replace( "\\", "/").replace("/", path.sep) ext = self._get_ext(filename) getattr(self, "_import_{0}".format(ext))(filename, data_name) diff --git a/aidesign/Data/__init__.py b/src/vai_lab/Data/__init__.py similarity index 100% rename from aidesign/Data/__init__.py rename to src/vai_lab/Data/__init__.py diff --git a/src/vai_lab/Data/tests/__init__.py b/src/vai_lab/Data/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/aidesign/Data/xml_handler.py b/src/vai_lab/Data/xml_handler.py similarity index 95% rename from aidesign/Data/xml_handler.py rename to src/vai_lab/Data/xml_handler.py index e44c6ce2..0975c962 100644 --- a/aidesign/Data/xml_handler.py +++ b/src/vai_lab/Data/xml_handler.py @@ -1,17 +1,15 @@ from os import path +from ast import literal_eval +from typing import Any, Dict, List, Optional, Union +import xml.etree.ElementTree as ET +# Allows standalone testing if not __package__: import sys root_mod = path.dirname(path.dirname(path.dirname(__file__))) sys.path.append(root_mod) - -import xml.etree.ElementTree as ET -from ast import literal_eval -from os import path -from typing import Any, Dict, List, Optional, Union - -from aidesign._import_helper import get_lib_parent_dir +from vai_lab._import_helper import get_lib_parent_dir class XML_handler: @@ -41,6 +39,7 @@ def __init__(self, filename: str = None): "Modelling": "module", "InputData": "module", "DecisionMaking": "module", + "Environment": "module", "DataStorage": "module", "loop": "loop" } @@ -245,6 +244,23 @@ def _load_list(self, element: ET.Element, parent: dict) -> None: if len(parent[element.tag]) == 1: parent[element.tag] = parent[element.tag][0] + def _str_to_num(self, string:str) -> Union[str, int, float]: + """Converts a string containing only numeric data to float or int + Otherwise returns string + + :param string: The string to be converted if possible + :returns out: either a string, float or int + """ + try: + return int(string) + except ValueError: + pass + + try: + return float(string) + except ValueError: + return string + def _parse_text_to_list(self, element: ET.Element) -> List: """Formats raw text data :param elem: xml.etree.ElementTree.Element to be parsed @@ -261,6 +277,8 @@ def _parse_text_to_list(self, element: ET.Element) -> List: out[idx] = literal_eval(out[idx]) if "(" in out[idx] and ")" in out[idx]: out[idx] = list(literal_eval(out[idx])) + if type(out[idx]) == str: + out[idx] = self._str_to_num(out[idx]) element.text = raw_elem_text return out @@ -462,15 +480,16 @@ def append_input_data(self, xml_parent: Union[ET.Element, str] = "Initialiser", save_dir_as_relative: bool = True): """Appened path to input datafile. Replaces windows backslash - :param plugin_type: string type of plugin to be loaded into module - :param plugin_options: dict where keys & values are options & values - :param xml_parent: dict OR str. - If string given, parent elem is found via search, - Otherwise, plugin appeneded directly - :param save_dir_as_relative: bool. If True [default], attempts to - replace the library base path in the absolute - filename with "./" to make it relative to library - path. Recommended. + + :param plugin_type: plugin to be loaded into module + :type plugin_type: str + :param plugin_options: plugin options as key-value pairs + :type plugin_options: dict + :param xml_parent: Name of parent or pass element directly + :type xml_parent: str, ET.Element + :param save_dir_as_relative: Replace root path in filename with "./" + :type save_dir_as_relative: bool, optional + :return: None """ if isinstance(xml_parent, str): xml_parent = self._get_element_from_name(xml_parent) @@ -493,6 +512,7 @@ def append_plugin_to_module(self, overwrite_existing: Union[bool, int] = False ): """Appened plugin as subelement to existing module element + :param plugin_type: string type of plugin to be loaded into module :param plugin_options: dict where keys & values are options & values :param xml_parent: dict OR str. diff --git a/aidesign/DataProcessing/DataProcessing_core.py b/src/vai_lab/DataProcessing/DataProcessing_core.py similarity index 90% rename from aidesign/DataProcessing/DataProcessing_core.py rename to src/vai_lab/DataProcessing/DataProcessing_core.py index f036cd77..ae681db3 100644 --- a/aidesign/DataProcessing/DataProcessing_core.py +++ b/src/vai_lab/DataProcessing/DataProcessing_core.py @@ -1,5 +1,5 @@ -from aidesign._import_helper import import_plugin_absolute -from aidesign._types import PluginSpecsInterface, DataInterface, DataProcessingPluginInterface +from vai_lab._import_helper import import_plugin_absolute +from vai_lab._types import PluginSpecsInterface, DataInterface, DataProcessingPluginInterface class DataProcessing(object): def __init__(self) -> None: diff --git a/aidesign/DataProcessing/__init__.py b/src/vai_lab/DataProcessing/__init__.py similarity index 100% rename from aidesign/DataProcessing/__init__.py rename to src/vai_lab/DataProcessing/__init__.py diff --git a/aidesign/DataProcessing/plugins/argopt.py b/src/vai_lab/DataProcessing/plugins/argopt.py similarity index 93% rename from aidesign/DataProcessing/plugins/argopt.py rename to src/vai_lab/DataProcessing/plugins/argopt.py index e2c48e12..4cf7219e 100644 --- a/aidesign/DataProcessing/plugins/argopt.py +++ b/src/vai_lab/DataProcessing/plugins/argopt.py @@ -1,12 +1,12 @@ from distutils.command.config import config from numpy import argmin, argmax -from aidesign._plugin_templates import DataProcessingT +from vai_lab._plugin_templates import DataProcessingT import pandas as pd _PLUGIN_READABLE_NAMES = {"argopt": "default", "argmax": "alias", "argmin": "alias"} # type:ignore -_PLUGIN_MODULE_OPTIONS = {"Type": "Other"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "math operator"} # type:ignore _PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore _PLUGIN_OPTIONAL_SETTINGS = {'min/max': "str"} # type:ignore _PLUGIN_REQUIRED_DATA = {} # type:ignore diff --git a/src/vai_lab/DataProcessing/plugins/binarizer.py b/src/vai_lab/DataProcessing/plugins/binarizer.py new file mode 100644 index 00000000..14d0e285 --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/binarizer.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import DataProcessingT + +import pandas as pd +from sklearn.preprocessing import Binarizer as model + +_PLUGIN_READABLE_NAMES = {"Binarizer":"default","binarizer":"alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"threshold": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + +class Binarizer(DataProcessingT): + """ + Binarize data (set feature values to 0 or 1) according to a threshold + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/integral.py b/src/vai_lab/DataProcessing/plugins/integral.py new file mode 100644 index 00000000..6778dbec --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/integral.py @@ -0,0 +1,32 @@ +from scipy.integrate import simps as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"Integral": "default", + "integral": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "math operator"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"dx": "float", + "axis": "int", + "even": "str"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst","Y_tst"} # type:ignore + +class Integral(DataProcessingT): + """ + Calculate integral of array using the composite trapezoidal rule + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() + + def fit(self): + return + + def transform(self,data): + data.append_data_column("X", pd.DataFrame(self.proc(self.X))) + return data \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/kbinsdiscretizer.py b/src/vai_lab/DataProcessing/plugins/kbinsdiscretizer.py new file mode 100644 index 00000000..a844db5e --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/kbinsdiscretizer.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import DataProcessingT + +import pandas as pd +from sklearn.preprocessing import KBinsDiscretizer as model + +_PLUGIN_READABLE_NAMES = {"KBinsDiscretizer":"default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"n_bins": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst","Y_tst"} # type:ignore + +class KBinsDiscretizer(DataProcessingT): + """ + Bin continuous data into interval + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/labelbinarizer.py b/src/vai_lab/DataProcessing/plugins/labelbinarizer.py new file mode 100644 index 00000000..b9858065 --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/labelbinarizer.py @@ -0,0 +1,21 @@ +from sklearn.preprocessing import LabelBinarizer as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"LabelBinarizer":"default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"neg_label": "int", "pos_label": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + +class LabelBinarizer(DataProcessingT): + """ + Binarize labels in a one-vs-all fashion + """ + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/labelencoder.py b/src/vai_lab/DataProcessing/plugins/labelencoder.py new file mode 100644 index 00000000..84bed864 --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/labelencoder.py @@ -0,0 +1,22 @@ +from sklearn.preprocessing import LabelEncoder as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"LabelEncoder":"default","LE":"alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + +class LabelEncoder(DataProcessingT): + """ + Encode target labels with value between 0 and n_classes-1 + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/maxabsscaler.py b/src/vai_lab/DataProcessing/plugins/maxabsscaler.py new file mode 100644 index 00000000..d8e700ce --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/maxabsscaler.py @@ -0,0 +1,22 @@ +from sklearn.preprocessing import MaxAbsScaler as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"MaxAbsScaler":"default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "scaler"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + +class MaxAbsScaler(DataProcessingT): + """ + Scale each feature by its maximum absolute value + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/minmaxscaler.py b/src/vai_lab/DataProcessing/plugins/minmaxscaler.py new file mode 100644 index 00000000..d3010e3b --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/minmaxscaler.py @@ -0,0 +1,23 @@ +from sklearn.preprocessing import MinMaxScaler as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"MinMaxScaler":"default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "scaler"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"feature_range": "tuple"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + +class MinMaxScaler(DataProcessingT): + """ + This estimator scales and translates each feature individually such that it\n + is in the given range on the training set, e.g. between zero and one + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/multilabelbinarizer.py b/src/vai_lab/DataProcessing/plugins/multilabelbinarizer.py new file mode 100644 index 00000000..24be8036 --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/multilabelbinarizer.py @@ -0,0 +1,22 @@ +from sklearn.preprocessing import MultiLabelBinarizer as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"MultiLabelBinarizer":"default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"classes": "array-like"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + +class MultiLabelBinarizer(DataProcessingT): + """ + Transform between iterable of iterables and a multilabel format + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/normalizer.py b/src/vai_lab/DataProcessing/plugins/normalizer.py new file mode 100644 index 00000000..5b92cfe6 --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/normalizer.py @@ -0,0 +1,26 @@ +from vai_lab._plugin_templates import DataProcessingT +from vai_lab._types import DataInterface + +from sklearn.preprocessing import Normalizer as model +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"Normalizer": "default", + "Norm": "alias", "normalizer": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "scaler"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"norm": "str"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X", "Y", "X_tst", 'Y_tst'} # type:ignore + + +class Normalizer(DataProcessingT): + """ + Normalize samples individually to unit norm + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/onehotencoder.py b/src/vai_lab/DataProcessing/plugins/onehotencoder.py new file mode 100644 index 00000000..259574bc --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/onehotencoder.py @@ -0,0 +1,22 @@ +from sklearn.preprocessing import OneHotEncoder as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"OneHotEncoder":"default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"categories": "array-like"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + +class OneHotEncoder(DataProcessingT): + """ + Encode categorical features as a one-hot numeric array + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/ordinalencoder.py b/src/vai_lab/DataProcessing/plugins/ordinalencoder.py new file mode 100644 index 00000000..558083cf --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/ordinalencoder.py @@ -0,0 +1,23 @@ +from sklearn.preprocessing import OrdinalEncoder as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = {"OrdinalEncoder": "default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"categories": "array-like"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X", "Y", "X_tst", 'Y_tst'} # type:ignore + + +class OrdinalEncoder(DataProcessingT): + """ + Encode categorical features as an integer array + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/aidesign/DataProcessing/plugins/polynomialfeatures.py b/src/vai_lab/DataProcessing/plugins/polynomialfeatures.py similarity index 51% rename from aidesign/DataProcessing/plugins/polynomialfeatures.py rename to src/vai_lab/DataProcessing/plugins/polynomialfeatures.py index 8523feb6..deae011c 100644 --- a/aidesign/DataProcessing/plugins/polynomialfeatures.py +++ b/src/vai_lab/DataProcessing/plugins/polynomialfeatures.py @@ -1,4 +1,4 @@ -from aidesign._plugin_templates import DataProcessingT +from vai_lab._plugin_templates import DataProcessingT from sklearn.preprocessing import PolynomialFeatures as model import pandas as pd @@ -24,29 +24,4 @@ def __init__(self): Passes `globals` dict of all current variables """ super().__init__(globals()) - self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - super().configure(config) - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) - - def fit(self): - cleaned_options = self._clean_solver_options() - self.proc.set_params(**cleaned_options) - self.proc.fit(self.X) - - def transform(self, data): - data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) - if self.X_tst is not None: - data.append_data_column("X_test", pd.DataFrame( - self.proc.transform(self.X_tst))) - return data + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/quantiletransformer.py b/src/vai_lab/DataProcessing/plugins/quantiletransformer.py new file mode 100644 index 00000000..1e6b1c5b --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/quantiletransformer.py @@ -0,0 +1,24 @@ +from sklearn.preprocessing import QuantileTransformer as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = { + "QuantileTransformer": "default", "Quantile": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "encoder"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"n_quantiles": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X", "Y", "X_tst", 'Y_tst'} # type:ignore + + +class QuantileTransformer(DataProcessingT): + """ + Transform features using quantiles information + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/standardscaler.py b/src/vai_lab/DataProcessing/plugins/standardscaler.py new file mode 100644 index 00000000..f44c7d7b --- /dev/null +++ b/src/vai_lab/DataProcessing/plugins/standardscaler.py @@ -0,0 +1,24 @@ +from sklearn.preprocessing import StandardScaler as model +from vai_lab._plugin_templates import DataProcessingT +import pandas as pd + +_PLUGIN_READABLE_NAMES = { + "StandardScaler": "default", "standardscaler": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "scaler"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"Data": "str"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"with_mean": "bool"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X", "Y", "X_tst", 'Y_tst'} # type:ignore + + +class StandardScaler(DataProcessingT): + """ + Standardize features by removing the mean and scaling to unit variance + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.proc = model() \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/tests/__init__.py b/src/vai_lab/DataProcessing/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/aidesign/DataStorage/DataStorage_core.py b/src/vai_lab/DataStorage/DataStorage_core.py similarity index 100% rename from aidesign/DataStorage/DataStorage_core.py rename to src/vai_lab/DataStorage/DataStorage_core.py diff --git a/aidesign/DataStorage/__init__.py b/src/vai_lab/DataStorage/__init__.py similarity index 100% rename from aidesign/DataStorage/__init__.py rename to src/vai_lab/DataStorage/__init__.py diff --git a/src/vai_lab/DataStorage/tests/__init__.py b/src/vai_lab/DataStorage/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/vai_lab/DecisionMaking/DecisionMaking_core.py b/src/vai_lab/DecisionMaking/DecisionMaking_core.py new file mode 100644 index 00000000..88c8ea44 --- /dev/null +++ b/src/vai_lab/DecisionMaking/DecisionMaking_core.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from vai_lab._import_helper import import_plugin_absolute +class DecisionMaking(object): + def __init__(self): + self.output_data = None + + def set_avail_plugins(self,avail_plugins): + self._avail_plugins = avail_plugins + + def set_data_in(self,data_in): + self._data_in = data_in + + def _load_plugin(self, plugin_name:str): + avail_plugins = self._avail_plugins.find_from_readable_name(plugin_name) + self._plugin_name = plugin_name + self._plugin = import_plugin_absolute(globals(),\ + avail_plugins["_PLUGIN_PACKAGE"],\ + avail_plugins["_PLUGIN_CLASS_NAME"])\ + .__call__() + + def set_options(self, module_config: dict): + """Send configuration arguments to plugin + + :param module_config: dict of settings to configure the plugin + """ + self._module_config = module_config + self._load_plugin(self._module_config["plugin"]["plugin_name"]) + + def launch(self): + self._plugin.set_data_in(self._data_in) + self._plugin.configure(self._module_config["plugin"]) + # self._plugin.optimise() + self.output_data = self._plugin.suggest_locations() + + def get_result(self): + return self.output_data \ No newline at end of file diff --git a/aidesign/Modelling/__init__.py b/src/vai_lab/DecisionMaking/__init__.py similarity index 80% rename from aidesign/Modelling/__init__.py rename to src/vai_lab/DecisionMaking/__init__.py index 99e78a66..77990d09 100644 --- a/aidesign/Modelling/__init__.py +++ b/src/vai_lab/DecisionMaking/__init__.py @@ -4,4 +4,4 @@ Let's look for a more automatic way of doing this when we have more modules """ -from .Modelling_core import Modelling +from .DecisionMaking_core import DecisionMaking diff --git a/src/vai_lab/DecisionMaking/plugins/BayesianOptimisation(GPy).py b/src/vai_lab/DecisionMaking/plugins/BayesianOptimisation(GPy).py new file mode 100644 index 00000000..bf9ff291 --- /dev/null +++ b/src/vai_lab/DecisionMaking/plugins/BayesianOptimisation(GPy).py @@ -0,0 +1,61 @@ +from vai_lab._plugin_templates import DecisionMakingPluginT +from GPyOpt.methods import BayesianOptimization as model +from typing import Dict + +_PLUGIN_READABLE_NAMES = {"GPyOpt": "default", + "BayesianOptimisation": "alias", + "BayesianOptimisation_GPy": "alias",} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "decision making"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"f": "function"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"domain": "list", + "constraints": "list", + "acquisition_type ": "str", + "files": "list", + "normalize_Y": "bool", + "evaluator_type": "str", + "batch_size": "int", + "acquisition_jitter": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y"} # type:ignore + +class GPyOpt(DecisionMakingPluginT): + """ + Bayesian optimisation model using GPyOpt. Compatible with no objective function using tabular data. + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.model = model + + def _parse_options_dict(self, options_dict:Dict): + super()._parse_options_dict(options_dict) + if self.X is not None: + options_dict['X'] = self.X + if self.Y is not None: + options_dict['Y'] = self.Y.reshape(-1,1) + return options_dict + + def optimise(self): + """Sends parameters to optimizer, then runs Bayesian Optimization for a number 'max_iter' of iterations""" + try: + self.BO.run_optimization() + except Exception as exc: + print('The plugin encountered an error when running the optimization ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + + def suggest_locations(self): + """Run a single optimization step and return the next locations to evaluate the objective. + Number of suggested locations equals to batch_size. + :returns: array, shape (n_samples,) + Returns suggested values. + """ + try: + return self.BO.suggest_next_locations() + except Exception as exc: + print('The plugin encountered an error when suggesting points with ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise \ No newline at end of file diff --git a/src/vai_lab/DecisionMaking/plugins/BayesianOptimisation(bayes_opt).py b/src/vai_lab/DecisionMaking/plugins/BayesianOptimisation(bayes_opt).py new file mode 100644 index 00000000..544bcdc9 --- /dev/null +++ b/src/vai_lab/DecisionMaking/plugins/BayesianOptimisation(bayes_opt).py @@ -0,0 +1,49 @@ +from vai_lab._plugin_templates import DecisionMakingPluginT +from bayes_opt import BayesianOptimization as model + +_PLUGIN_READABLE_NAMES = {"bayes_opt": "default", + "BayesOpt": "alias",} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "decision making"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"f": "function", + "pbounds": "dict"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = { + # "constraint": "ConstraintModel", + "random_state ": "int", + "verbose": "bool", + # "bounds_transformer": "DomainTransformer", + "allow_duplicate_points": "str"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y"} # type:ignore + +class bayes_opt(DecisionMakingPluginT): + """ + Bayesian optimisation model using bayes_opt. + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.model = model + + def optimise(self): + """Probes the target space to find the parameters that yield the maximum value for the given function.""" + try: + self.BO.maximize() + except Exception as exc: + print('The plugin encountered an error when running the optimization ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + + def suggest_locations(self, utility_function): + """Run a single optimization step and return the next locations to evaluate the objective. + :returns: array, shape (n_samples,) + Returns suggested values. + """ + try: + return self.BO.suggest(utility_function) + except Exception as exc: + print('The plugin encountered an error when suggesting points with ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise \ No newline at end of file diff --git a/src/vai_lab/DecisionMaking/tests/__init__.py b/src/vai_lab/DecisionMaking/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/vai_lab/Environment/Environment_core.py b/src/vai_lab/Environment/Environment_core.py new file mode 100644 index 00000000..70bcf13c --- /dev/null +++ b/src/vai_lab/Environment/Environment_core.py @@ -0,0 +1,41 @@ +from vai_lab._import_helper import import_plugin_absolute +from vai_lab._types import PluginSpecsInterface, DataInterface, EnvironmentPluginInterface + +class Environment(object): + def __init__(self) -> None: + self.output_data: DataInterface + + def set_avail_plugins(self, avail_plugins: PluginSpecsInterface) -> None: + self._avail_plugins = avail_plugins + + def set_data_in(self, data_in: DataInterface) -> None: + self._data_in = data_in + + def _load_plugin(self, plugin_name: str) -> None: + avail_plugins = self._avail_plugins.find_from_readable_name( + plugin_name) + self._plugin_name = plugin_name + self._plugin: EnvironmentPluginInterface = import_plugin_absolute(globals(), + avail_plugins["_PLUGIN_PACKAGE"], + avail_plugins["_PLUGIN_CLASS_NAME"])\ + .__call__() + + def set_options(self, module_config: dict) -> None: + """Send configuration arguments to plugin + + :param module_config: dict of settings to configure the plugin + """ + self._module_config = module_config + self._load_plugin(self._module_config["plugin"]["plugin_name"]) + + def launch(self) -> None: + self._plugin.set_data_in(self._data_in) + self._plugin.configure(self._module_config["plugin"]) + self._plugin.connect() + self._plugin.load_model() + self._plugin.run_simulation() + + + def get_result(self) -> DataInterface: + # return self.output_data + return self._data_in diff --git a/src/vai_lab/Environment/plugins/PyBulletEnv.py b/src/vai_lab/Environment/plugins/PyBulletEnv.py new file mode 100644 index 00000000..727f2d79 --- /dev/null +++ b/src/vai_lab/Environment/plugins/PyBulletEnv.py @@ -0,0 +1,101 @@ +from attr import attr +from vai_lab._plugin_templates import EnvironmentPluginT +from vai_lab._import_helper import rel_to_abs +from typing import Any, Dict +import pybullet as p +from time import sleep + + +_PLUGIN_READABLE_NAMES = {"PyBullet":"default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {"model_dir": "str", "headless": "bool", + "timestep":"float", "max_steps":"int"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"gravity":"list"} # type:ignore +_PLUGIN_REQUIRED_DATA = {} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X","Y","X_tst", 'Y_tst'} # type:ignore + + +class PyBulletEnv(EnvironmentPluginT): + """ + Loads the pybullet library as wildcard and exposes all functions + """ + + def __init__(self) -> None: + super().__init__(globals()) + self.model_ids: Dict = {} + + def set_gui(self, use_gui: bool = True): + if type(use_gui) == str: + use_gui = eval(use_gui) + if use_gui: + self.connection_mode = p.GUI # Use pybullet GUI + else: + self.connection_mode = p.DIRECT # Use pybullet without GUI + + def connect(self): + self.physicsClient = p.connect(self.connection_mode) + + def disconnect(self): + return p.disconnect() + + def reset(self): + return p.resetSimulation() + + def load_pb_data(self): + import pybullet_data # type:ignore + p.setAdditionalSearchPath(pybullet_data.getDataPath()) + + def _load_model_by_type(self, model): + model = rel_to_abs(model) + ext = model.split(".")[-1] + name = model.split("/")[-1].split(".")[0] + if name == "plane": + self.load_pb_data() + if ext == "urdf": + self.model_ids[name] = p.loadURDF(model) + elif ext == "sdf": + self.model_ids[name] = p.loadSDF(model, 1, 1.0) + elif ext == "xml": + self.model_ids[name] = p.loadMJCF(model) + + def load_model(self) -> None: + model_paths = self._config["options"]["model_dir"] + if type(model_paths) == str: + self._load_model_by_type(model_paths) + elif type(model_paths) == list: + for model in model_paths: + self._load_model_by_type(model) + + def _set_options(self): + api_list = dir(p) + for key, value in self._config["options"].items(): + if key in api_list: + getattr(p,key)(*value) + if "timestep" in self._config["options"]: + p.setPhysicsEngineParameter( + fixedTimeStep=self._config["options"]["timestep"]) + elif "max_steps" in self._config["options"]: + p.setPhysicsEngineParameter( + numSolverIterations=self._config["options"]["max_steps"]) + + def run_simulation(self): + self._set_options() + for step in range(1, self._config["options"]["max_steps"]): + p.stepSimulation() + sleep(self._config["options"]["timestep"]) + self.disconnect() + + def __getattr__(self, attr: str) -> Any: + """Allows calling pybullet functions directly as if they were functions of this class + + TODO: This is probably not the best way to do this, but pybullet is + a compiled module and cannot be used as a parent. This is a workaround. + """ + return getattr(p, attr) + + +if __name__ == "__main__": + pb = PyBulletEnv() + print(pb.ACTIVATION_STATE_DISABLE_SLEEPING) + # pb.set_gui(True) + # pb.connect() diff --git a/src/vai_lab/Environment/resources/models/half_cheetah.xml b/src/vai_lab/Environment/resources/models/half_cheetah.xml new file mode 100644 index 00000000..eb888fd5 --- /dev/null +++ b/src/vai_lab/Environment/resources/models/half_cheetah.xml @@ -0,0 +1,82 @@ + + + + + + + + + + diff --git a/src/vai_lab/Environment/resources/models/half_cheetah_with_mass.xml b/src/vai_lab/Environment/resources/models/half_cheetah_with_mass.xml new file mode 100644 index 00000000..3c465ced --- /dev/null +++ b/src/vai_lab/Environment/resources/models/half_cheetah_with_mass.xml @@ -0,0 +1,88 @@ + + + + + + + + + + diff --git a/src/vai_lab/Environment/tests/__init__.py b/src/vai_lab/Environment/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/aidesign/GUI/GUI_core.py b/src/vai_lab/GUI/GUI_core.py similarity index 89% rename from aidesign/GUI/GUI_core.py rename to src/vai_lab/GUI/GUI_core.py index ddd329d4..348d26af 100644 --- a/aidesign/GUI/GUI_core.py +++ b/src/vai_lab/GUI/GUI_core.py @@ -1,6 +1,6 @@ -from aidesign.Data.xml_handler import XML_handler -from aidesign._import_helper import import_plugin_absolute -from aidesign._types import DataInterface, PluginSpecsInterface, DictT +from vai_lab.Data.xml_handler import XML_handler +from vai_lab._import_helper import import_plugin_absolute +from vai_lab._types import DataInterface, PluginSpecsInterface, DictT from typing import Any import tkinter as tk @@ -26,8 +26,9 @@ def __init__(self, *args, **kwargs): self._desired_ui_types = [] self._top_ui_layer = None self._module_config = None - self.closed = False - self.startpage = False + self._debug = False + self._closed = False + self._is_startpage = False self.output = {} def set_avail_plugins(self, avail_plugins: PluginSpecsInterface): @@ -45,15 +46,15 @@ def set_options(self, module_config: DictT): self._load_plugin(self._module_config["plugin"]["plugin_name"]) def set_gui_as_startpage(self): - self.startpage = True + self._is_startpage = True self._load_plugin("main") - self.s = XML_handler() - self.s.new_config_file() + self.xml_handler = XML_handler() + self.xml_handler.new_config_file() - def set_gui(self, status): - self.startpage = True + def set_gui_as_progress_tracker(self, status): + self._is_startpage = True self._load_plugin("progressTracker") - self.status = status + self._status = status def _compare_layer_priority(self, ui_specs): """Check if a new module should have higher layer priority than the existing one @@ -125,7 +126,7 @@ def _show_frame(self, page_name): frame.tkraise() def _on_closing(self) -> None: - self.closed = True + self._closed = True self.destroy() def destroy(self) -> None: @@ -145,7 +146,7 @@ def launch(self): page_name = F.__name__ frame = F(parent=container, controller=self, config=self._module_config) - if not self.startpage: + if not self._is_startpage: frame.set_data_in(self._data_in) self.frames[page_name] = frame @@ -154,7 +155,10 @@ def launch(self): self._show_frame(self._top_ui_layer) self.protocol("WM_DELETE_WINDOW", self._on_closing) - self.mainloop() + if not self._debug: + self.mainloop() + else: + self._closed = True return self.output def get_result(self): diff --git a/aidesign/GUI/User_Feedback_template.py b/src/vai_lab/GUI/User_Feedback_template.py similarity index 63% rename from aidesign/GUI/User_Feedback_template.py rename to src/vai_lab/GUI/User_Feedback_template.py index 07c9dce5..8b3b2967 100644 --- a/aidesign/GUI/User_Feedback_template.py +++ b/src/vai_lab/GUI/User_Feedback_template.py @@ -1,8 +1,8 @@ -from aidesign._plugin_templates import UI +from vai_lab._plugin_templates import UI class UserFeedbackTemplate(UI): """UserFeedbackTemplate directly duplicates: - aidesign.UserInteraction.User_Interaction_template + vai_lab.UserInteraction.User_Interaction_template """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) \ No newline at end of file diff --git a/aidesign/GUI/__init__.py b/src/vai_lab/GUI/__init__.py similarity index 100% rename from aidesign/GUI/__init__.py rename to src/vai_lab/GUI/__init__.py diff --git a/aidesign/GUI/plugins/__init__.py b/src/vai_lab/GUI/plugins/__init__.py similarity index 100% rename from aidesign/GUI/plugins/__init__.py rename to src/vai_lab/GUI/plugins/__init__.py diff --git a/src/vai_lab/GUI/tests/__init__.py b/src/vai_lab/GUI/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/aidesign/InputData/InputData_core.py b/src/vai_lab/InputData/InputData_core.py similarity index 90% rename from aidesign/InputData/InputData_core.py rename to src/vai_lab/InputData/InputData_core.py index 9f2b40a0..f9f4d165 100644 --- a/aidesign/InputData/InputData_core.py +++ b/src/vai_lab/InputData/InputData_core.py @@ -1,6 +1,6 @@ -from aidesign.Data.Data_core import Data -from aidesign._import_helper import import_plugin_absolute -from aidesign._types import PluginSpecsInterface, DataInterface +from vai_lab.Data.Data_core import Data +from vai_lab._import_helper import import_plugin_absolute +from vai_lab._types import PluginSpecsInterface, DataInterface class InputData(Data): def __init__(self): diff --git a/aidesign/InputData/__init__.py b/src/vai_lab/InputData/__init__.py similarity index 100% rename from aidesign/InputData/__init__.py rename to src/vai_lab/InputData/__init__.py diff --git a/src/vai_lab/InputData/tests/__init__.py b/src/vai_lab/InputData/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/aidesign/Modelling/Modelling_core.py b/src/vai_lab/Modelling/Modelling_core.py similarity index 95% rename from aidesign/Modelling/Modelling_core.py rename to src/vai_lab/Modelling/Modelling_core.py index 7ca67626..81695629 100644 --- a/aidesign/Modelling/Modelling_core.py +++ b/src/vai_lab/Modelling/Modelling_core.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from aidesign._import_helper import import_plugin_absolute +from vai_lab._import_helper import import_plugin_absolute class Modelling(object): def __init__(self): self.output_data = None diff --git a/aidesign/DecisionMaking/__init__.py b/src/vai_lab/Modelling/__init__.py similarity index 100% rename from aidesign/DecisionMaking/__init__.py rename to src/vai_lab/Modelling/__init__.py diff --git a/src/vai_lab/Modelling/plugins/affinitypropagation.py b/src/vai_lab/Modelling/plugins/affinitypropagation.py new file mode 100644 index 00000000..b3dbc666 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/affinitypropagation.py @@ -0,0 +1,22 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.cluster import AffinityPropagation as model + +_PLUGIN_READABLE_NAMES = {"Birch": "default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "clustering"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"damping": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"Y", "X_tst", 'Y_tst'} # type:ignore + + +class AffinityPropagation(ModellingPluginT): + """ + Perform Affinity Propagation Clustering of data + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/bayesianridge.py b/src/vai_lab/Modelling/plugins/bayesianridge.py new file mode 100644 index 00000000..9844f610 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/bayesianridge.py @@ -0,0 +1,22 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.linear_model import BayesianRidge as model + +_PLUGIN_READABLE_NAMES = {"BayesianRidge": "default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"n_iter": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class BayesianRidge(ModellingPluginT): + """ + Bayesian ridge regression + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/birch.py b/src/vai_lab/Modelling/plugins/birch.py new file mode 100644 index 00000000..c1bd4a01 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/birch.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.cluster import Birch as model + +_PLUGIN_READABLE_NAMES = {"Birch": "default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "clustering"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"n_clusters": "int", # type:ignore + "threshold": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"Y", "X_tst", 'Y_tst'} # type:ignore + + +class Birch(ModellingPluginT): + """ + Implements the BIRCH clustering algorithm + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/decisiontreeclassifier.py b/src/vai_lab/Modelling/plugins/decisiontreeclassifier.py new file mode 100644 index 00000000..32a8b3cc --- /dev/null +++ b/src/vai_lab/Modelling/plugins/decisiontreeclassifier.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import ModellingPluginTClass +from sklearn.tree import DecisionTreeClassifier as model + +_PLUGIN_READABLE_NAMES = {"DecissionTreeClassifier": "default", + "DTClassifier": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"max_depth": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class DecisionTreeClassifier(ModellingPluginTClass): + """ + A decision tree classifier + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/decisiontreeregressor.py b/src/vai_lab/Modelling/plugins/decisiontreeregressor.py new file mode 100644 index 00000000..d3b071f6 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/decisiontreeregressor.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.tree import DecisionTreeRegressor as model + +_PLUGIN_READABLE_NAMES = {"DecisionTreeRegressor": "default", + "DTregressor": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"max_depth": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class DecisionTreeRegressor(ModellingPluginT): + """ + A decision tree regressor + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/elasticnet.py b/src/vai_lab/Modelling/plugins/elasticnet.py new file mode 100644 index 00000000..0e85f49d --- /dev/null +++ b/src/vai_lab/Modelling/plugins/elasticnet.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.linear_model import ElasticNet as model + +_PLUGIN_READABLE_NAMES = {"ElasticNet": "default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float", + "l1_ratio": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class ElasticNet(ModellingPluginT): + """ + Linear regression with combined L1 and L2 priors as regularizer + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/gpclassifier.py b/src/vai_lab/Modelling/plugins/gpclassifier.py new file mode 100644 index 00000000..b332f673 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/gpclassifier.py @@ -0,0 +1,25 @@ +from vai_lab._plugin_templates import ModellingPluginTClass +from sklearn.gaussian_process import GaussianProcessClassifier as model + +_PLUGIN_READABLE_NAMES = {"GPClassifier": "default", + "GPC": "alias", + "GaussianProcessClassifier": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"n_restarts_optimizer": "int", + "random_state": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class GPClassifier(ModellingPluginTClass): + """ + Gaussian process Classifier (GPC) based on Laplace approximation + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/gpregressor.py b/src/vai_lab/Modelling/plugins/gpregressor.py new file mode 100644 index 00000000..edf23cb6 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/gpregressor.py @@ -0,0 +1,25 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.gaussian_process import GaussianProcessRegressor as model + +_PLUGIN_READABLE_NAMES = {"GPRegressor": "default", + "GPR": "alias", + "GaussianProcessRegressor": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"n_restarts_optimizer": "int", + "random_state": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class GPRegressor(ModellingPluginT): + """ + Gaussian process regressor + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/kernelridge.py b/src/vai_lab/Modelling/plugins/kernelridge.py new file mode 100644 index 00000000..95cbdb20 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/kernelridge.py @@ -0,0 +1,26 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.kernel_ridge import KernelRidge as model + +_PLUGIN_READABLE_NAMES = {"KernelRidge": "default", + "KR": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float", + "kernel": "str", + "gamma": "float", + "degree": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class KernelRidge(ModellingPluginT): + """ + Kernel ridge regression. + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/kmeans.py b/src/vai_lab/Modelling/plugins/kmeans.py new file mode 100644 index 00000000..960512ce --- /dev/null +++ b/src/vai_lab/Modelling/plugins/kmeans.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.cluster import KMeans as model + +_PLUGIN_READABLE_NAMES = {"KMeans": "default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "clustering"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"n_clusters": "int", + "n_init": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"Y", "X_tst", 'Y_tst'} # type:ignore + + +class KMeans(ModellingPluginT): + """ + K-Means clustering + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/knnclassifier.py b/src/vai_lab/Modelling/plugins/knnclassifier.py new file mode 100644 index 00000000..9ae82293 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/knnclassifier.py @@ -0,0 +1,24 @@ +from vai_lab._plugin_templates import ModellingPluginTClass +from sklearn.neighbors import KNeighborsClassifier as model + +_PLUGIN_READABLE_NAMES = {"KNNClassifier": "default", + "KNN-C": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"n_neighbors": "int", + "weights": "str"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class KNNclassifier(ModellingPluginTClass): + """ + Classifier implementing the k-nearest neighbors vote + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/knnregressor.py b/src/vai_lab/Modelling/plugins/knnregressor.py new file mode 100644 index 00000000..8be1bcc8 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/knnregressor.py @@ -0,0 +1,24 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.neighbors import KNeighborsRegressor as model + +_PLUGIN_READABLE_NAMES = {"KNNRegressor": "default", + "KNN-R": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"n_neighbors": "int", + "weights": "str"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class KNNRegressor(ModellingPluginT): + """ + Regression based on k-nearest neighbors + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/lasso.py b/src/vai_lab/Modelling/plugins/lasso.py new file mode 100644 index 00000000..10ccbfed --- /dev/null +++ b/src/vai_lab/Modelling/plugins/lasso.py @@ -0,0 +1,21 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.linear_model import Lasso as model + +_PLUGIN_READABLE_NAMES = {"Lasso":"default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X","Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + +class Lasso(ModellingPluginT): + """ + Linear Model trained with L1 prior as regularizer + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/linearregression.py b/src/vai_lab/Modelling/plugins/linearregression.py new file mode 100644 index 00000000..c25523cf --- /dev/null +++ b/src/vai_lab/Modelling/plugins/linearregression.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.linear_model import LinearRegression as model + +_PLUGIN_READABLE_NAMES = {"LinearRegression": "default", + "LR": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class LinearRegression(ModellingPluginT): + """ + Ordinary least squares Linear Regression + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/logisticregression.py b/src/vai_lab/Modelling/plugins/logisticregression.py new file mode 100644 index 00000000..84390752 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/logisticregression.py @@ -0,0 +1,24 @@ +from vai_lab._plugin_templates import ModellingPluginTClass +from sklearn.linear_model import LogisticRegression as model + +_PLUGIN_READABLE_NAMES = {"LogisticRegression": "default", + "logit": "alias", + "MaxEnt": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"penalty": "str", "C": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class LogisticRegression(ModellingPluginTClass): + """ + Logistic regression classification. + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/meanshift.py b/src/vai_lab/Modelling/plugins/meanshift.py new file mode 100644 index 00000000..d3d8c207 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/meanshift.py @@ -0,0 +1,21 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.cluster import MeanShift as model + +_PLUGIN_READABLE_NAMES = {"MeanShift":"default"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "clustering"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"bandwidth": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"Y", "X_tst", 'Y_tst'} # type:ignore + +class MeanShift(ModellingPluginT): + """ + Mean shift clustering using a flat kernel. + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/passiveaggressiveclassifier.py b/src/vai_lab/Modelling/plugins/passiveaggressiveclassifier.py new file mode 100644 index 00000000..772ef0e6 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/passiveaggressiveclassifier.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import ModellingPluginTClass +from sklearn.linear_model import PassiveAggressiveClassifier as model + +_PLUGIN_READABLE_NAMES = {"PassiveAggressiveClassifier": "default", + "PassiveAgressive": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"C": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class PassiveAggressiveClassifier(ModellingPluginTClass): + """ + Passive aggressive classifier + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/perceptron.py b/src/vai_lab/Modelling/plugins/perceptron.py new file mode 100644 index 00000000..63155166 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/perceptron.py @@ -0,0 +1,24 @@ +from vai_lab._plugin_templates import ModellingPluginTClass +from sklearn.linear_model import Perceptron as model + +_PLUGIN_READABLE_NAMES = {"Perceptron": "default", + "LinearPerceptron": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float", + "penalty": "str"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class Perceptron(ModellingPluginTClass): + """ + Linear perceptron classification + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/randomforestclassifier.py b/src/vai_lab/Modelling/plugins/randomforestclassifier.py new file mode 100644 index 00000000..39850f41 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/randomforestclassifier.py @@ -0,0 +1,25 @@ +from vai_lab._plugin_templates import ModellingPluginTClass +from sklearn.ensemble import RandomForestClassifier as model + +_PLUGIN_READABLE_NAMES = {"RandomForestClassifier": "default", + "RFclassifier": "alias", + "RFC": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"max_depth": "int", + "n_estimators": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class RandomForestClassifier(ModellingPluginTClass): + """ + A random forest classifier + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/randomforestregressor.py b/src/vai_lab/Modelling/plugins/randomforestregressor.py new file mode 100644 index 00000000..bea9acbb --- /dev/null +++ b/src/vai_lab/Modelling/plugins/randomforestregressor.py @@ -0,0 +1,25 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.ensemble import RandomForestRegressor as model + +_PLUGIN_READABLE_NAMES = {"RandomForestRegressor": "default", + "RFRegressor": "alias", + "RFR": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"max_depth": "int", + "n_estimators": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class RandomForestRegressor(ModellingPluginT): + """ + A random forest regression + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/ridgeregression.py b/src/vai_lab/Modelling/plugins/ridgeregression.py new file mode 100644 index 00000000..1034b353 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/ridgeregression.py @@ -0,0 +1,23 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.linear_model import Ridge as model + +_PLUGIN_READABLE_NAMES = {"Ridge": "default", + "RidgeRegression": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"alpha": "float"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class RidgeRegression(ModellingPluginT): + """ + Linear least squares with l2 regularization + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() diff --git a/src/vai_lab/Modelling/plugins/svc.py b/src/vai_lab/Modelling/plugins/svc.py new file mode 100644 index 00000000..7862ae70 --- /dev/null +++ b/src/vai_lab/Modelling/plugins/svc.py @@ -0,0 +1,26 @@ +from vai_lab._plugin_templates import ModellingPluginTClass +from sklearn.svm import SVC as model + +_PLUGIN_READABLE_NAMES = {"SVC": "default", + "SupportVectorClassification": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "classification"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"C": "float", + "kernel": "str", + "gamma": "float", + "degree": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class SVC(ModellingPluginTClass): + """ + C-Support Vector Classification + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/plugins/svr.py b/src/vai_lab/Modelling/plugins/svr.py new file mode 100644 index 00000000..9519bf7e --- /dev/null +++ b/src/vai_lab/Modelling/plugins/svr.py @@ -0,0 +1,26 @@ +from vai_lab._plugin_templates import ModellingPluginT +from sklearn.svm import SVR as model + +_PLUGIN_READABLE_NAMES = {"SVR": "default", + "SupportVectorRegression": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"Type": "regression"} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"C": "float", + "kernel": "str", + "gamma": "float", + "degree": "int"} # type:ignore +_PLUGIN_REQUIRED_DATA = {"X", "Y"} # type:ignore +_PLUGIN_OPTIONAL_DATA = {"X_tst", 'Y_tst'} # type:ignore + + +class SVR(ModellingPluginT): + """ + Epsilon-Support Vector Regression + """ + + def __init__(self): + """Initialises parent class. + Passes `globals` dict of all current variables + """ + super().__init__(globals()) + self.clf = model() \ No newline at end of file diff --git a/src/vai_lab/Modelling/tests/__init__.py b/src/vai_lab/Modelling/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/aidesign/UserInteraction/UserInteraction_core.py b/src/vai_lab/UserInteraction/UserInteraction_core.py similarity index 83% rename from aidesign/UserInteraction/UserInteraction_core.py rename to src/vai_lab/UserInteraction/UserInteraction_core.py index 24db1ed5..588f9111 100644 --- a/aidesign/UserInteraction/UserInteraction_core.py +++ b/src/vai_lab/UserInteraction/UserInteraction_core.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from aidesign.GUI.GUI_core import GUI +from vai_lab.GUI.GUI_core import GUI class UserInteraction(GUI): """UserFeedback core class directly uses GUI_core.py""" def __init__(self, *args, **kwargs): diff --git a/aidesign/UserInteraction/__init__.py b/src/vai_lab/UserInteraction/__init__.py similarity index 100% rename from aidesign/UserInteraction/__init__.py rename to src/vai_lab/UserInteraction/__init__.py diff --git a/aidesign/UserInteraction/plugins/CanvasInput.py b/src/vai_lab/UserInteraction/plugins/CanvasInput.py similarity index 98% rename from aidesign/UserInteraction/plugins/CanvasInput.py rename to src/vai_lab/UserInteraction/plugins/CanvasInput.py index 25811d95..0de202e7 100644 --- a/aidesign/UserInteraction/plugins/CanvasInput.py +++ b/src/vai_lab/UserInteraction/plugins/CanvasInput.py @@ -1,6 +1,6 @@ from typing import List -from aidesign._plugin_templates import UI +from vai_lab._plugin_templates import UI import tkinter as tk from tkinter import messagebox, ttk @@ -11,7 +11,7 @@ import pandas as pd import math -_PLUGIN_READABLE_NAMES = {"canvas": "default", +_PLUGIN_READABLE_NAMES = {"CanvasInput": "default", "state-action": "alias", "robot": "alias"} # type:ignore _PLUGIN_MODULE_OPTIONS = {"layer_priority": 2, "required_children": None} # type:ignore @@ -42,15 +42,18 @@ def __init__(self, parent, controller, config: dict): self.save_path = '' self.saved = True - def set_data_in(self, data_in): - req_check = [ - r for r in _PLUGIN_REQUIRED_DATA if r not in data_in.keys()] + def set_data_in(self, data_in: dict): + """Set data. + """ + req_check = [r for r in _PLUGIN_REQUIRED_DATA if r not in data_in.keys()] + if len(req_check) > 0: raise Exception("Minimal Data Requirements not met" + "\n\t{0} ".format("CanvasInput") + "requires data: {0}".format(_PLUGIN_REQUIRED_DATA) + "\n\tThe following data is missing:" + "\n\t\u2022 {}".format(",\n\t\u2022 ".join([*req_check]))) + self._data_in = data_in self._load_classes_from_data() @@ -252,10 +255,7 @@ def draw_dot(self, event): # Update coordinates in corresponding row if exists. if self.tree[ii].selection(): n = int(self.tree[ii].selection()[0]) + 1 - if n % 2 == 0: - tag = ('even',) - else: - tag = ('odd',) + tag = ('odd' if n%2 == 0 else 'even',) self.tree[ii].insert( parent='', index='end', iid=n, text=n+1, values=tuple(self.dict2mat( diff --git a/aidesign/UserInteraction/plugins/ManualInput.py b/src/vai_lab/UserInteraction/plugins/ManualInput.py similarity index 98% rename from aidesign/UserInteraction/plugins/ManualInput.py rename to src/vai_lab/UserInteraction/plugins/ManualInput.py index 85cc826b..d61ed754 100644 --- a/aidesign/UserInteraction/plugins/ManualInput.py +++ b/src/vai_lab/UserInteraction/plugins/ManualInput.py @@ -1,6 +1,6 @@ -from aidesign._plugin_templates import UI -from aidesign._import_helper import get_lib_parent_dir -from aidesign._types import DictT, DataInterface, GUICoreInterface +from vai_lab._plugin_templates import UI +from vai_lab._import_helper import get_lib_parent_dir +from vai_lab._types import DictT, DataInterface, GUICoreInterface import os import numpy as np @@ -12,7 +12,7 @@ from tkinter import messagebox, ttk from tkinter.filedialog import asksaveasfile -_PLUGIN_READABLE_NAMES = {"manual": "default", +_PLUGIN_READABLE_NAMES = {"ManualInput": "default", "binary": "alias", "classification": "alias"} # type:ignore _PLUGIN_MODULE_OPTIONS = {"layer_priority": 2, @@ -114,14 +114,18 @@ def _load_images_from_data(self): self.grid_columnconfigure(0, weight=2) def set_data_in(self, data_in): + """Set data. + """ req_check = [ r for r in _PLUGIN_REQUIRED_DATA if r not in data_in.keys()] + if len(req_check) > 0: raise Exception("Minimal Data Requirements not met" + "\n\t{0} ".format("ManualInput") + "requires data: {0}".format(_PLUGIN_REQUIRED_DATA) + "\n\tThe following data is missing:" + "\n\t\u2022 {}".format(",\n\t\u2022 ".join([*req_check]))) + self._data_in = data_in self._load_images_from_data() self._load_classes_from_data() diff --git a/aidesign/UserInteraction/plugins/OptimisationInput.py b/src/vai_lab/UserInteraction/plugins/OptimisationInput.py similarity index 96% rename from aidesign/UserInteraction/plugins/OptimisationInput.py rename to src/vai_lab/UserInteraction/plugins/OptimisationInput.py index 2cba174c..d0027488 100644 --- a/aidesign/UserInteraction/plugins/OptimisationInput.py +++ b/src/vai_lab/UserInteraction/plugins/OptimisationInput.py @@ -1,6 +1,6 @@ -from aidesign._plugin_templates import UI -from aidesign._import_helper import get_lib_parent_dir -from aidesign._types import DictT, DataInterface, GUICoreInterface +from vai_lab._plugin_templates import UI +from vai_lab._import_helper import get_lib_parent_dir +from vai_lab._types import DictT, DataInterface, GUICoreInterface import os import numpy as np @@ -14,13 +14,13 @@ from tkinter import messagebox, ttk from tkinter.filedialog import asksaveasfile -_PLUGIN_READABLE_NAMES = {"optimisationUI": "default", +_PLUGIN_READABLE_NAMES = {"OptimisationInput": "default", "BOUI": "alias", - "BayesianOptimisationUI": "alias"} # type:ignore + "optimisationUI": "alias"} # type:ignore _PLUGIN_MODULE_OPTIONS = {"layer_priority": 2, "required_children": None} # type:ignore _PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"Bounds"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"Bounds": "list"} # type:ignore _PLUGIN_REQUIRED_DATA = {"X"} # type:ignore @@ -78,7 +78,7 @@ def _load_values_from_data(self): frame4, text='Save', fg='white', bg=self.parent['bg'], height=3, width=20, command=self.save_file) self.button_save.grid(column=0, row=0, sticky="news", pady=2, padx=[2,0]) - + tk.Button( frame5, text="Done", fg='white', bg=self.parent['bg'], diff --git a/aidesign/UserInteraction/plugins/__init__.py b/src/vai_lab/UserInteraction/plugins/__init__.py similarity index 100% rename from aidesign/UserInteraction/plugins/__init__.py rename to src/vai_lab/UserInteraction/plugins/__init__.py diff --git a/aidesign/UserInteraction/plugins/resources/Assets/UFAIcon.ico b/src/vai_lab/UserInteraction/plugins/resources/Assets/UFAIcon.ico similarity index 100% rename from aidesign/UserInteraction/plugins/resources/Assets/UFAIcon.ico rename to src/vai_lab/UserInteraction/plugins/resources/Assets/UFAIcon.ico diff --git a/aidesign/UserInteraction/plugins/resources/Assets/UFAIcon_name.png b/src/vai_lab/UserInteraction/plugins/resources/Assets/UFAIcon_name.png similarity index 100% rename from aidesign/UserInteraction/plugins/resources/Assets/UFAIcon_name.png rename to src/vai_lab/UserInteraction/plugins/resources/Assets/UFAIcon_name.png diff --git a/aidesign/UserInteraction/plugins/resources/Assets/back_arrow.png b/src/vai_lab/UserInteraction/plugins/resources/Assets/back_arrow.png similarity index 100% rename from aidesign/UserInteraction/plugins/resources/Assets/back_arrow.png rename to src/vai_lab/UserInteraction/plugins/resources/Assets/back_arrow.png diff --git a/aidesign/UserInteraction/plugins/resources/Assets/forw_arrow.png b/src/vai_lab/UserInteraction/plugins/resources/Assets/forw_arrow.png similarity index 100% rename from aidesign/UserInteraction/plugins/resources/Assets/forw_arrow.png rename to src/vai_lab/UserInteraction/plugins/resources/Assets/forw_arrow.png diff --git a/src/vai_lab/UserInteraction/tests/__init__.py b/src/vai_lab/UserInteraction/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/vai_lab/__init__.py b/src/vai_lab/__init__.py new file mode 100644 index 00000000..5ecee7eb --- /dev/null +++ b/src/vai_lab/__init__.py @@ -0,0 +1 @@ +from .Core import Core \ No newline at end of file diff --git a/aidesign/_import_helper.py b/src/vai_lab/_import_helper.py similarity index 65% rename from aidesign/_import_helper.py rename to src/vai_lab/_import_helper.py index 31afecf5..e083a007 100644 --- a/aidesign/_import_helper.py +++ b/src/vai_lab/_import_helper.py @@ -17,7 +17,7 @@ def import_plugin_absolute(script_config,plugin_package,plugin_name): return plugin_class def import_module(script_config, module_name): - module_list = __import__('aidesign.' + module_name + "." + module_name + "_core", + module_list = __import__('vai_lab.' + module_name + "." + module_name + "_core", script_config, {}, [module_name]) @@ -32,4 +32,15 @@ def get_lib_parent_dir(): return [__file__[:i] \ for i,_ in enumerate(__file__)\ if __file__[:i].\ - endswith("{0}aidesign{0}".format(path.sep))][-1] \ No newline at end of file + endswith("{0}vai_lab{0}".format(path.sep))][-1] + +def rel_to_abs(filename: str) -> str: + """Checks if path is relative or absolute + If absolute, returns original path + If relative, converts path to absolute by appending to base directory + """ + if filename[0] == ".": + filename = path.join(get_lib_parent_dir(), filename) + elif filename[0] == "/" or (filename[0].isalpha() and filename[0].isupper()): + filename = filename + return filename \ No newline at end of file diff --git a/aidesign/_plugin_helpers.py b/src/vai_lab/_plugin_helpers.py similarity index 85% rename from aidesign/_plugin_helpers.py rename to src/vai_lab/_plugin_helpers.py index 9217a9bd..90b4003d 100644 --- a/aidesign/_plugin_helpers.py +++ b/src/vai_lab/_plugin_helpers.py @@ -1,8 +1,8 @@ -from typing import List, Iterator, Union -from aidesign._types import DictT - import ast import os +from typing import Iterator, List, Union + +from vai_lab._types import DictT class PluginSpecs(ast.NodeVisitor): @@ -26,12 +26,16 @@ def _get_plugin_files(self, root_dir: str = None) -> None: f for f in plugins if f.endswith(".py")] def _get_clean_module_dirs(self, root_dir: str) -> List[str]: - excludes = ["__init__.py", "__pycache__", "resources"] + excludes = ["tests", "examples", "resources", "run_pipeline.py"] if os.path.exists(root_dir): mod_dirs = os.listdir(root_dir) - for ex in excludes: - if ex in mod_dirs: - mod_dirs.remove(ex) + rm_list = [] + for m in mod_dirs: + if m.startswith("_") \ + or m in excludes: + rm_list.append(m) + for ex in rm_list: + mod_dirs.remove(ex) return mod_dirs else: return [] @@ -53,7 +57,7 @@ def _get_module_package(self): if __package__: parent_package = __package__.split(".")[0] else: - parent_package = "aidesign" + parent_package = "vai_lab" scipt_filename = self.curr_plugin.replace(".py", "") return "{0}.{1}.plugins.{2}".format(parent_package, self.curr_module, scipt_filename) @@ -96,6 +100,11 @@ def visit_Assign(self, node): "", "eval")) self.available_plugins[self.curr_module][self.curr_plugin][key] = val + def _get_default_name_from_dict(self, plugin: DictT) -> str: + default = list(plugin.keys())\ + [list(plugin.values()).index("default")] + return default + def _get_option_specs(self, option: str) -> dict: """Gets either required OR optional settings for each plugin. @@ -106,16 +115,16 @@ def _get_option_specs(self, option: str) -> dict: :returns output: nested dict of options by [Module][Plugin Class Name] """ - output : DictT= {} + output: DictT = {} for module in self.available_plugins.keys(): output[module] = {} for plugin in self.available_plugins[module].keys(): if option in self.available_plugins[module][plugin]: - cn = self.available_plugins[module][plugin]["_PLUGIN_CLASS_NAME"] - output[module][cn] = self.available_plugins[module][plugin][option] + rn = self._get_default_name_from_dict(self.available_plugins[module][plugin]["_PLUGIN_READABLE_NAMES"]) + output[module][rn] = self.available_plugins[module][plugin][option] return output - def _find_plugin_by_tag_and_value(self, tag: str, name: str) -> Union[DictT,None]: + def _find_plugin_by_tag_and_value(self, tag: str, name: str) -> Union[DictT, None]: for module in self.available_plugins.keys(): for plugin in self.available_plugins[module].keys(): if tag in self.available_plugins[module][plugin]: @@ -153,9 +162,7 @@ def available_plugin_names(self): output = [] for n in names.keys(): for plugin in names[n]: - for variant in names[n][plugin].keys(): - if names[n][plugin][variant] == "default": - output.append(variant) + output.append(self._get_default_name_from_dict(names[n][plugin])) return output def find_from_class_name(self, value): @@ -172,7 +179,8 @@ def print(self, value): if __name__ == "__main__": ps = PluginSpecs() - ps.print(ps.available_plugin_names) + # ps.print(ps.available_plugin_names) + ps.print(ps.optional_settings) # ps.print(ps.names) # ps.print(ps.class_descriptions) # print(list(ps.class_descriptions()['GUI'].values())) diff --git a/aidesign/_plugin_templates.py b/src/vai_lab/_plugin_templates.py similarity index 50% rename from aidesign/_plugin_templates.py rename to src/vai_lab/_plugin_templates.py index 27e51831..150eae9c 100644 --- a/aidesign/_plugin_templates.py +++ b/src/vai_lab/_plugin_templates.py @@ -1,8 +1,9 @@ -from aidesign._types import DataInterface +from typing import Dict +from vai_lab._types import DataInterface from abc import ABC, abstractmethod import numpy as np - +import pandas as pd class PluginTemplate: def __init__(self, plugin_globals: dict) -> None: @@ -34,7 +35,7 @@ def configure(self, config: dict) -> None: def set_data_in(self, data_in: DataInterface) -> None: """Sets and parses incoming data :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data + expected type: vai_lab.Data.Data_core.Data """ req_check = [ r for r in self._PLUGIN_REQUIRED_DATA if r not in data_in.keys()] @@ -70,7 +71,7 @@ def _data_missing_error(self, req): def _parse_config(self): """Parse incoming data and args, sets them as class variables""" - self.X = self._data_in["X"] + self.X = np.array(self._get_data_if_exist(self._data_in, "X")) self.Y = np.array(self._get_data_if_exist(self._data_in, "Y")).ravel() self.X_tst = self._get_data_if_exist(self._data_in, "X_test") self.Y_tst = np.array(self._get_data_if_exist( @@ -97,20 +98,30 @@ def _reshape(self, data, shape): """ return data.reshape(shape[0], shape[1]) + def _parse_options_dict(self,options_dict:Dict): + for key, val in options_dict.items(): + if type(val) == str and val.replace('.', '').replace(',', '').isnumeric(): + cleaned_opts = [] + for el in val.split(","): + val = float(el) + if val.is_integer(): + val = int(val) + cleaned_opts.append(val) + options_dict[key] = cleaned_opts + elif type(val) == str and val.lower() in ('y', 'yes', 't', 'true', 'on'): + options_dict[key] = True + elif type(val) == str and val.lower() in ('n', 'no', 'f', 'false', 'off'): + options_dict[key] = False + elif type(val) == str and val.lower() in ('none'): + options_dict[key] = None + return options_dict + def _clean_options(self): """Parses incoming plugin options in self._config["options"] and modifies DataInterface in-place str options which only contain numeric data are converted to float OR int """ - for key, val in self._config["options"].items(): - """ - TODO: Maybe, if list -> cv - """ - if type(val) == str and val.replace('.', '').replace(',', '').isnumeric(): - val = float(val) - if val.is_integer(): - val = int(val) - self._config["options"][key] = val + return self._parse_options_dict(self._config["options"]) def _test(self, data: DataInterface) -> DataInterface: """Run debug tests on data operations @@ -159,34 +170,132 @@ def _clean_solver_options(self): TODO: in future, reverse the direction of this function - instead pull all options that are directly required by the solver """ - return {i[0]: i[1] + _cleaned = {i[0]: i[1] for i in self._config["options"].items() if i[0] not in self._options_to_ignore} - - @abstractmethod + for key, val in _cleaned.items(): + if val == 'True': + _cleaned[key] = True + elif val == 'False': + _cleaned[key] = False + return _cleaned + def fit(self): - pass + cleaned_options = self._clean_solver_options() + try: + self.proc.set_params(**cleaned_options) + except Exception as exc: + print('The plugin encountered an error on the parameters of ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + try: + self.proc.fit(self.X) + except Exception as exc: + print('The plugin encountered an error when fitting ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise - @abstractmethod def transform(self, data: DataInterface) -> DataInterface: - pass + try: + data.append_data_column("X", pd.DataFrame(self.proc.transform(self.X))) + except Exception as exc: + print('The plugin encountered an error when transforming the data with ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + if self.X_tst is not None: + try: + data.append_data_column("X_test", pd.DataFrame(self.proc.transform(self.X_tst))) + except Exception as exc: + print('The plugin encountered an error when transforming the data with ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + return data class ModellingPluginT(PluginTemplate, ABC): def __init__(self, plugin_globals: dict) -> None: super().__init__(plugin_globals) - @abstractmethod def solve(self): - pass - - @abstractmethod - def predict(self): - pass + """Sends params to solver, then runs solver""" + try: + self.clf.set_params(**self._config["options"]) + except Exception as exc: + print('The plugin encountered an error on the parameters of ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + try: + self.clf.fit(self.X, self.Y) + except Exception as exc: + print('The plugin encountered an error when fitting ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + + def predict(self, data): + """Uses fitted model to predict output of a given Y + :param data: array-like or sparse matrix, shape (n_samples, n_features) + Samples + expected type: vai_lab.Data.Data_core.Data + :returns: array, shape (n_samples,) + Returns predicted values. + """ + try: + return self.clf.predict(data) + except Exception as exc: + print('The plugin encountered an error when predicting with ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + + def score(self, X, Y, sample_weight): + """Return the coefficient of determination + :param X : array-like of shape (n_samples, n_features) + :param Y : array-like of shape (n_samples,) or (n_samples, n_outputs) + :param sample_weight : array-like of shape (n_samples,), default=None + Sample weights. + + :returns: score : float of ``self.predict(X)`` wrt. `y`. + """ + try: + return self.clf.score(X, Y, sample_weight) + except Exception as exc: + print('The plugin encountered an error when calculating the score with ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + +class ModellingPluginTClass(ModellingPluginT, ABC): + def __init__(self, plugin_globals: dict) -> None: + super().__init__(plugin_globals) - @abstractmethod - def score(self): - pass + def predict_proba(self, data): + """Uses fitted model to predict the probability of the output of a given Y + :param data: array-like or sparse matrix, shape (n_samples, n_features) + Samples + expected type: vai_lab.Data.Data_core.Data + :returns: array, shape (n_samples,) + Returns predicted values. + """ + try: + return self.clf.predict_proba(data) + except Exception as exc: + print('The plugin encountered an error when predicting the probability with ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + +class DecisionMakingPluginT(PluginTemplate, ABC): + def __init__(self, plugin_globals: dict) -> None: + super().__init__(plugin_globals) + + def configure(self, config: dict): + """Extended from PluginTemplate.configure""" + super().configure(config) + try: + self.BO = self.model(**self._clean_options()) + except Exception as exc: + print('The plugin encountered an error on the parameters of ' + +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+'.') + raise + if type(self.X) is None and type(self.Y) is None: + print('Invalid Data name. Indicate whether to use `X` or `Y`') class UI(PluginTemplate, ABC): @@ -207,3 +316,31 @@ def save_file(self): @abstractmethod def save_file_as(self): pass + + +class EnvironmentPluginT(PluginTemplate, ABC): + + @abstractmethod + def load_model(self) -> None: + pass + + @abstractmethod + def connect(self): + pass + + @abstractmethod + def reset(self): + pass + + @abstractmethod + def disconnect(self): + pass + + @abstractmethod + def set_gui(self,use_gui:bool=True) -> None: + pass + + def configure(self, config: dict): + """Extended from PluginTemplate.configure""" + super().configure(config) + self.set_gui(self._config["options"]["usegui"]) \ No newline at end of file diff --git a/aidesign/_types.py b/src/vai_lab/_types.py similarity index 90% rename from aidesign/_types.py rename to src/vai_lab/_types.py index 5c11fed1..2d9d9470 100644 --- a/aidesign/_types.py +++ b/src/vai_lab/_types.py @@ -53,6 +53,22 @@ def fit(self): def transform(self, data: DataInterfaceT) -> DataInterface: ... +class EnvironmentPluginInterface(PluginInterface, Protocol): + def load_model(self) -> None: + ... + + def connect(self): + ... + + def reset(self): + ... + + def disconnect(self): + ... + + def run_simulation(self): + ... + class PluginSpecsInterface(Protocol): @property @@ -94,6 +110,8 @@ def print(self, value): class ModuleInterface(Protocol): + _debug: bool + def set_avail_plugins(self, avail_plugins: PluginSpecsInterface): ... diff --git a/aidesign/examples/crystalDesign/00001.png b/src/vai_lab/examples/crystalDesign/00001.png similarity index 100% rename from aidesign/examples/crystalDesign/00001.png rename to src/vai_lab/examples/crystalDesign/00001.png diff --git a/aidesign/examples/crystalDesign/00002.png b/src/vai_lab/examples/crystalDesign/00002.png similarity index 100% rename from aidesign/examples/crystalDesign/00002.png rename to src/vai_lab/examples/crystalDesign/00002.png diff --git a/aidesign/examples/crystalDesign/00003.png b/src/vai_lab/examples/crystalDesign/00003.png similarity index 100% rename from aidesign/examples/crystalDesign/00003.png rename to src/vai_lab/examples/crystalDesign/00003.png diff --git a/aidesign/examples/crystalDesign/00005.png b/src/vai_lab/examples/crystalDesign/00005.png similarity index 100% rename from aidesign/examples/crystalDesign/00005.png rename to src/vai_lab/examples/crystalDesign/00005.png diff --git a/aidesign/examples/crystalDesign/00007.png b/src/vai_lab/examples/crystalDesign/00007.png similarity index 100% rename from aidesign/examples/crystalDesign/00007.png rename to src/vai_lab/examples/crystalDesign/00007.png diff --git a/aidesign/examples/crystalDesign/00009.png b/src/vai_lab/examples/crystalDesign/00009.png similarity index 100% rename from aidesign/examples/crystalDesign/00009.png rename to src/vai_lab/examples/crystalDesign/00009.png diff --git a/aidesign/examples/crystalDesign/00011.png b/src/vai_lab/examples/crystalDesign/00011.png similarity index 100% rename from aidesign/examples/crystalDesign/00011.png rename to src/vai_lab/examples/crystalDesign/00011.png diff --git a/aidesign/examples/crystalDesign/00013.png b/src/vai_lab/examples/crystalDesign/00013.png similarity index 100% rename from aidesign/examples/crystalDesign/00013.png rename to src/vai_lab/examples/crystalDesign/00013.png diff --git a/aidesign/examples/crystalDesign/00015.png b/src/vai_lab/examples/crystalDesign/00015.png similarity index 100% rename from aidesign/examples/crystalDesign/00015.png rename to src/vai_lab/examples/crystalDesign/00015.png diff --git a/src/vai_lab/examples/crystalDesign/phasestability/.DS_Store b/src/vai_lab/examples/crystalDesign/phasestability/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0084bcb0d935f7fdeaf748e680dd4a5c5bad4435 GIT binary patch literal 6148 zcmeHKOHRW;47E!ZMQplcIalZnh7#DZ>ID#q1dBwe)OMev!7X|Wj==NS3ezUpB2?Lu z=S}=cJTFol6A^EY^_pl!L`$gP;ugzD~St7m!`&VVzJGa%oG02Pdey<+-wU`ZnY zuz)!U_A*OIPB4syy&^mi)>NRTvW*z5>97Zji-x_TrW4!n!S-af35C=SytH_pZi3%xb zH`<%?-K_6!Y}|m5 z?dvP;YlPF;(*$Q^KOITD>k7C6uE13)z??0X-79+Q3b+EUz(oQ1K18};ZrCcOPY0V+ z0ubjMPR72}5)u;)bHi4VGqj+gL=6pAVgwE6cr;#a*eYr`f)yXZRt{F7gtR*E9}^uR zSM=5ua0U7b-00;(^8e#>{ofDrD_6i3_*V*qd9f~5Jd)+s!Qmv=M*4;>CcIYh62eYO g#pFsVexN7gc$5w?H*6I-Li0ZYNd|9Rfj?E?2Xt;x>i_@% literal 0 HcmV?d00001 diff --git a/src/vai_lab/examples/crystalDesign/phasestability/CsFA/fulldata/CsFA_T300_above.csv b/src/vai_lab/examples/crystalDesign/phasestability/CsFA/fulldata/CsFA_T300_above.csv new file mode 100644 index 00000000..12a64d5c --- /dev/null +++ b/src/vai_lab/examples/crystalDesign/phasestability/CsFA/fulldata/CsFA_T300_above.csv @@ -0,0 +1 @@ +Cs,MA,FA,dEmix (ev/f.u.),TdSmix (ev/f.u.),dGmix (ev/f.u.) 1.00,0.00,0.00,0.000,0.000,0.000 0.92,0.00,0.08,0.043,0.007,0.036 0.92,0.00,0.08,0.053,0.007,0.046 0.88,0.00,0.13,0.055,0.010,0.045 0.88,0.00,0.13,0.033,0.010,0.023 0.83,0.00,0.17,0.090,0.012,0.078 0.83,0.00,0.17,0.089,0.012,0.077 0.75,0.00,0.25,0.041,0.015,0.026 0.75,0.00,0.25,0.038,0.015,0.023 0.73,0.00,0.28,0.020,0.015,0.005 0.73,0.00,0.28,0.017,0.016,0.001 0.67,0.00,0.33,0.011,0.016,-0.005 0.67,0.00,0.33,0.008,0.016,-0.008 0.65,0.00,0.35,0.000,0.017,-0.017 0.65,0.00,0.35,0.000,0.017,-0.017 0.63,0.00,0.38,-0.043,0.017,-0.060 0.63,0.00,0.38,-0.048,0.017,-0.065 0.58,0.00,0.42,-0.011,0.018,-0.029 0.58,0.00,0.42,-0.004,0.018,-0.022 0.50,0.00,0.50,-0.047,0.018,-0.065 0.50,0.00,0.50,-0.027,0.018,-0.045 0.42,0.00,0.58,-0.052,0.018,-0.070 0.42,0.00,0.58,-0.045,0.018,-0.063 0.38,0.00,0.63,-0.095,0.017,-0.112 0.38,0.00,0.63,-0.100,0.017,-0.117 0.33,0.00,0.67,-0.083,0.016,-0.099 0.33,0.00,0.67,-0.088,0.016,-0.104 0.25,0.00,0.75,-0.137,0.015,-0.152 0.25,0.00,0.75,-0.142,0.015,-0.157 0.17,0.00,0.83,-0.085,0.012,-0.097 0.17,0.00,0.83,-0.091,0.012,-0.103 0.13,0.00,0.88,-0.069,0.010,-0.079 0.13,0.00,0.88,-0.074,0.010,-0.084 0.08,0.00,0.92,-0.110,0.007,-0.117 0.08,0.00,0.92,-0.115,0.007,-0.122 0.00,0.00,1.00,0.000,0.000,0.000 \ No newline at end of file diff --git a/src/vai_lab/examples/crystalDesign/phasestability/CsMA/.DS_Store b/src/vai_lab/examples/crystalDesign/phasestability/CsMA/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5bdbc9ea04ee6e5e413f9d38c05b7e4363b17af6 GIT binary patch literal 6148 zcmeHKJ5Iw;5S)b+mS|E^zANMgPf<7l7a(Azij)GAHDXxOnQv9s3#Y`S!)o}jda~7oV)1mI-=ZAW6BVU^6u4C2 zKDTS{|4;Nk=Ko8Qc2YnJ+>`>g+CA^Ke5LBGvzPN;+vqQJulb_8aUB$fXvf58$J}^3 eevG25Yrf|FUN|KNo%x^>^)ukQ$fUrv75D~yVHI@% literal 0 HcmV?d00001 diff --git a/src/vai_lab/examples/crystalDesign/phasestability/CsMA/fulldata/CsMA_T300_above.csv b/src/vai_lab/examples/crystalDesign/phasestability/CsMA/fulldata/CsMA_T300_above.csv new file mode 100644 index 00000000..f2358371 --- /dev/null +++ b/src/vai_lab/examples/crystalDesign/phasestability/CsMA/fulldata/CsMA_T300_above.csv @@ -0,0 +1 @@ +Cs,MA,FA,dEmix (ev/f.u.),TdSmix (ev/f.u.),dGmix (ev/f.u.) 1.00,0.00,0.00,0.000,0.000,0.000 0.92,0.08,0.00,0.054,0.007,0.047 0.88,0.12,0.00,0.043,0.010,0.033 0.88,0.12,0.00,0.058,0.010,0.048 0.75,0.25,0.00,0.044,0.015,0.029 0.67,0.33,0.00,0.053,0.016,0.037 0.63,0.37,0.00,0.024,0.017,0.007 0.63,0.37,0.00,0.026,0.017,0.009 0.50,0.50,0.00,0.016,0.018,-0.002 0.50,0.50,0.00,0.037,0.018,0.019 0.42,0.58,0.00,0.012,0.018,-0.006 0.42,0.58,0.00,0.027,0.018,0.009 0.38,0.62,0.00,0.001,0.017,-0.016 0.38,0.62,0.00,0.003,0.017,-0.014 0.33,0.67,0.00,0.029,0.016,0.013 0.33,0.67,0.00,0.031,0.016,0.015 0.25,0.75,0.00,0.005,0.015,-0.010 0.25,0.75,0.00,0.005,0.015,-0.010 0.17,0.83,0.00,-0.016,0.012,-0.028 0.17,0.83,0.00,-0.013,0.012,-0.025 0.13,0.87,0.00,-0.038,0.010,-0.048 0.13,0.87,0.00,-0.026,0.010,-0.036 0.08,0.92,0.00,-0.014,0.007,-0.021 0.08,0.92,0.00,-0.045,0.007,-0.052 0.00,1.00,0.00,0.000,0.000,0.000 \ No newline at end of file diff --git a/src/vai_lab/examples/crystalDesign/phasestability/FAMA/.DS_Store b/src/vai_lab/examples/crystalDesign/phasestability/FAMA/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9ffa3a33701ca322297e306fceeb140fe11e8206 GIT binary patch literal 6148 zcmeHKK~BRk5FD2t0$h6J=r8mKQ59a$4^R@Ihn7^LK^*f99>F8{1<&9K%&u2eq9hV0 z1ZcNfk7Liq_9V)70fbdkErA7qj4n7kXES0lFFvrDjYwS3d)(m#TQu15=k^yAkY{&+ z5;Y$2jL!4hJuKH{y+3pgG0NN5MNu`!68QLhe^vGbulg46upO+M;ojb<3L6};sh-N+ zZC&d-HkcgQ_<+;f^qX#agwxsZ1ZQNk9lgBk3b+EUz;9K6Ia@5dOz5pE;0m|`Ck5pD z5b1)sVVh7t9c;7&AkI0g#=g`N5)%z`!!{vjXd#6XQ)sXfBcyPSN8{y&ZNe0eV8utU zm4j6%L09MfqtX%bgx + + + + + + [(350.0,50),0,{}] + + + + + + + + + + [(350.0, 350.0), 2, {0: 'd0-u2'}] + + + + + + + + + [(350.0, 650), 1, {2: 'd2-u1'}] + + + diff --git a/aidesign/examples/xml_files/KNN-classification_demo.xml b/src/vai_lab/examples/xml_files/KNN-classification_demo.xml similarity index 100% rename from aidesign/examples/xml_files/KNN-classification_demo.xml rename to src/vai_lab/examples/xml_files/KNN-classification_demo.xml diff --git a/aidesign/examples/xml_files/SVR_demo.xml b/src/vai_lab/examples/xml_files/SVR_demo.xml similarity index 100% rename from aidesign/examples/xml_files/SVR_demo.xml rename to src/vai_lab/examples/xml_files/SVR_demo.xml diff --git a/src/vai_lab/examples/xml_files/bayes_opt_demo.xml b/src/vai_lab/examples/xml_files/bayes_opt_demo.xml new file mode 100644 index 00000000..728002fe --- /dev/null +++ b/src/vai_lab/examples/xml_files/bayes_opt_demo.xml @@ -0,0 +1,36 @@ + + + + + + + [(350.0,50),0,{}] + + + + + + + + + [(350.0,350.0),2,{0:'d0-u2'}] + + + + None + + + {'CsPbI': (0,1), 'MAPbI': (0, 1), 'FAPbI': (0, 1)} + + + + + + + + + + [(350.0,650),1,{2:'d2-u1'}] + + + diff --git a/aidesign/examples/xml_files/canvas_demo.xml b/src/vai_lab/examples/xml_files/canvas_demo.xml similarity index 93% rename from aidesign/examples/xml_files/canvas_demo.xml rename to src/vai_lab/examples/xml_files/canvas_demo.xml index 04028d1c..5ed1f55d 100644 --- a/aidesign/examples/xml_files/canvas_demo.xml +++ b/src/vai_lab/examples/xml_files/canvas_demo.xml @@ -24,7 +24,7 @@ - + diff --git a/aidesign/examples/xml_files/crystalDesign_v2.xml b/src/vai_lab/examples/xml_files/crystalDesign_v2.xml similarity index 100% rename from aidesign/examples/xml_files/crystalDesign_v2.xml rename to src/vai_lab/examples/xml_files/crystalDesign_v2.xml diff --git a/aidesign/examples/xml_files/crystalDesign_v3.xml b/src/vai_lab/examples/xml_files/crystalDesign_v3.xml similarity index 100% rename from aidesign/examples/xml_files/crystalDesign_v3.xml rename to src/vai_lab/examples/xml_files/crystalDesign_v3.xml diff --git a/aidesign/examples/xml_files/k-mean_clustering_demo.xml b/src/vai_lab/examples/xml_files/k-mean_clustering_demo.xml similarity index 100% rename from aidesign/examples/xml_files/k-mean_clustering_demo.xml rename to src/vai_lab/examples/xml_files/k-mean_clustering_demo.xml diff --git a/aidesign/examples/xml_files/logistic_regression_demo.xml b/src/vai_lab/examples/xml_files/logistic_regression_demo.xml similarity index 100% rename from aidesign/examples/xml_files/logistic_regression_demo.xml rename to src/vai_lab/examples/xml_files/logistic_regression_demo.xml diff --git a/src/vai_lab/examples/xml_files/optimisation_demo.xml b/src/vai_lab/examples/xml_files/optimisation_demo.xml new file mode 100644 index 00000000..cb1b578d --- /dev/null +++ b/src/vai_lab/examples/xml_files/optimisation_demo.xml @@ -0,0 +1,55 @@ + + + + + + + [(350.0,50),0,{}] + + + + + + + + + + + + + [(350.0,350.0),2,{0:'d0-u2'}] + + + + None + + + EI + + + ['./examples/optimisation/phasestability/CsFA/fulldata/CsFA_T300_above.csv', './examples/optimisation/phasestability/FAMA/fulldata/FAMA_T300_above.csv', './examples/optimisation/phasestability/CsMA/fulldata/CsMA_T300_above.csv'] + + + True + + + local_penalization + + + 0.01 + + + [{'name': 'CsPbI', 'type': 'continuous', 'domain': (0, 1)}, {'name': 'MAPbI', 'type': 'continuous', 'domain': (0, 1)}, {'name': 'FAPbI', 'type': 'continuous', 'domain': (0, 1)}] + + + + + + + + + + [(350.0,650),1,{2:'d2-u1'}] + + + diff --git a/src/vai_lab/examples/xml_files/pybullet_env_example.xml b/src/vai_lab/examples/xml_files/pybullet_env_example.xml new file mode 100644 index 00000000..886051e0 --- /dev/null +++ b/src/vai_lab/examples/xml_files/pybullet_env_example.xml @@ -0,0 +1,48 @@ + + + + + + + [(300.0,50),0,{}] + + + + + + + + + [(300.0,300.0),2,{0:'d0-u2'}] + + + + plane.urdf + ./Environment/resources/models/half_cheetah_with_mass.xml + + + False + + + 0.0 + 0.0 + -9.81 + + + 0.01 + + + 10 + + + + + + + + + + [(300.0,550),1,{2:'d2-u1'}] + + + \ No newline at end of file diff --git a/aidesign/examples/xml_files/random_forest_class_demo.xml b/src/vai_lab/examples/xml_files/random_forest_class_demo.xml similarity index 73% rename from aidesign/examples/xml_files/random_forest_class_demo.xml rename to src/vai_lab/examples/xml_files/random_forest_class_demo.xml index 04dd8952..e7c0d450 100644 --- a/aidesign/examples/xml_files/random_forest_class_demo.xml +++ b/src/vai_lab/examples/xml_files/random_forest_class_demo.xml @@ -4,10 +4,10 @@ - - - - + + + + @@ -35,7 +35,7 @@ - + diff --git a/aidesign/examples/xml_files/regression_demo.xml b/src/vai_lab/examples/xml_files/regression_demo.xml similarity index 100% rename from aidesign/examples/xml_files/regression_demo.xml rename to src/vai_lab/examples/xml_files/regression_demo.xml diff --git a/aidesign/examples/xml_files/ridge-scalar-ridge_demo.xml b/src/vai_lab/examples/xml_files/ridge-scalar-ridge_demo.xml similarity index 98% rename from aidesign/examples/xml_files/ridge-scalar-ridge_demo.xml rename to src/vai_lab/examples/xml_files/ridge-scalar-ridge_demo.xml index b8131122..777ca31c 100644 --- a/aidesign/examples/xml_files/ridge-scalar-ridge_demo.xml +++ b/src/vai_lab/examples/xml_files/ridge-scalar-ridge_demo.xml @@ -37,7 +37,7 @@ - + True diff --git a/aidesign/examples/xml_files/ridge_regression_demo.xml b/src/vai_lab/examples/xml_files/ridge_regression_demo.xml similarity index 100% rename from aidesign/examples/xml_files/ridge_regression_demo.xml rename to src/vai_lab/examples/xml_files/ridge_regression_demo.xml diff --git a/aidesign/examples/xml_files/scalar_demo.xml b/src/vai_lab/examples/xml_files/scalar_demo.xml similarity index 91% rename from aidesign/examples/xml_files/scalar_demo.xml rename to src/vai_lab/examples/xml_files/scalar_demo.xml index e3768515..10e3f914 100644 --- a/aidesign/examples/xml_files/scalar_demo.xml +++ b/src/vai_lab/examples/xml_files/scalar_demo.xml @@ -21,10 +21,10 @@ [(300.0,300.0),2,{0:'d0-u2'}] - - - str - + + + True + diff --git a/aidesign/examples/xml_files/scaler-lasso_demo.xml b/src/vai_lab/examples/xml_files/scaler-lasso_demo.xml similarity index 100% rename from aidesign/examples/xml_files/scaler-lasso_demo.xml rename to src/vai_lab/examples/xml_files/scaler-lasso_demo.xml diff --git a/aidesign/examples/xml_files/user_feedback_demo.xml b/src/vai_lab/examples/xml_files/user_feedback_demo.xml similarity index 93% rename from aidesign/examples/xml_files/user_feedback_demo.xml rename to src/vai_lab/examples/xml_files/user_feedback_demo.xml index 16098dff..6d1a21a6 100644 --- a/aidesign/examples/xml_files/user_feedback_demo.xml +++ b/src/vai_lab/examples/xml_files/user_feedback_demo.xml @@ -25,7 +25,7 @@ - + diff --git a/src/vai_lab/run_pipeline.py b/src/vai_lab/run_pipeline.py new file mode 100644 index 00000000..6a9cba4a --- /dev/null +++ b/src/vai_lab/run_pipeline.py @@ -0,0 +1,68 @@ +""" +CLI for running a pipeline from a config file +or start a GUI if no config file given. +""" + +import argparse +from os.path import abspath + +import vai_lab as ai + + + +def parse_args(): + """ + Parse command line arguments + """ + + parser = argparse.ArgumentParser( + prog = 'vai_lab', + description = 'AI-assisted virtual laboratories', + ) + + parser.add_argument( + '-f', + '--file', + type=str, + nargs='+', + default=None, + help='pipeline config file', + ) + + args = parser.parse_args() + + return args + +def _config_files_iter(core,files): + """Iterate over config files and load them into core + TODO: WIP - need a reset function to avoid instantiating core every time + """ + if files: + for f in files: + core = ai.Core() + core.load_config_file(abspath(f)) + core.run() + + +def main(): + + # Parse command line arguments + args = parse_args() + + # Core instance + core = ai.Core() + + # Load config file if given + if args.file: + for i in range(0,len(args.file)): + args.file[i] = abspath(args.file[i]) + core.load_config_file(args.file) + # core.load_config_file(("./examples", + # "xml_files", + # 'bayes_opt_demo.xml')) + # Run pipeline + core.run() + +if __name__=='__main__': + + main() \ No newline at end of file diff --git a/src/vai_lab/tests/__init__.py b/src/vai_lab/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/vai_lab/tests/test_examples.py b/src/vai_lab/tests/test_examples.py new file mode 100644 index 00000000..b23eece1 --- /dev/null +++ b/src/vai_lab/tests/test_examples.py @@ -0,0 +1,18 @@ +from glob import glob + +import vai_lab as ai +from vai_lab._import_helper import get_lib_parent_dir + +def test_examples(): + """ + Test launching GUI + """ + scripts = glob(get_lib_parent_dir() + "/examples/xml_files/" + "*") + for file in scripts: + """Temp removal of userfeedback test - relies too heavily on GUI for github actions""" + if "user_feedback" not in file: + print (file) + core = ai.Core() + core.load_config_file(file) + core._debug = True + core.run() \ No newline at end of file diff --git a/src/vai_lab/tests/test_launch.py b/src/vai_lab/tests/test_launch.py new file mode 100644 index 00000000..07d70d5e --- /dev/null +++ b/src/vai_lab/tests/test_launch.py @@ -0,0 +1,9 @@ +import vai_lab as ai + +def test_launch(): + """ + Test launching GUI + """ + core = ai.Core() + core._debug = True + core.run() diff --git a/aidesign/utils/AID_core.py b/src/vai_lab/utils/AID_core.py similarity index 80% rename from aidesign/utils/AID_core.py rename to src/vai_lab/utils/AID_core.py index 824724ca..33e89ec3 100644 --- a/aidesign/utils/AID_core.py +++ b/src/vai_lab/utils/AID_core.py @@ -1,4 +1,4 @@ -from aidesign.GUI.GUI_core import GUI +from vai_lab.GUI.GUI_core import GUI class AID(GUI): """UserFeedback core class directly uses GUI_core.py""" diff --git a/aidesign/utils/UserInterfaceClass.py b/src/vai_lab/utils/UserInterfaceClass.py similarity index 62% rename from aidesign/utils/UserInterfaceClass.py rename to src/vai_lab/utils/UserInterfaceClass.py index 1e9329ea..da82108f 100644 --- a/aidesign/utils/UserInterfaceClass.py +++ b/src/vai_lab/utils/UserInterfaceClass.py @@ -1,8 +1,8 @@ -from aidesign._plugin_templates import UI +from vai_lab._plugin_templates import UI class UserInterfaceClass(UI): """UserInterfaceClass directly duplicates: - aidesign.UserInteraction.User_Interaction_template + vai_lab.UserInteraction.User_Interaction_template """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) \ No newline at end of file diff --git a/aidesign/utils/__init__.py b/src/vai_lab/utils/__init__.py similarity index 100% rename from aidesign/utils/__init__.py rename to src/vai_lab/utils/__init__.py diff --git a/aidesign/utils/plugins/MainPage.py b/src/vai_lab/utils/plugins/MainPage.py similarity index 88% rename from aidesign/utils/plugins/MainPage.py rename to src/vai_lab/utils/plugins/MainPage.py index ea0a23b3..e9b5dabe 100644 --- a/aidesign/utils/plugins/MainPage.py +++ b/src/vai_lab/utils/plugins/MainPage.py @@ -5,9 +5,9 @@ from tkinter.filedialog import askopenfilename, askdirectory import pandas as pd -from aidesign.Data.xml_handler import XML_handler -from aidesign.utils.plugins.dataLoader import dataLoader -from aidesign._import_helper import get_lib_parent_dir +from vai_lab.Data.xml_handler import XML_handler +from vai_lab.utils.plugins.dataLoader import dataLoader +from vai_lab._import_helper import get_lib_parent_dir, rel_to_abs _PLUGIN_READABLE_NAMES = {"main": "default", "main page": "alias", @@ -31,7 +31,10 @@ def __init__(self, parent, controller, config: dict): self.controller.output_type = 'regression' self.out_data = pd.DataFrame() self.bg = parent['bg'] + if not self.controller._debug: + self._setup_frame() + def _setup_frame(self): self.script_dir = get_lib_parent_dir() self.my_img1 = ImageTk.PhotoImage( Image.open( @@ -52,7 +55,7 @@ def __init__(self, parent, controller, config: dict): self.my_label = tk.Label(frame1, image=self.my_img1, - bg=parent['bg']) + bg=self.bg) self.my_label.grid(column=0, row=0, @@ -64,8 +67,8 @@ def __init__(self, parent, controller, config: dict): my_label = tk.Label(frame2, text='Indicate the data folder and define the pipeline.', pady=10, - font=controller.title_font, - bg=parent['bg'], + font=self.controller.title_font, + bg=self.bg, fg='white') my_label.grid(column=0, row=11, @@ -74,8 +77,8 @@ def __init__(self, parent, controller, config: dict): tk.Button(frame3, text='Upload data matrix', fg='white', - font=controller.title_font, - bg=parent['bg'], + font=self.controller.title_font, + bg=self.bg, height=3, width=20, command=self.upload_data_folder, @@ -83,8 +86,8 @@ def __init__(self, parent, controller, config: dict): tk.Button(frame3, text='Upload data path', fg='white', - font=controller.title_font, - bg=parent['bg'], + font=self.controller.title_font, + bg=self.bg, height=3, width=20, command=self.upload_data_path, @@ -93,8 +96,8 @@ def __init__(self, parent, controller, config: dict): text='Optional', pady=10, padx=10, - font=controller.title_font, - bg=parent['bg'], + font=self.controller.title_font, + bg=self.bg, fg='white') self.controller.Datalabel.grid(column=2, row=12) @@ -102,8 +105,8 @@ def __init__(self, parent, controller, config: dict): self.interactButton = tk.Button(frame3, text='Interact with canvas', fg='white', - font=controller.title_font, - bg=parent['bg'], + font=self.controller.title_font, + bg=self.bg, height=3, width=20, # state=tk.DISABLED, @@ -115,8 +118,8 @@ def __init__(self, parent, controller, config: dict): self. uploadButton = tk.Button(frame3, text='Upload XML file', fg='white', - font=controller.title_font, - bg=parent['bg'], + font=self.controller.title_font, + bg=self.bg, height=3, width=20, # state=tk.DISABLED, @@ -128,16 +131,16 @@ def __init__(self, parent, controller, config: dict): text='Incomplete', pady=10, padx=10, - font=controller.title_font, - bg=parent['bg'], + font=self.controller.title_font, + bg=self.bg, fg='white') self.controller.XMLlabel.grid(column=2, row=13) self.PluginButton = tk.Button(frame3, text='Modules plugins', fg='white', - font=controller.title_font, - bg=parent['bg'], + font=self.controller.title_font, + bg=self.bg, height=3, width=20, state=tk.DISABLED, @@ -148,8 +151,8 @@ def __init__(self, parent, controller, config: dict): self.RunButton = tk.Button(frame3, text='Run Pipeline', fg='white', - font=controller.title_font, - bg=parent['bg'], + font=self.controller.title_font, + bg=self.bg, height=3, width=20, state=tk.DISABLED, @@ -317,7 +320,7 @@ def start_dataloader(self): # Infers by default, should it be None? data[variable] = pd.read_csv(filename) isVar[i] = 1 - self.controller.s.append_input_data(variable, self.rel_path(filename)) + self.controller.xml_handler.append_input_data(variable, rel_to_abs(filename)) if i == 0: self.controller.Data.set(True) if any(isVar[1::2]) and ( @@ -333,21 +336,6 @@ def start_dataloader(self): tk.messagebox.showwarning(title='Error - X not specified', message='You need to specify X before proceeding.') - def rel_path(self, path): - """ Returns relative path if available. """ - if path[0].lower() == os.getcwd()[0].lower(): - #Same drive - _folder = os.path.relpath(path, os.getcwd()) - if _folder[:2] == '..': - # Absolute path - return path - else: - # Relative path - return os.path.join('.',_folder) - else: - #Different drive -> Absolute path - return path - def upload_data_path(self): """ Stores the directory containing the data that will be later loaded """ @@ -355,7 +343,7 @@ def upload_data_path(self): title='Select a folder', mustexist=True) if folder is not None and len(folder) > 0: - self.controller.s.append_input_data('X', self.rel_path(folder)) + self.controller.xml_handler.append_input_data('X', rel_to_abs(folder)) def upload_data_folder(self): """ Stores the directory containing the data that will be later loaded diff --git a/aidesign/utils/plugins/__init__.py b/src/vai_lab/utils/plugins/__init__.py similarity index 100% rename from aidesign/utils/plugins/__init__.py rename to src/vai_lab/utils/plugins/__init__.py diff --git a/aidesign/utils/plugins/aidCanvas.py b/src/vai_lab/utils/plugins/aidCanvas.py similarity index 95% rename from aidesign/utils/plugins/aidCanvas.py rename to src/vai_lab/utils/plugins/aidCanvas.py index f4030bd7..04171f0d 100644 --- a/aidesign/utils/plugins/aidCanvas.py +++ b/src/vai_lab/utils/plugins/aidCanvas.py @@ -1,5 +1,5 @@ -from aidesign.Data.xml_handler import XML_handler -from aidesign._types import DictT +from vai_lab.Data.xml_handler import XML_handler +from vai_lab._types import DictT import os import numpy as np @@ -10,7 +10,7 @@ import tkinter as tk from tkinter import messagebox from tkinter.filedialog import asksaveasfile, askopenfile, askopenfilename -from aidesign._import_helper import get_lib_parent_dir +from vai_lab._import_helper import get_lib_parent_dir _PLUGIN_READABLE_NAMES = {"aid_canvas": "default", @@ -34,6 +34,21 @@ def __init__(self, parent, controller, config: dict): self.bg = parent['bg'] self.controller = controller + self.canvas_startxy: List[Tuple] = [] + self.out_data = pd.DataFrame() + self.connections: DictT = {} + self.modules = 0 + self.module_list: List[str] = [] + self.module_names: List[str] = [] + + # Create module + self.w, self.h = 100, 50 + self.cr = 4 + + if not self.controller._debug: + self._setup_frame() + + def _setup_frame(self): script_dir = get_lib_parent_dir() self.tk.call('wm', 'iconphoto', self.controller._w, ImageTk.PhotoImage( file=os.path.join(os.path.join( @@ -58,7 +73,7 @@ def __init__(self, parent, controller, config: dict): frame3 = tk.Frame(self, bg=self.bg) frame4 = tk.Frame(self, bg=self.bg) - self.my_label = tk.Label(frame2, image=self.my_img1, bg=parent['bg'], anchor = 'center') + self.my_label = tk.Label(frame2, image=self.my_img1, bg=self.bg, anchor = 'center') self.my_label.grid(column=5, row=0, padx=(10, 10), pady=(10, 10), sticky="nwse") # Create canvas @@ -69,17 +84,6 @@ def __init__(self, parent, controller, config: dict): # height=self.height, background="white") self.canvas.pack(fill=tk.BOTH, expand=True, padx=(10, 0), pady=10) - self.canvas_startxy: List[Tuple] = [] - self.out_data = pd.DataFrame() - self.connections: DictT = {} - self.modules = 0 - self.module_list: List[str] = [] - self.module_names: List[str] = [] - - # Create module - self.w, self.h = 100, 50 - self.cr = 4 - # Initialiser module self.add_module('Initialiser', self.width/2, self.h, ini=True) self.add_module('Output', self.width/2, self.height - self.h, out=True) @@ -96,69 +100,70 @@ def __init__(self, parent, controller, config: dict): # for m, module in enumerate(modules): tk.Button( - frame4, text='Data processing', fg='white', bg=parent['bg'], + frame4, text='Data processing', fg='white', bg=self.bg, height=3, width=25, font=self.controller.pages_font, command=lambda: self.add_module('Data Processing', self.width/2, self.height/2) ).grid(column=5, row=1, padx=(10, 10), sticky="news") tk.Button( - frame4, text='Modelling', fg='white', bg=parent['bg'], + frame4, text='Modelling', fg='white', bg=self.bg, height=3, width=25, font=self.controller.pages_font, command=lambda: self.add_module('Modelling', self.width/2, self.height/2) ).grid(column=5, row=2, padx=(10, 10), sticky="news") tk.Button( - frame4, text='Decision making', fg='white', bg=parent['bg'], + frame4, text='Decision making', fg='white', bg=self.bg, height=3, width=25, font=self.controller.pages_font, command=lambda: self.add_module('Decision Making', self.width/2, self.height/2) ).grid(column=5, row=3, padx=(10, 10), sticky="news") tk.Button( - frame4, text='User Interaction', fg='white', bg=parent['bg'], + frame4, text='User Interaction', fg='white', bg=self.bg, height=3, width=25, font=self.controller.pages_font, command=lambda: self.add_module('User Interaction', self.width/2, self.height/2) ).grid(column=5, row=4, padx=(10, 10), sticky="news") tk.Button( - frame4, text = 'Input data', fg = 'white', bg = parent['bg'], + frame4, text = 'Input data', fg = 'white', bg = self.bg, height = 3, width = 25, font = self.controller.pages_font, command = lambda: self.add_module('Input Data', self.width/2, self.height/2) - ).grid(column = 5, row = 5, padx=(0,10), sticky="news") + ).grid(column = 5, row = 5, padx=(10,10), sticky="news") tk.Button( - frame4, text = 'Data Storage', fg = 'white', bg = parent['bg'], + frame4, text = 'Data Storage', fg = 'white', bg = self.bg, height = 3, width = 25, font = self.controller.pages_font, command = lambda: self.add_module('Data Storage', self.width/2, self.height/2) - ).grid(column = 5, row = 6, padx=(0,10), sticky="news") + ).grid(column = 5, row = 6, padx=(10,10), sticky="news") tk.Button( - frame4, text = 'Delete selection', fg = 'white', bg = parent['bg'], + frame4, text = 'Delete selection', fg = 'white', bg = self.bg, height = 3, width = 25, font = self.controller.pages_font, command = self.delete_sel).grid(column = 5, row = 7, sticky="news" - , padx=(0,10), pady=(0,10)) + , padx=(10,10), pady=(0,10)) + ww = int(self.width/40) tk.Button( - frame3, text='Upload', fg='white', bg=parent['bg'], - height=3, width=15, font=self.controller.pages_font, + frame3, text='Upload', fg='white', bg=self.bg, + height=3, width=ww, font=self.controller.pages_font, command=self.upload).grid(column=0, row=10, sticky="news", padx=(10, 0), pady=(0, 10)) tk.Button( - frame3, text='Save', fg='white', bg=parent['bg'], - height=3, width=15, font=self.controller.pages_font, + frame3, text='Save', fg='white', bg=self.bg, + height=3, width=ww, font=self.controller.pages_font, command=self.save_file).grid(column=1, row=10, sticky="news", pady=(0, 10)) tk.Button( - frame3, text='Reset', fg='white', bg=parent['bg'], - height=3, width=15, font=self.controller.pages_font, + frame3, text='Reset', fg='white', bg=self.bg, + height=3, width=ww, font=self.controller.pages_font, command=self.reset).grid(column=2, row=10, sticky="news", pady=(0, 10)) tk.Button( - frame3, text='Done', fg='white', bg=parent['bg'], - height=3, width=15, font=self.controller.pages_font, + frame3, text='Done', fg='white', bg=self.bg, + height=3, width=ww, font=self.controller.pages_font, command=self.check_quit).grid(column=3, row=10, sticky="news", pady=(0, 10)) self.save_path = '' @@ -169,7 +174,7 @@ def __init__(self, parent, controller, config: dict): frame3.grid(column=0, row=2, sticky="swe") frame4.grid(column=1, row=1, sticky="nse") - frame3.grid_columnconfigure(4, weight=1) + frame3.grid_columnconfigure(tuple(range(4)), weight=1) frame4.grid_rowconfigure(7, weight=1) def class_list(self, value): @@ -697,9 +702,9 @@ def save_file(self): # to avoid numpy bug during elementwise comparison of lists loop_modules.dtype = mn.dtype if len(loop_modules) == 0 else loop_modules.dtype - self.controller.s.update_module_coords(self.module_list[0],[self.canvas_startxy[0], 0, self.connections[0]]) - self.controller.s.append_module_relationships(self.module_list[0],list(mn[values[:,0]]),list(mn[values[0,:]])) - self.controller.s.filename = self.save_path.name + self.controller.xml_handler.update_module_coords(self.module_list[0],[self.canvas_startxy[0], 0, self.connections[0]]) + self.controller.xml_handler.append_module_relationships(self.module_list[0],list(mn[values[:,0]]),list(mn[values[0,:]])) + self.controller.xml_handler.filename = self.save_path.name for i, mnn in enumerate(mn_id): if (i > 1) and mnn: xml_parent = None @@ -708,7 +713,7 @@ def save_file(self): for x, loop in enumerate(self.loops): if mn[i] in loop['mod']: # Model in this loop if not out_loops[x]: # Loop already defined - self.controller.s.append_pipeline_loop(self.loops[x]['type'], + self.controller.xml_handler.append_pipeline_loop(self.loops[x]['type'], self.loops[x]['condition'], "loop" + str(x), @@ -723,7 +728,7 @@ def save_file(self): xml_parent = "loop"+str(x) parent_loops.append("loop"+str(x)) - self.controller.s.append_pipeline_module(self.module_list[i], + self.controller.xml_handler.append_pipeline_module(self.module_list[i], mn[i], "", {}, @@ -734,7 +739,7 @@ def save_file(self): xml_parent, [self.canvas_startxy[i], i, self.connections[i]]) - self.controller.s.append_pipeline_module(self.module_list[1], # Out + self.controller.xml_handler.append_pipeline_module(self.module_list[1], # Out mn[1], "", {}, @@ -742,7 +747,7 @@ def save_file(self): list(mn[values[1, :]]), None, [self.canvas_startxy[1], 1, self.connections[1]]) - self.controller.s.write_to_XML() + self.controller.xml_handler.write_to_XML() self.saved = True self.controller._append_to_output( "xml_filename", self.save_path.name) diff --git a/aidesign/utils/plugins/dataLoader.py b/src/vai_lab/utils/plugins/dataLoader.py similarity index 94% rename from aidesign/utils/plugins/dataLoader.py rename to src/vai_lab/utils/plugins/dataLoader.py index c940b3c4..cd0559a4 100644 --- a/aidesign/utils/plugins/dataLoader.py +++ b/src/vai_lab/utils/plugins/dataLoader.py @@ -3,9 +3,17 @@ from PIL import ImageTk from tkinter import ttk import numpy as np -from aidesign._import_helper import get_lib_parent_dir +from vai_lab._import_helper import get_lib_parent_dir # from ttkwidgets import CheckboxTreeview # from sys import platform +_PLUGIN_READABLE_NAMES = {"dataLoader": "default", + "data_Loader": "alias", + "data loader": "alias"} # type:ignore +_PLUGIN_MODULE_OPTIONS = {"layer_priority": 2, + "required_children": None} # type:ignore +_PLUGIN_REQUIRED_SETTINGS = {} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {} # type:ignore + class dataLoader(): """ Creates a window diff --git a/aidesign/utils/plugins/pluginCanvas.py b/src/vai_lab/utils/plugins/pluginCanvas.py similarity index 91% rename from aidesign/utils/plugins/pluginCanvas.py rename to src/vai_lab/utils/plugins/pluginCanvas.py index 8ec8788e..4b648c14 100644 --- a/aidesign/utils/plugins/pluginCanvas.py +++ b/src/vai_lab/utils/plugins/pluginCanvas.py @@ -1,6 +1,6 @@ -from aidesign.Data.xml_handler import XML_handler -from aidesign._plugin_helpers import PluginSpecs -from aidesign._import_helper import get_lib_parent_dir +from vai_lab.Data.xml_handler import XML_handler +from vai_lab._plugin_helpers import PluginSpecs +from vai_lab._import_helper import get_lib_parent_dir import os import numpy as np @@ -32,7 +32,19 @@ def __init__(self, parent, controller, config: dict): self.parent = parent self.controller = controller self.s = XML_handler() + self.m: int + self.w, self.h = 100, 50 + self.cr = 4 + self.id_done = [0, 1] + self.id_mod:List[int] = [0, 1] + self.out_data = pd.DataFrame() + self.plugin: Dict[int, tk.StringVar] = {} + self.dataType: Dict[int, tk.StringVar] = {} + self.allWeHearIs: List[tk.Radiobutton] = [] + if not self.controller._debug: + self._setup_frame() + def _setup_frame(self): script_dir = get_lib_parent_dir() self.tk.call('wm', 'iconphoto', self.controller._w, ImageTk.PhotoImage( file=os.path.join(os.path.join( @@ -57,16 +69,8 @@ def __init__(self, parent, controller, config: dict): height=self.height, background="white") self.canvas.pack(fill=tk.BOTH, expand=True, padx=(10, 0), pady=10) - self.m: int - self.w, self.h = 100, 50 - self.cr = 4 self.canvas.bind('', self.on_click) - self.id_done = [0, 1] - self.id_mod:List[int] = [0, 1] - self.out_data = pd.DataFrame() - self.plugin: Dict[int, tk.StringVar] = {} - self.dataType: Dict[int, tk.StringVar] = {} - self.allWeHearIs: List[tk.Radiobutton] = [] + self.my_label = tk.Label(self.frame2, text='', @@ -91,14 +95,15 @@ def __init__(self, parent, controller, config: dict): 'Assets', 'forw_arrow.png')).resize((140, 60))) + ww = int(self.width/40) tk.Button( - frame3, text='Load Pipeline', fg='white', bg=parent['bg'], - height=3, width=15, font=self.controller.pages_font, + frame3, text='Load Pipeline', fg='white', bg=self.bg, + height=3, width=ww, font=self.controller.pages_font, command=self.upload).grid(column=0, row=0, sticky="news", padx=(10, 0), pady=(0, 10)) tk.Button( - frame3, text='Back to main', fg='white', bg=parent['bg'], - height=3, width=15, font=self.controller.pages_font, + frame3, text='Back to main', fg='white', bg=self.bg, + height=3, width=ww, font=self.controller.pages_font, command=self.check_quit).grid(column=1, row=0, sticky="news", pady=(0, 10)) self.frame_canvas = tk.Canvas( @@ -113,15 +118,16 @@ def __init__(self, parent, controller, config: dict): self.save_path = '' self.saved = True frame1.grid(column=0, row=0, rowspan=5, sticky="nsew") - self.frame2.grid(column=1, row=0, sticky="new") + self.frame2.grid(column=1, row=0, sticky="new", padx=(10, 0)) frame3.grid(column=0, row=5, sticky="nswe") - self.frame4.grid(column=1, row=5, sticky="sew") + self.frame4.grid(column=1, row=5, sticky="nsew") # self.frame2.grid_columnconfigure(tuple(range(2)), weight=1) self.frame2.grid_columnconfigure(1, weight=1) - # frame3.grid_columnconfigure(tuple(range(2)), weight=2) frame3.grid_columnconfigure(tuple(range(2)), weight=2) + # frame3.grid_columnconfigure(2, weight=2) self.frame4.grid_columnconfigure(tuple(range(2)), weight=2) + # self.frame4.grid_columnconfigure(2, weight=2) def class_list(self, value): """ Temporary fix """ @@ -223,6 +229,8 @@ def select(self, x: float, y: float): self.my_label.config(text='') for widget in self.allWeHearIs: widget.grid_remove() + for widget in self.radio_label: + widget.grid_remove() if hasattr(self, 'button_forw'): self.button_back.config(image=self.back_img, bg=self.bg, state=tk.DISABLED, text='') @@ -246,7 +254,7 @@ def check_updated(self): if (self.m in self.plugin.keys()) and\ (self.plugin[self.m].get() != 'None'): # add self.id_done.append(self.m) - self.s.append_plugin_to_module(self.plugin[self.m].get(), + self.xml_handler.append_plugin_to_module(self.plugin[self.m].get(), {**self.req_settings, ** self.opt_settings}, np.array(self.module_names)[ @@ -287,21 +295,21 @@ def display_buttons(self): self.framexx = [] framexx_name = np.array([]) framexx_i = [] + self.radio_label = [] values = np.append(np.unique(type_list[type_list != None]), 'other') if not any( type_list == 'other') else np.unique(type_list[type_list != None]) for i, t in enumerate(values): self.framexx.append(tk.Frame(self.framep, bg=self.bg)) framexx_name = np.append( framexx_name, t if t is not None else 'other') - print(framexx_name) framexx_i.append(0) - label = tk.Label(self.framexx[-1], + self.radio_label.append(tk.Label(self.framexx[-1], text=framexx_name[-1].capitalize(), pady=10, font=self.controller.title_font, bg=self.bg, - fg='white') - label.grid(column=0, + fg='white')) + self.radio_label[-1].grid(column=0, row=0, padx=(10, 0), sticky='nw') self.framexx[-1].grid(column=0, row=i, sticky="nsew") unsupervised = ['clustering', 'RL'] @@ -357,7 +365,6 @@ def optionsWindow(self): # Print settings tk.Label(frame1, text="Please indicate your desired options for the "+self.plugin[self.m].get()+" plugin.", anchor=tk.N, justify=tk.LEFT).pack(expand=True) - # self.entry = [] style = ttk.Style() style.configure( @@ -373,14 +380,12 @@ def optionsWindow(self): frame2 = tk.Frame(self.newWindow, bg='green') self.r = 1 self.create_treeView(frame2) - self.fill_treeview(frame2, self.req_settings, self.opt_settings) + self.fill_treeview(self.req_settings, self.opt_settings) frame2.grid(column=0, row=1, sticky="nswe", pady=10, padx=10) frame2.grid_rowconfigure(tuple(range(self.r)), weight=1) frame2.grid_columnconfigure(tuple(range(2)), weight=1) - # if len(self.entry) > 0: - # self.entry[0].focus() self.finishButton = tk.Button( frame4, text='Finish', command=self.removewindow) self.finishButton.grid( @@ -391,22 +396,19 @@ def optionsWindow(self): frame1.grid(column=0, row=0, sticky="ew") frame4.grid(column=0, row=2, sticky="se") - # self.newWindow.grid_rowconfigure(0, weight=1) self.newWindow.grid_rowconfigure(1, weight=2) self.newWindow.grid_columnconfigure(0, weight=1) - def create_treeView(self, frame): + def create_treeView(self, tree_frame): """ Function to create a new tree view in the given frame Parameters ---------- - frame : tk.Frame - frame where the tree view will be included + tree_frame : tk.Frame + frame where the tree view will be included key : str key for the tree dictionary """ - tree_frame = frame #tk.Frame(frame) - # tree_frame.grid(row=0, column=0, sticky="nsew", pady=10, padx=10) tree_scrollx = tk.Scrollbar(tree_frame, orient='horizontal') tree_scrollx.pack(side=tk.BOTTOM, fill=tk.X) @@ -425,7 +427,6 @@ def create_treeView(self, frame): self.tree['columns'] = columns_names # Format columns - # self.tree['show'] = 'headings' self.tree.column("#0", width=20, minwidth=0, stretch=tk.NO) for n, cl in enumerate(columns_names): @@ -443,19 +444,6 @@ def create_treeView(self, frame): background='#E8E8E8') self.tree.tag_configure('func', foreground='black', background='#DFDFDF') - - # Add data - # for n, sample in enumerate(self.out_data): - # if n % 2 == 0: - # self.tree.insert(parent='', index='end', iid=n, text=n+1, - # values=tuple(sample.astype(int)), tags=('even',)) - # else: - # self.tree.insert(parent='', index='end', iid=n, text=n+1, - # values=tuple(sample.astype(int)), tags=('odd',)) - - # Select the current row - # self.tree.selection_set(str(int(0))) - # Define double-click on row action self.tree.bind("", self.OnDoubleClick) @@ -510,10 +498,11 @@ def on_return(self, event): self.entry.destroy() self.saved = False - def fill_treeview(self, frame, req_settings, opt_settings, parent = ''): + def fill_treeview(self, req_settings, opt_settings, parent = ''): """ Adds an entry for each setting. Displays it in the specified row. - :param frame: tkinter frame type of frame - :param settings: dict type of plugin setting options + :param req_settings: dict type of plugin required setting options + :param opt_settings: dict type of plugin optional setting options + :param parent: string type of parent name """ self.tree.insert(parent=parent, index='end', iid=parent+'_req', text='', values=tuple(['Required settings', '', '']), tags=('type',)) @@ -537,24 +526,35 @@ def fill_treeview(self, frame, req_settings, opt_settings, parent = ''): def removewindow(self): """ Stores settings options and closes window """ self.req_settings.pop("Data", None) - req_keys = list(self.req_settings.keys()) - opt_keys = list(self.opt_settings.keys()) - for c, child in enumerate(self.tree.get_children()): - val = self.tree.item(child)["values"] - tag = self.tree.item(child)["tags"][0] - if val[0] == 'Data': - if val[2] == 'Choose X or Y' or len(val[2]) == 0: - self.updateSettings(tag, val[0], 'X') - else: - self.updateSettings(tag, val[0], val[2]) - else: - if val[2] == 'default' or len(str(val[2])) == 0: - self.updateSettings(tag, val[0]) - else: - self.updateSettings(tag, val[0], val[2]) + children = self.get_all_children() + for child in children: + tag = self.tree.item(child)["tags"][0] + if tag in ['req', 'opt']: + val = self.tree.item(child)["values"] + self.settingOptions(tag, val) self.newWindow.destroy() self.newWindow = None self.focus() + + def get_all_children(self, item=""): + """ Iterates over the treeview to get all children """ + children = self.tree.get_children(item) + for child in children: + children += self.get_all_children(child) + return children + + def settingOptions(self, tag, val): + """ Identifies how the data should be stored """ + if val[0] == 'Data': + if val[2] == 'Choose X or Y' or len(val[2]) == 0: + self.updateSettings(tag, val[0], 'X') + else: + self.updateSettings(tag, val[0], val[2]) + else: + if val[2] == 'default' or len(str(val[2])) == 0: + self.updateSettings(tag, val[0]) + else: + self.updateSettings(tag, val[0], val[2]) def updateSettings(self, tag, key, value = None): """ Return the selected settings @@ -565,7 +565,10 @@ def updateSettings(self, tag, key, value = None): tag for the settings """ if tag == 'req': - self.req_settings[key] = value + if value is not None or self.req_settings[key] != value: + self.req_settings[key] = value + else: + self.req_settings.pop(key, None) elif tag == 'opt': if value is not None or self.opt_settings[key] != value: self.opt_settings[key] = value @@ -704,10 +707,10 @@ def upload(self): self.reset() - self.s = XML_handler() - self.s.load_XML(filename) - # self.s._print_pretty(self.s.loaded_modules) - modules = self.s.loaded_modules + self.xml_handler = XML_handler() + self.xml_handler.load_XML(filename) + # self.xml_handler._print_pretty(self.xml_handler.loaded_modules) + modules = self.xml_handler.loaded_modules modout = modules['Output'] # They are generated when resetting del modules['Initialiser'], modules['Output'] @@ -729,8 +732,8 @@ def upload(self): yout + self.cr, xins + self.cr, yins + self.cr, - fill="red", width = 2, - arrow = tk.LAST, arrowshape = (12,10,5), + fill = "red", width = 2, + arrow = tk.LAST, arrowshape = (12,10,5), tags=('o'+str(parent_id), 'o'+str(1), modout['coordinates'][2][connect[p]])) self.out_data.iloc[int(parent_id)][1] = 1 @@ -832,8 +835,8 @@ def draw_connection(self, modules): yout + self.cr, xins + self.cr, yins + self.cr, - fill = "red", width = 2, - arrow = tk.LAST, arrowshape = (12,10,5), + fill = "red", width = 2, + arrow = tk.LAST, arrowshape = (12,10,5), tags = ('o'+str(parent_id), # 'o'+str(self.id_mod[-1]), modules[key]['coordinates'][2][connect[p]])) @@ -882,18 +885,18 @@ def check_quit(self): self.canvas.delete(tk.ALL) self.frame_canvas.delete(tk.ALL) self.saved = True - self.s.write_to_XML() + self.xml_handler.write_to_XML() self.controller.Plugin.set(True) self.controller._show_frame("MainPage") # TODO: Check if loaded - elif len(self.s.loaded_modules) == 0: + elif len(self.xml_handler.loaded_modules) == 0: self.controller._show_frame("MainPage") self.controller.Plugin.set(False) else: self.reset() self.canvas.delete(tk.ALL) self.frame_canvas.delete(tk.ALL) - self.s.write_to_XML() + self.xml_handler.write_to_XML() self.controller.Plugin.set(True) self.controller._show_frame("MainPage") diff --git a/aidesign/utils/plugins/progressTracker.py b/src/vai_lab/utils/plugins/progressTracker.py similarity index 51% rename from aidesign/utils/plugins/progressTracker.py rename to src/vai_lab/utils/plugins/progressTracker.py index 8888dcd4..4dc7a6a0 100644 --- a/aidesign/utils/plugins/progressTracker.py +++ b/src/vai_lab/utils/plugins/progressTracker.py @@ -1,13 +1,13 @@ import os import tkinter as tk from typing import Dict +from tkinter import ttk import numpy as np import pandas as pd from PIL import Image, ImageTk -from aidesign._plugin_helpers import PluginSpecs -from aidesign.Data.xml_handler import XML_handler -from aidesign._import_helper import get_lib_parent_dir +from vai_lab.Data.xml_handler import XML_handler +from vai_lab._import_helper import get_lib_parent_dir _PLUGIN_READABLE_NAMES = {"progress_tracker":"default", "progressTracker":"alias", @@ -27,27 +27,27 @@ def __init__(self, parent, controller, config: dict): super().__init__(parent, bg=parent['bg']) self.bg = parent['bg'] self.controller = controller - self.s = XML_handler() + self.xml_handler = XML_handler() self.controller.title('Progress Tracker') - script_dir = os.path.dirname(__file__) - self.tk.call('wm', 'iconphoto', self.controller._w, ImageTk.PhotoImage( - file=os.path.join(os.path.join( - get_lib_parent_dir(), - 'utils', - 'resources', - 'Assets', - 'VAILabsIcon.ico')))) + if not self.controller._debug: + self.tk.call('wm', 'iconphoto', self.controller._w, ImageTk.PhotoImage( + file=os.path.join(os.path.join( + get_lib_parent_dir(), + 'utils', + 'resources', + 'Assets', + 'VAILabsIcon.ico')))) self.grid_columnconfigure(0, weight=1) - frame1 = tk.Frame(self, bg=self.bg) - self.frame2 = tk.Frame(self, bg=self.bg) + self.frame1 = tk.Frame(self, bg=self.bg) + frame2 = tk.Frame(self, bg=self.bg) frame3 = tk.Frame(self, bg=self.bg) self.frame4 = tk.Frame(self, bg=self.bg) # Create canvas self.width, self.height = 700, 700 - self.canvas = tk.Canvas(frame1, width=self.width, + self.canvas = tk.Canvas(self.frame1, width=self.width, height=self.height, background="white") self.canvas.pack(fill=tk.BOTH, expand=True, padx=(10, 0), pady=10) @@ -61,11 +61,9 @@ def __init__(self, parent, controller, config: dict): self.canvas.bind('', self.on_click) self.dataType: Dict = {} - self.upload() - sec = 5 self.click = False - self.my_label = tk.Label(self.frame2, + self.my_label = tk.Label(frame2, text = 'This window will get\nclosed after '+str(sec)+' seconds\nunless you click on the canvas.', pady= 10, @@ -75,6 +73,9 @@ def __init__(self, parent, controller, config: dict): anchor = tk.CENTER) self.my_label.grid(column = 0, row = 0, columnspan = 2, padx=10) + + self.upload() + tk.Button( self.frame4, text = 'Next step', fg = 'white', bg = parent['bg'], height = 3, width = 15, font = self.controller.pages_font, @@ -84,11 +85,11 @@ def __init__(self, parent, controller, config: dict): height = 3, width = 15, font = self.controller.pages_font, command = self.terminate).grid(column = 0, row = 26, sticky="news", pady=(0,10)) - self.controller._append_to_output('close', False) + self.controller._append_to_output('terminate', False) self.save_path = '' self.saved = True - frame1.grid(column=0, row=0, sticky="nsew") - self.frame2.grid(column=1, row=0, sticky="new") + self.frame1.grid(column=0, row=0, sticky="nsew") + frame2.grid(column=1, row=0, sticky="new") frame3.grid(column=0, row=1, sticky="swe") self.frame4.grid(column=1, row=1, sticky="sew") @@ -97,10 +98,6 @@ def __init__(self, parent, controller, config: dict): self.controller.after(sec*1000,lambda:self.check_click()) - def class_list(self,value): - """ Temporary fix """ - return value - def on_click(self, event): """ Passes the mouse click coordinates to the select function.""" self.click = True @@ -113,7 +110,7 @@ def check_click(self): def terminate(self): """ Terminates window and pipeline. """ - self.controller._append_to_output('close', True) + self.controller._append_to_output('terminate', True) self.check_quit() def select(self, x: float, y:float): @@ -184,8 +181,8 @@ def add_module(self,boxName: str,x: float,y:float,ini = False,out = False): tag = ('n0',) text_w = self.controller.pages_font.measure(boxName+'-00') + 10 # Check module status - if 'start' in self.controller.status[boxName]: - if 'finish' in self.controller.status[boxName]: + if 'start' in self.controller._status[boxName]: + if 'finish' in self.controller._status[boxName]: colour = '#46da63' else: colour = '#dbaa21' @@ -213,9 +210,9 @@ def add_module(self,boxName: str,x: float,y:float,ini = False,out = False): justify = tk.CENTER) CanvasTooltip(self.canvas, self.canvas.find_withtag('p'+str(self.modules))[0], - text = 'Model progress status:\n'+self.pretty_status(self.controller.status[boxName])) # Link box + text = 'Model progress status:\n'+self.pretty_status(self.controller._status[boxName])) # Link box CanvasTooltip(self.canvas, self.canvas.find_withtag('t'+str(self.modules))[0], - text = 'Model progress status:\n'+self.pretty_status(self.controller.status[boxName])) # Link text + text = 'Model progress status:\n'+self.pretty_status(self.controller._status[boxName])) # Link text if not out: self.canvas.create_oval( @@ -259,128 +256,253 @@ def add_module(self,boxName: str,x: float,y:float,ini = False,out = False): self.canvas_startxy.append((x, y)) self.connections[self.modules] = {} self.module_out(boxName) - self.module_list.append(boxName) + # self.module_list.append(boxName) self.modules += 1 def optionsWindow(self): """ Function to create a new window displaying the available options of the selected plugin.""" - self.module = np.array(self.module_list)[self.m == np.array(self.id_mod)][0] - self.plugin = self.s.loaded_modules[self.module]['plugin']['plugin_name'] - module_type = self.s.loaded_modules[self.module]['module_type'] - ps = PluginSpecs() - self.opt_settings = ps.optional_settings[module_type][self.plugin] - self.req_settings = ps.required_settings[module_type][self.plugin] + mod_idx = np.where(self.m == np.array(self.id_mod))[0][0] + self.module = np.array(self.module_list)[mod_idx] + self.plugin = np.array(self.plugin_list)[mod_idx] + module_type = np.array(self.type_list)[mod_idx] + + self.opt_settings = self.controller._avail_plugins.optional_settings[module_type][self.plugin] + self.req_settings = self.controller._avail_plugins.required_settings[module_type][self.plugin] if (len(self.opt_settings) != 0) or (len(self.req_settings) != 0): if hasattr(self, 'newWindow') and (self.newWindow!= None): self.newWindow.destroy() self.newWindow = tk.Toplevel(self.controller) # Window options self.newWindow.title(self.plugin+' plugin options') - script_dir = os.path.dirname(__file__) - self.tk.call('wm','iconphoto', self.newWindow, ImageTk.PhotoImage( - file = os.path.join(os.path.join( - script_dir, - 'resources', - 'Assets', - 'AIDIcon.ico')))) + script_dir = get_lib_parent_dir() + self.tk.call('wm', 'iconphoto', self.newWindow, ImageTk.PhotoImage( + file=os.path.join(os.path.join( + script_dir, + 'utils', + 'resources', + 'Assets', + 'VAILabsIcon.ico')))) self.newWindow.geometry("350x400") - + self.raise_above_all(self.newWindow) frame1 = tk.Frame(self.newWindow) frame4 = tk.Frame(self.newWindow) # Print settings tk.Label(frame1, text ="Please indicate your desired options for the "+self.plugin+" plugin.", anchor = tk.N, justify=tk.LEFT).pack(expand = True) - self.entry = [] - # Required - r = 1 - if len(self.req_settings) > 0: - frame2 = tk.Frame(self.newWindow) - tk.Label(frame2, - text ="Required settings:", anchor = tk.W, justify=tk.LEFT).grid(row=0,column=0 , columnspan=2) - self.displaySettings(frame2, self.req_settings) - frame2.grid(column=0, row=r, sticky="nswe") - r += 1 - # Optional - if len(self.opt_settings) > 0: - frame3 = tk.Frame(self.newWindow) - tk.Label(frame3, - text ="Optional settings:", anchor = tk.W, justify=tk.LEFT).grid(row=0,column=0, columnspan=2) - self.displaySettings(frame3, self.opt_settings) - frame3.grid(column=0, row=r, sticky="nswe") - r += 1 - if len(self.entry) > 0: - self.entry[0].focus() + + style = ttk.Style() + style.configure( + "Treeview", background='white', foreground='white', + rowheight=25, fieldbackground='white', + # font=self.controller.pages_font + ) + style.configure("Treeview.Heading", + # font=self.controller.pages_font + ) + style.map('Treeview', background=[('selected', 'grey')]) + + frame2 = tk.Frame(self.newWindow, bg='green') + self.r = 1 + self.create_treeView(frame2, ['Name', 'Type', 'Value']) + self.fill_treeview(frame2, self.req_settings, self.opt_settings) + frame2.grid(column=0, row=1, sticky="nswe", pady=10, padx=10) + + frame2.grid_rowconfigure(tuple(range(self.r)), weight=1) + frame2.grid_columnconfigure(tuple(range(2)), weight=1) + self.finishButton = tk.Button( - frame4, text = 'Finish', command = self.removewindow) - self.finishButton.grid(column = 1, row = r+1, sticky="es", pady=(0,10), padx=(0,10)) - self.finishButton.bind("", lambda event: self.removewindow()) + frame4, text='Finish', command=self.removewindow) + self.finishButton.grid( + column=1, row=0, sticky="es", pady=(0, 10), padx=(0, 10)) + self.finishButton.bind( + "", lambda event: self.removewindow()) self.newWindow.protocol('WM_DELETE_WINDOW', self.removewindow) - frame1.grid(column=0, row=0, sticky="new") - frame4.grid(column=0, row=r, sticky="se") - self.newWindow.grid_rowconfigure(0, weight=1) - self.newWindow.grid_rowconfigure(tuple(range(r+1)), weight=2) + frame1.grid(column=0, row=0, sticky="ew") + frame4.grid(column=0, row=2, sticky="se") + self.newWindow.grid_rowconfigure(1, weight=2) self.newWindow.grid_columnconfigure(0, weight=1) - def displaySettings(self, frame, settings): - """ Adds an entry for each input setting. Displays it in the specified row. - :param frame: tkinter frame type of frame - :param settings: dict type of plugin setting options + def raise_above_all(self, window): + window.attributes('-topmost', 1) + window.attributes('-topmost', 0) + + def create_treeView(self, tree_frame, columns_names): + """ Function to create a new tree view in the given frame + + Parameters + ---------- + tree_frame : tk.Frame + frame where the tree view will be included + key : str + key for the tree dictionary + """ + + tree_scrollx = tk.Scrollbar(tree_frame, orient='horizontal') + tree_scrollx.pack(side=tk.BOTTOM, fill=tk.X) + tree_scrolly = tk.Scrollbar(tree_frame) + tree_scrolly.pack(side=tk.RIGHT, fill=tk.Y) + + self.tree = ttk.Treeview(tree_frame, + yscrollcommand=tree_scrolly.set, + xscrollcommand=tree_scrollx.set) + self.tree.pack(fill='both', expand=True) + + tree_scrollx.config(command=self.tree.xview) + tree_scrolly.config(command=self.tree.yview) + + self.tree['columns'] = columns_names + + # Format columns + self.tree.column("#0", width=20, + minwidth=0, stretch=tk.NO) + for n, cl in enumerate(columns_names): + self.tree.column( + cl, width=int(self.controller.pages_font.measure(str(cl)))+20, + minwidth=50, anchor=tk.CENTER) + # Headings + for cl in columns_names: + self.tree.heading(cl, text=cl, anchor=tk.CENTER) + self.tree.tag_configure('req', foreground='black', + background='#9fc5e8') + self.tree.tag_configure('opt', foreground='black', + background='#cfe2f3') + self.tree.tag_configure('type', foreground='black', + background='#E8E8E8') + self.tree.tag_configure('func', foreground='black', + background='#DFDFDF') + # Define double-click on row action + self.tree.bind("", self.OnDoubleClick) + + def OnDoubleClick(self, event): + """ Executed when a row of the treeview is double clicked. + Opens an entry box to edit a cell. """ + + self.treerow = self.tree.identify_row(event.y) + self.treecol = self.tree.identify_column(event.x) + tags = self.tree.item(self.treerow)["tags"] + if len(tags) > 0 and tags[0] in ['opt', 'req']: + # get column position info + x, y, width, height = self.tree.bbox(self.treerow, self.treecol) + + # y-axis offset + pady = height // 2 + # pady = 0 + + if hasattr(self, 'entry'): + self.entry.destroy() + + self.entry = tk.Entry(self.tree, justify='center') + + if int(self.treecol[1:]) > 0: + value = self.tree.item(self.treerow)['values'][int(str(self.treecol[1:]))-1] + value = str(value) if str(value) not in ['default', 'Choose X or Y'] else '' + self.entry.insert(0, value) + # self.entry['selectbackground'] = '#123456' + self.entry['exportselection'] = False + + self.entry.focus_force() + self.entry.bind("", self.on_return) + self.entry.bind("", lambda *ignore: self.entry.destroy()) + + self.entry.place(x=x, + y=y + pady, + anchor=tk.W, width=width) + + def on_return(self, event): + """ Executed when the entry is edited and pressed enter. + Saves the edited value""" + + val = self.tree.item(self.treerow)['values'] + val[int(self.treecol[1:])-1] = self.entry.get() + if self.entry.get() != '': + self.tree.item(self.treerow, values=tuple([val[0], val[1], self.entry.get()])) + elif val[2] == '': + self.tree.item(self.treerow, values=tuple([val[0], val[1], 'default'])) + else: + self.tree.item(self.treerow, values=val) + self.entry.destroy() + self.saved = False + + def fill_treeview(self, frame, req_settings, opt_settings, parent = ''): + """ Adds an entry for each setting. Displays it in the specified row. + :param req_settings: dict type of plugin required setting options + :param opt_settings: dict type of plugin optional setting options + :param parent: string type of parent name """ - r = 1 - options = self.s.loaded_modules[self.module]['plugin']['options'] - for arg, val in settings.items(): - tk.Label(frame, - text = arg).grid(row=r,column=0) + self.tree.insert(parent=parent, index='end', iid=parent+'_req', text='', + values=tuple(['Required settings', '', '']), tags=('type',)) + self.r+=1 + for arg, val in req_settings.items(): if arg == 'Data': - if self.m not in self.dataType: - self.dataType[self.m] = tk.StringVar() - self.dataType[self.m].set('X') - tk.Radiobutton(frame, text = 'Input (X)', fg = 'black', - var = self.dataType[self.m], value = 'X' - ).grid(row=r, column=1) - if len(self.s._get_all_elements_with_tag("Y")) > 0 or \ - len(self.s._get_all_elements_with_tag("Y_test")) > 0: - tk.Radiobutton(frame, text = 'Output (Y)', fg = 'black', - var = self.dataType[self.m], value = 'Y' - ).grid(row=r, column=2) + self.tree.insert(parent=parent+'_req', index='end', iid=str(self.r), text='', + values=tuple([arg, val, 'Choose X or Y']), tags=('req',)) else: - self.entry.append(tk.Entry(frame)) - value = options[arg] if arg in options else 'default' - self.entry[-1].insert(0, str(value)) - self.entry[-1].grid(row=r, column=1) - self.entry[-1].bind("", lambda event, a = len(self.entry): self.on_return_entry(a)) - r += 1 - frame.grid_rowconfigure(tuple(range(r)), weight=1) - frame.grid_columnconfigure(tuple(range(2)), weight=1) + self.tree.insert(parent=parent+'_req', index='end', iid=str(self.r), text='', + values=tuple([arg, val, '']), tags=('req',)) + self.r+=1 + self.tree.insert(parent=parent, index='end', iid=parent+'_opt', text='', + values=tuple(['Optional settings', '', '']), tags=('type',)) + self.r+=1 + for arg, val in opt_settings.items(): + self.tree.insert(parent=parent+'_opt', index='end', iid=str(self.r), text='', + values=tuple([arg, val, 'default']), tags=('opt',)) + self.r+=1 def removewindow(self): """ Stores settings options and closes window """ self.req_settings.pop("Data", None) - req_keys = list(self.req_settings.keys()) - opt_keys = list(self.opt_settings.keys()) - for e,ent in enumerate(self.entry): - if e < len(self.req_settings): - if len(ent.get()) == 0 or ent.get() == 'default': - self.req_settings.pop(req_keys[e], None) - else: - self.req_settings[req_keys[e]] = ent.get() - else: - if len(ent.get()) == 0 or ent.get() == 'default': - self.opt_settings.pop(opt_keys[e-len(req_keys)], None) - else: - self.opt_settings[opt_keys[e-len(req_keys)]] = ent.get() - if self.m in self.dataType: - self.req_settings['Data'] = self.dataType[self.m].get() + children = self.get_all_children() + for child in children: + tag = self.tree.item(child)["tags"][0] + if tag in ['req', 'opt']: + val = self.tree.item(child)["values"] + self.settingOptions(tag, val) self.newWindow.destroy() self.newWindow = None - self.s.update_plugin_options(self.module, - {**self.req_settings, **self.opt_settings}, - True) self.focus() + + def get_all_children(self, item=""): + """ Iterates over the treeview to get all childer """ + children = self.tree.get_children(item) + for child in children: + children += self.get_all_children(child) + return children + + def settingOptions(self, tag, val): + """ Identifies how the data should be stored """ + if val[0] == 'Data': + if val[2] == 'Choose X or Y' or len(val[2]) == 0: + self.updateSettings(tag, val[0], 'X') + else: + self.updateSettings(tag, val[0], val[2]) + else: + if val[2] == 'default' or len(str(val[2])) == 0: + self.updateSettings(tag, val[0]) + else: + self.updateSettings(tag, val[0], val[2]) + + def updateSettings(self, tag, key, value = None): + """ Return the selected settings + + Parameters + ---------- + tag : str + tag for the settings + """ + if tag == 'req': + if value is not None or self.req_settings[key] != value: + self.req_settings[key] = value + else: + self.req_settings.pop(key, None) + elif tag == 'opt': + if value is not None or self.opt_settings[key] != value: + self.opt_settings[key] = value + else: + self.opt_settings.pop(key, None) def on_return_entry(self, r): """ Changes focus to the next available entry. When no more, focuses @@ -392,6 +514,41 @@ def on_return_entry(self, r): else: self.finishButton.focus() + def isKey(self, d, k, key_list, cond_list): + """ Checks if the elements of a dictionary hava a specific key + : param d: dict type. + : param k: string type. + : param key_list: list type with keys that are not loop. + : param cond_list: list type with whether k is in a module in key_list. + """ + for key in [key for key, val in d.items() if type(val) == dict]: + if d[key]['class'] == 'loop': + key_list, cond_list = self.isKey(d[key],k,key_list,cond_list) + else: + if d[key]['class'].lower() == 'entry_point': + self.init_module = key + elif d[key]['class'].lower() == 'exit_point': + self.out_module = key + key_list.append(key) + cond_list.append(k in d[key].keys()) + return key_list, cond_list + + def allKeysinDict(self, d, k, out_list): + """ Stores the elements of a dictionary for a specific key + : param d: dict type. + : param k: string type. + : param out_list: list type to store the keys that fulfill the condition. + """ + for key in [key for key, val in d.items() if type(val) == dict]: + if d[key]['class'] == 'loop': + out_list = self.allKeysinDict(d[key],k,out_list) + else: + if k in d[key].keys(): + out_list.append(d[key][k]) + else: + out_list.append({}) + return out_list + def upload(self): """ Opens the XML file that was previously uploaded and places the modules, loops and connections in the canvas.""" @@ -400,39 +557,133 @@ def upload(self): self.reset() - self.s = XML_handler() - self.s.load_XML(filename) - # self.s._print_pretty(self.s.loaded_modules) - modules = self.s.loaded_modules - modout = modules['Output'] - del modules['Initialiser'], modules['Output'] # They are generated when resetting - self.disp_mod = ['Initialiser', 'Output'] - self.id_mod = [0, 1] - - # Place the modules - self.place_modules(modules) - connect = list(modout['coordinates'][2].keys()) - for p, parent in enumerate(modout['parents']): - parent_id = self.id_mod[np.where( - np.array(self.disp_mod) == parent)[0][0]] - out, ins = modout['coordinates'][2][connect[p]].split('-') - xout, yout, _, _ = self.canvas.coords(out[0]+str(parent_id)) - xins, yins, _, _ = self.canvas.coords(ins[0]+str(1)) - self.canvas.create_line( - xout + self.cr, - yout + self.cr, - xins + self.cr, - yins + self.cr, - fill="red", - arrow=tk.LAST, - tags=('o'+str(parent_id), - 'o'+str(1), modout['coordinates'][2][connect[p]])) - self.out_data.iloc[int(parent_id)][1] = 1 - self.connections[1][ - int(parent_id)] = out[0]+str(parent_id) + '-' + ins[0]+str(1) - self.m = self.id_mod[2] - x0, y0, x1, y1 = self.canvas.coords('p'+str(self.m)) - # self.select(x0, y0) + self.xml_handler = XML_handler() + self.xml_handler.load_XML(filename) + modules = self.xml_handler.loaded_modules + self.module_list, self.isCoords = self.isKey(modules, 'coordinates', [], []) + self.p_list = self.allKeysinDict(modules, 'plugin', []) + self.plugin_list = [] + for e in self.p_list: + if type(e) == dict and 'plugin_name' in e.keys(): + self.plugin_list.append(e['plugin_name']) + else: + self.plugin_list.append(0) + self.type_list = self.allKeysinDict(modules, 'module_type', []) + + if all(self.isCoords): + + # Place Initialiser and Output + self.add_module(self.init_module, self.width/2, self.h, ini=True) + self.add_module(self.out_module, self.width/2, self.height - self.h, out=True) + + modout = modules[self.out_module] + del modules[self.init_module], modules[self.out_module] # They are generated when resetting + self.disp_mod = [self.init_module, self.out_module] + self.id_mod = [0, 1] + # Reorder to have input and output in first two positions + self.plugin_list.insert(1, self.plugin_list.pop(len(self.plugin_list)-1)) + self.type_list.insert(1, self.type_list.pop(len(self.type_list)-1)) + + # Place the modules + self.place_modules(modules) + + #Output module + connect = list(modout['coordinates'][2].keys()) + for p, parent in enumerate(modout['parents']): + parent_id = self.id_mod[np.where( + np.array(self.disp_mod) == parent)[0][0]] + out, ins = modout['coordinates'][2][connect[p]].split('-') + xout, yout, _, _ = self.canvas.coords(out[0]+str(parent_id)) + xins, yins, _, _ = self.canvas.coords(ins[0]+str(1)) + self.canvas.create_line( + xout + self.cr, + yout + self.cr, + xins + self.cr, + yins + self.cr, + fill="red", + arrow=tk.LAST, + tags=('o'+str(parent_id), + 'o'+str(1), modout['coordinates'][2][connect[p]])) + self.out_data.iloc[int(parent_id)][1] = 1 + self.connections[1][ + int(parent_id)] = out[0]+str(parent_id) + '-' + ins[0]+str(1) + self.m = self.id_mod[2] + + else: # There are no coordinates for some modules. + self.my_label.config(text = " ".join(self.my_label.cget('text').split(' ')[:-1] + ['list.'])) + + self.update_output(modules) + self.id_mod = list(range(len(self.module_list))) + self.disp_mod = self.module_list + self.canvas.pack_forget() + frame_tree = tk.Frame(self.frame1, bg='green') + self.create_treeView(frame_tree, ['Module']) + self.r=0 + for i, module in enumerate(self.module_list): + self.tree.insert(parent='', index='end', iid=str(self.r), text='', + values=tuple([module]), tags=('odd' if i%2 == 0 else 'even',)) + self.r+=1 + self.tree.column( + 'Module', width=int(self.controller.pages_font.measure(str(max(self.module_list, key=len))))+20, + minwidth=50, anchor=tk.CENTER) + self.tree.tag_configure('odd', foreground='black', + background='#9fc5e8') + self.tree.tag_configure('even', foreground='black', + background='#cfe2f3') + + frame_tree.grid(column=0, row=1, sticky="nswe", pady=10, padx=10) + frame_tree.grid_rowconfigure(tuple(range(len(self.module_list))), weight=1) + frame_tree.grid_columnconfigure(tuple(range(2)), weight=1) + self.tree.bind('', self.on_click_noCanvas) + + def on_click_noCanvas(self, event): + """ Passes the mouse click coordinates to the select function when there is no Canvas.""" + self.click = True + self.select_noCanvas(event.x, event.y) + + def select_noCanvas(self, x: float, y:float): + """ + Selects the module at the mouse location and updates the associated + plugins as well as the colours. + Blue means no plugin has been specified, + Orange means the module is selected. + Green means the plugin for this module is already set. + :param x: float type of module x coordinate + :param y: float type of module y coordinate + """ + self.treerow = self.tree.identify_row(y) + self.treecol = self.tree.identify_column(x) + tags = self.tree.item(self.treerow)["tags"] + + if len(tags) > 0: + self.m = int(self.treerow) + if int(self.m) > 0 and int(self.m) < len(self.module_list)-1: + self.optionsWindow() + + def update_output(self, modules: dict): + """Update the output. + :param modules: dict type of modules in the pipeline. + """ + + for key in [key for key, val in modules.items() if type(val) == dict]: + if modules[key]['class'] == 'loop': + # Extracts numbers from string + l = int( + ''.join(map(str, list(filter(str.isdigit, modules[key]['name']))))) + self.loops.append({'type': modules[key]['type'], + 'condition': modules[key]['condition'], + 'mod': []}) + self.update_output(modules[key]) + else: + self.module_out(modules[key]['name']) + # Connect modules + for p, parent in enumerate(modules[key]['parents']): + if not (parent[:4] == 'loop'): + self.out_data.loc[parent].loc[modules[key]['name']] = 1 + # self.connections[int(self.id_mod[-1])][ + # int(parent_id)] = out[0]+str(parent_id) + '-' + ins[0]+str(self.id_mod[-1]) + else: + self.loops[-1]['mod'].append(key) def place_modules(self, modules: dict): """Places the modules in the dictionary in the canvas. @@ -521,11 +772,10 @@ def reset(self): self.out_data = pd.DataFrame() self.connections = {} self.modules = 0 - self.module_list = [] self.module_names = [] - self.add_module('Initialiser', self.width/2, self.h, ini=True) - self.add_module('Output', self.width/2, self.height - self.h, out=True) + # self.add_module('Initialiser', self.width/2, self.h, ini=True) + # self.add_module('Output', self.width/2, self.height - self.h, out=True) self.draw = False self.loops = [] @@ -672,4 +922,4 @@ def hide(self): if __name__ == "__main__": app = progressTracker() - app.mainloop() + app.mainloop() \ No newline at end of file diff --git a/aidesign/utils/plugins/startpage.py b/src/vai_lab/utils/plugins/startpage.py similarity index 99% rename from aidesign/utils/plugins/startpage.py rename to src/vai_lab/utils/plugins/startpage.py index c71f65e5..21feb79b 100644 --- a/aidesign/utils/plugins/startpage.py +++ b/src/vai_lab/utils/plugins/startpage.py @@ -1,4 +1,4 @@ -from aidesign._import_helper import get_lib_parent_dir +from vai_lab._import_helper import get_lib_parent_dir import tkinter as tk import os diff --git a/aidesign/utils/resources/Assets/ADDIcon.ico b/src/vai_lab/utils/resources/Assets/ADDIcon.ico similarity index 100% rename from aidesign/utils/resources/Assets/ADDIcon.ico rename to src/vai_lab/utils/resources/Assets/ADDIcon.ico diff --git a/aidesign/utils/resources/Assets/ADDIcon_name.png b/src/vai_lab/utils/resources/Assets/ADDIcon_name.png similarity index 100% rename from aidesign/utils/resources/Assets/ADDIcon_name.png rename to src/vai_lab/utils/resources/Assets/ADDIcon_name.png diff --git a/aidesign/utils/resources/Assets/AIDIcon.ico b/src/vai_lab/utils/resources/Assets/AIDIcon.ico similarity index 100% rename from aidesign/utils/resources/Assets/AIDIcon.ico rename to src/vai_lab/utils/resources/Assets/AIDIcon.ico diff --git a/aidesign/utils/resources/Assets/AIDIcon_name.png b/src/vai_lab/utils/resources/Assets/AIDIcon_name.png similarity index 100% rename from aidesign/utils/resources/Assets/AIDIcon_name.png rename to src/vai_lab/utils/resources/Assets/AIDIcon_name.png diff --git a/aidesign/utils/resources/Assets/AIFRED.png b/src/vai_lab/utils/resources/Assets/AIFRED.png similarity index 100% rename from aidesign/utils/resources/Assets/AIFRED.png rename to src/vai_lab/utils/resources/Assets/AIFRED.png diff --git a/aidesign/utils/resources/Assets/Hospital.xml b/src/vai_lab/utils/resources/Assets/Hospital.xml similarity index 100% rename from aidesign/utils/resources/Assets/Hospital.xml rename to src/vai_lab/utils/resources/Assets/Hospital.xml diff --git a/aidesign/utils/resources/Assets/UFAIcon.ico b/src/vai_lab/utils/resources/Assets/UFAIcon.ico similarity index 100% rename from aidesign/utils/resources/Assets/UFAIcon.ico rename to src/vai_lab/utils/resources/Assets/UFAIcon.ico diff --git a/aidesign/utils/resources/Assets/UFAIcon_name.png b/src/vai_lab/utils/resources/Assets/UFAIcon_name.png similarity index 100% rename from aidesign/utils/resources/Assets/UFAIcon_name.png rename to src/vai_lab/utils/resources/Assets/UFAIcon_name.png diff --git a/aidesign/utils/resources/Assets/VAIL.ico b/src/vai_lab/utils/resources/Assets/VAIL.ico similarity index 100% rename from aidesign/utils/resources/Assets/VAIL.ico rename to src/vai_lab/utils/resources/Assets/VAIL.ico diff --git a/aidesign/utils/resources/Assets/VAILabs.png b/src/vai_lab/utils/resources/Assets/VAILabs.png similarity index 100% rename from aidesign/utils/resources/Assets/VAILabs.png rename to src/vai_lab/utils/resources/Assets/VAILabs.png diff --git a/aidesign/utils/resources/Assets/VAILabsIcon.ico b/src/vai_lab/utils/resources/Assets/VAILabsIcon.ico similarity index 100% rename from aidesign/utils/resources/Assets/VAILabsIcon.ico rename to src/vai_lab/utils/resources/Assets/VAILabsIcon.ico diff --git a/aidesign/utils/resources/Assets/back_arrow.png b/src/vai_lab/utils/resources/Assets/back_arrow.png similarity index 100% rename from aidesign/utils/resources/Assets/back_arrow.png rename to src/vai_lab/utils/resources/Assets/back_arrow.png diff --git a/aidesign/utils/resources/Assets/forw_arrow.png b/src/vai_lab/utils/resources/Assets/forw_arrow.png similarity index 100% rename from aidesign/utils/resources/Assets/forw_arrow.png rename to src/vai_lab/utils/resources/Assets/forw_arrow.png diff --git a/aidesign/utils/resources/example_radiography_images/00028173_005.png b/src/vai_lab/utils/resources/example_radiography_images/00028173_005.png similarity index 100% rename from aidesign/utils/resources/example_radiography_images/00028173_005.png rename to src/vai_lab/utils/resources/example_radiography_images/00028173_005.png diff --git a/aidesign/utils/resources/example_radiography_images/00028179_000.png b/src/vai_lab/utils/resources/example_radiography_images/00028179_000.png similarity index 100% rename from aidesign/utils/resources/example_radiography_images/00028179_000.png rename to src/vai_lab/utils/resources/example_radiography_images/00028179_000.png diff --git a/aidesign/utils/resources/example_radiography_images/00028198_002.png b/src/vai_lab/utils/resources/example_radiography_images/00028198_002.png similarity index 100% rename from aidesign/utils/resources/example_radiography_images/00028198_002.png rename to src/vai_lab/utils/resources/example_radiography_images/00028198_002.png diff --git a/aidesign/utils/resources/example_radiography_images/00028208_035.png b/src/vai_lab/utils/resources/example_radiography_images/00028208_035.png similarity index 100% rename from aidesign/utils/resources/example_radiography_images/00028208_035.png rename to src/vai_lab/utils/resources/example_radiography_images/00028208_035.png diff --git a/aidesign/utils/resources/example_radiography_images/00028211_021.png b/src/vai_lab/utils/resources/example_radiography_images/00028211_021.png similarity index 100% rename from aidesign/utils/resources/example_radiography_images/00028211_021.png rename to src/vai_lab/utils/resources/example_radiography_images/00028211_021.png diff --git a/aidesign/utils/resources/example_radiography_images/00028253_000.png b/src/vai_lab/utils/resources/example_radiography_images/00028253_000.png similarity index 100% rename from aidesign/utils/resources/example_radiography_images/00028253_000.png rename to src/vai_lab/utils/resources/example_radiography_images/00028253_000.png diff --git a/aidesign/utils/resources/example_radiography_images/00028279_000.png b/src/vai_lab/utils/resources/example_radiography_images/00028279_000.png similarity index 100% rename from aidesign/utils/resources/example_radiography_images/00028279_000.png rename to src/vai_lab/utils/resources/example_radiography_images/00028279_000.png diff --git a/src/vai_lab/utils/tests/__init__.py b/src/vai_lab/utils/tests/__init__.py new file mode 100644 index 00000000..e69de29b From 7ae51a9c27708171bad495061dcd8866f11ddefa Mon Sep 17 00:00:00 2001 From: Carlos Sevilla Salcedo Date: Wed, 10 May 2023 14:15:23 +0200 Subject: [PATCH 5/9] Fix issue with example --- src/vai_lab/examples/optimisation/X.csv | 29 +++++++++++++++++++ src/vai_lab/examples/optimisation/Y.csv | 29 +++++++++++++++++++ .../examples/xml_files/optimisation_demo.xml | 2 +- src/vai_lab/run_pipeline.py | 7 +++-- 4 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 src/vai_lab/examples/optimisation/X.csv create mode 100644 src/vai_lab/examples/optimisation/Y.csv diff --git a/src/vai_lab/examples/optimisation/X.csv b/src/vai_lab/examples/optimisation/X.csv new file mode 100644 index 00000000..94e55801 --- /dev/null +++ b/src/vai_lab/examples/optimisation/X.csv @@ -0,0 +1,29 @@ +CsPbI,MAPbI,FAPbI +0.75,0.25,0.0 +0.0,0.0,1.0 +0.5,0.5,0.0 +1.0,0.0,0.0 +0.5,0.0,0.5 +0.0,0.0,1.0 +0.5,0.5,0.0 +1.0,0.0,0.0 +0.0,0.25,0.75 +0.0,0.25,0.75 +0.25,0.0,0.75 +0.75,0.0,0.25 +0.0,0.5,0.5 +0.0,0.5,0.5 +0.25,0.25,0.5 +0.75,0.25,0.0 +0.0,0.75,0.25 +0.0,0.75,0.25 +0.25,0.5,0.25 +0.5,0.0,0.5 +0.25,0.25,0.5 +0.0,1.0,0.0 +0.25,0.75,0.0 +0.5,0.25,0.25 +0.25,0.5,0.25 +0.0,1.0,0.0 +0.75,0.0,0.25 +0.5,0.25,0.25 diff --git a/src/vai_lab/examples/optimisation/Y.csv b/src/vai_lab/examples/optimisation/Y.csv new file mode 100644 index 00000000..d9c602b5 --- /dev/null +++ b/src/vai_lab/examples/optimisation/Y.csv @@ -0,0 +1,29 @@ +merit +102012.10929790093 +480185.0038224852 +1167260.6222070677 +144556.1215368709 +239851.85597959475 +505656.58303249744 +1182235.6440845595 +351227.01607136783 +914155.5189523202 +981850.240125919 +144073.96913849353 +273687.1193117654 +1103449.8761320617 +1155542.3150353911 +284710.437804268 +128970.90311931062 +1300295.164428879 +1281399.3586171553 +446746.6993871238 +416656.76497605036 +602576.1724037583 +1267615.3317977716 +876981.2028616397 +925192.9561762783 +533060.412747728 +1274992.873707038 +253423.44691605103 +714734.685825648 diff --git a/src/vai_lab/examples/xml_files/optimisation_demo.xml b/src/vai_lab/examples/xml_files/optimisation_demo.xml index cb1b578d..048c4a23 100644 --- a/src/vai_lab/examples/xml_files/optimisation_demo.xml +++ b/src/vai_lab/examples/xml_files/optimisation_demo.xml @@ -19,7 +19,7 @@ [(350.0,350.0),2,{0:'d0-u2'}] - + None diff --git a/src/vai_lab/run_pipeline.py b/src/vai_lab/run_pipeline.py index 6a9cba4a..a3d5218d 100644 --- a/src/vai_lab/run_pipeline.py +++ b/src/vai_lab/run_pipeline.py @@ -57,9 +57,10 @@ def main(): for i in range(0,len(args.file)): args.file[i] = abspath(args.file[i]) core.load_config_file(args.file) - # core.load_config_file(("./examples", - # "xml_files", - # 'bayes_opt_demo.xml')) + core.load_config_file(("./examples", + "xml_files", + # 'bayes_opt_demo.xml')) + 'optimisation_demo.xml')) # Run pipeline core.run() From 44f56c1dd4b9810351940f7b390f031808b85e6f Mon Sep 17 00:00:00 2001 From: Carlos Sevilla Salcedo Date: Thu, 11 May 2023 16:16:30 +0200 Subject: [PATCH 6/9] Minor changes --- src/vai_lab/DataProcessing/plugins/argopt.py | 17 ------ .../plugins/OptimisationInput.py | 2 +- .../xml_files/materialdesign_demo.xml | 59 +++++++++++++++++++ src/vai_lab/run_pipeline.py | 8 +-- 4 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 src/vai_lab/examples/xml_files/materialdesign_demo.xml diff --git a/src/vai_lab/DataProcessing/plugins/argopt.py b/src/vai_lab/DataProcessing/plugins/argopt.py index 4cf7219e..a8ee1cfe 100644 --- a/src/vai_lab/DataProcessing/plugins/argopt.py +++ b/src/vai_lab/DataProcessing/plugins/argopt.py @@ -22,26 +22,9 @@ def __init__(self): Passes `globals` dict of all current variables """ super().__init__(globals()) - # self.proc = model() - - def configure(self, config: dict): - """Sets and parses plugin configurations options - :param config: dict of internal tags set in the XML config file - """ - # super().configure(config) - self.config = config - - def set_data_in(self, data_in): - """Sets and parses incoming data - :param data_in: saves data as class variable - expected type: aidesign.Data.Data_core.Data - """ - super().set_data_in(data_in) def fit(self): self.cleaned_options = self._clean_solver_options() - # self.proc.set_params(**cleaned_options) - # self.proc.fit(self.X) return def transform(self,data): diff --git a/src/vai_lab/UserInteraction/plugins/OptimisationInput.py b/src/vai_lab/UserInteraction/plugins/OptimisationInput.py index d0027488..7d54b562 100644 --- a/src/vai_lab/UserInteraction/plugins/OptimisationInput.py +++ b/src/vai_lab/UserInteraction/plugins/OptimisationInput.py @@ -20,7 +20,7 @@ _PLUGIN_MODULE_OPTIONS = {"layer_priority": 2, "required_children": None} # type:ignore _PLUGIN_REQUIRED_SETTINGS = {} # type:ignore -_PLUGIN_OPTIONAL_SETTINGS = {"Bounds": "list"} # type:ignore +_PLUGIN_OPTIONAL_SETTINGS = {"Bounds": "list"} # type:ignore _PLUGIN_REQUIRED_DATA = {"X"} # type:ignore diff --git a/src/vai_lab/examples/xml_files/materialdesign_demo.xml b/src/vai_lab/examples/xml_files/materialdesign_demo.xml new file mode 100644 index 00000000..16b994b9 --- /dev/null +++ b/src/vai_lab/examples/xml_files/materialdesign_demo.xml @@ -0,0 +1,59 @@ + + + + + + + [(350.0,50),0,{}] + + + + + + + + + + [(249, 180), 2, {0: 'd0-u2'}] + + + + + + + + + + [(447, 179), 3, {2: 'r2-l3'}] + + + + + + + + + + [(350.0, 350.0), 4, {3: 'd3-u4'}] + + + + + + + + + + [(350, 501), 5, {4: 'd4-u5'}] + + + + + + + + + [(350.0, 650), 1, {5: 'd5-u1'}] + + + diff --git a/src/vai_lab/run_pipeline.py b/src/vai_lab/run_pipeline.py index a3d5218d..c997a8d1 100644 --- a/src/vai_lab/run_pipeline.py +++ b/src/vai_lab/run_pipeline.py @@ -57,10 +57,10 @@ def main(): for i in range(0,len(args.file)): args.file[i] = abspath(args.file[i]) core.load_config_file(args.file) - core.load_config_file(("./examples", - "xml_files", - # 'bayes_opt_demo.xml')) - 'optimisation_demo.xml')) + # core.load_config_file(("./examples", + # "xml_files", + # # 'bayes_opt_demo.xml')) + # 'optimisation_demo.xml')) # Run pipeline core.run() From 9424218e8a6a3bbded5c3d6e6eaa3fad03aff61c Mon Sep 17 00:00:00 2001 From: Carlos Sevilla Salcedo Date: Fri, 19 May 2023 09:17:38 +0200 Subject: [PATCH 7/9] Recovered image processing files --- .../DataProcessing/plugins/RGBcalculation.py | Bin 0 -> 5048 bytes .../DataProcessing/plugins/RectangleDetection.py | Bin 0 -> 9542 bytes .../DataProcessing/plugins/imgtofeatvect.py | Bin 0 -> 6106 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/vai_lab/DataProcessing/plugins/RGBcalculation.py create mode 100644 src/vai_lab/DataProcessing/plugins/RectangleDetection.py create mode 100644 src/vai_lab/DataProcessing/plugins/imgtofeatvect.py diff --git a/src/vai_lab/DataProcessing/plugins/RGBcalculation.py b/src/vai_lab/DataProcessing/plugins/RGBcalculation.py new file mode 100644 index 0000000000000000000000000000000000000000..b69481619b200ae13de07601d172817a4f64a20e GIT binary patch literal 5048 zcmb_g+iu%N5S`}${RhH%h#WNh00SwI)J|X^w$oSv+$TnsWmO18DI}BD2=eP~&)MO4 zm&>If*M&e)Mq);K>s5mbg>ls>LdCl#tBdl-@^n>{e#L zO$+9359L*mG2KI7D|e8;OY$jXQFjgGGpAu_+ZHPoY*G95dyDJ7y^VZ^B@67Tu{)P* zw|_CP>2-Ji1opPhURqCI^yQ`a3)v~;4`?f$ef2;Kz1f1}5*n@@^Tf4q))v6N8t9Ah zubqS%JLd_{aFwhSSo;E>s`MLNlS_Jwn3StE_GoO;!UEoWCm-c4;_e(*ALK+{$uW-C zxPO8>=lFewUzd>Z632Hrz^=DgpF?7a@2~hKYb)rfuzG}pu|^+qt;O{ZJekQ;92Nyf z*w08LHpLTTIg<+@`PkvO0MaQ?UI0;uW!gm*^=8m?0S)v;eXo_8YO|LLOd$n{h14d2IRa>&NoK$eH4>Sotp)4B^AD40(yM z9LN=7;%6Mccjqgx*SgH-{Y;)=#qK=oVph@Y#==aRyDVs2cJ9iL31XVrGFC?q5yzBg zEQG%`t1@6LkUbR+R+T~~E~iUmT>P|2Ie^6nh+lnX1?{XwtOTs{tf^LyvcQrYYoUIV zX6N^KjuxwwPsryI`OI2d;_fFr&Du5x$_)2!@y(SIXI2eXCar;)d~*nh>l*8nm(r_N zq*fZpV{fU!#i8qNF++W9+;y1LL2Jll4td(=G?v$}fc%$E12Kd+%rkTBs_-;x8!cP} zzF~xg>#SGC&u+h6LJs4U{IU0$$Bg-~llOHHgRjk$uz_7xtJ5qWb@4F6JkXaGA~ugQ z4z*`j&_v%dwwDeq@zM9(!~3ku8M5#Ub*nEqbDYuh>4@9k^C83Lf$*MsJ>@$^%YLl+ zsS)IJi~j4cJb2{7Lie9P*PqHGV81S;J9_5gu^Q6{o=4k`u&++lvQt0;A5VMgZ zYsb0cgu}|zAd5NAtpgsztYMs5EU*{#XZ=I0zK~yb?&Fk{2tCOyt0W&<-R?cGqaj~27p`^?9w*R(K~z-*8eQ>WDN_MF)KJb62HFLvLdFZkr${5 z@h2tk3OHC_zB{>;e23>a<$eZU^3fpMsgoRK4lx;Ap_lP3MeS)Yqp*f>#{_4tGG`kn zA->i*KOMhj=d_!8JV!p7Gx-x5$hV)4Jlo6@tEYa3OEdfdC@ov|;W5Ra`J4X&@crKS z)O;m_w_sueep)b=l3+HU0S)_sw}n$jKXNR}D1D2V(m8Q=9F0!{L+mf1osx4GVMH^z Q?_q}1&i%ZLWzo`q0l>%U5C8xG literal 0 HcmV?d00001 diff --git a/src/vai_lab/DataProcessing/plugins/RectangleDetection.py b/src/vai_lab/DataProcessing/plugins/RectangleDetection.py new file mode 100644 index 0000000000000000000000000000000000000000..8ccd7a28d43d4fe23321870690faa94150dc2bb3 GIT binary patch literal 9542 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 Date: Mon, 22 May 2023 09:57:14 +0200 Subject: [PATCH 8/9] Added image processing plugins --- .../DataProcessing/plugins/RGBcalculation.py | Bin 5048 -> 4712 bytes .../plugins/RectangleDetection.py | Bin 9542 -> 9502 bytes .../DataProcessing/plugins/imgtofeatvect.py | Bin 6106 -> 3488 bytes src/vai_lab/run_pipeline.py | 8 ++++---- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vai_lab/DataProcessing/plugins/RGBcalculation.py b/src/vai_lab/DataProcessing/plugins/RGBcalculation.py index b69481619b200ae13de07601d172817a4f64a20e..daaf9b17cd12f1e518cbcc41876e3324c4ad62c0 100644 GIT binary patch delta 209 zcmdm?{z8T6|G$X_8k6G~qj<|05*acX;u&&)bkfB3@X3pqB{nZ%GGXOZW>8>AVMt|2 zo1DlYJoy8=1*7uh+nk1z!#Lc4EPXD?$(uOpfGk~3Ss-c8Ej>Aj(+J3#$mt6tZ*ztN zNqw$-AX&(wJ^2_{5|9N`8w_Okb87?1?cBvcvXe)CvKtS`+)f@>An7QeJ^279$K-!J i0h1m0942@1f^@&+^#_use20PLS3X@v<<0W^Gg$zMvpX;V delta 549 zcmZ8dJxIe~5Iy>rq-i1qDs?CZX*D{Ch(iY{PGUh&w1``5O=}CbjcEnl92{NZiHJDp z?owPF9G%>Ba@DDmlQ>EBzONw|m%DrK-uLeD^)mMpGe7+*IM9Ly=&%X}Sf*YlWE1B= zhfsk~33i|Y3dF$;8R_7@1#Q0Ze6S=X-Lsx6!Cvt7PJEUedV|Xxg4_IN zFb~_ZL?-R_VBfQ_2(y#yzu4hH&IW1t-{4I++5OH%1&eSMbbJ<)Sd7i#^pJ{|u_|>D z6T%6{*U=2V2(!NSO&UQ%obt3f9*g6iUd2aogU?%G9T%mt|NdRd;f0#Qhe!(7;x(@6 yM>TwnFZpv`HtchcHI4ZeGPUM-BjbAPJiQ delta 62 zcmbQ|bZ3m6g^@)%NpWHEyRlq>-9c^SASJ2Fddc4Bm3o%}$7WwH*l K)aFHOHF5x5HxX$7 diff --git a/src/vai_lab/DataProcessing/plugins/imgtofeatvect.py b/src/vai_lab/DataProcessing/plugins/imgtofeatvect.py index d56e07c4aca2df7ae47e7cf5385d1af9fdd63792..2a4f4ddd81f37b923ec0f545be156d54efe8292f 100644 GIT binary patch delta 141 zcmcbmzd)Ml|G&v`j8VL042cYx4Dk#(Kssq+d-!A%My-hk8j~+ESxv5BmYb}_>^GSQ zNKa!Hnam_2GdX}+X7Va#m&wam^(JeuWK6!mG7X6JCO5DPO@78224p)--opl>L1Iqa kN(_-e(@PkN8A>MGvq}TS<{9 literal 6106 zcmc(j+iu%N5Qg`9PLB(Pl=i0w3X3Kv13$g(24(ZQAIByNybZ~K2c znsP}lRS7{;2o%XZ%*;P$?q7et4T~@j)6fae!*O_~`>o#YG`0wZ-j4M)2;(pc--d5$ zbF*+C*FI?7AT0Gi)6V9)x#k!xc@0GAS~QoskF~28`r0`Wy|G4SdMh-G91~II8R}KZ z4&ux}a)V$P*FbM38W$VzE5d{5ucLe{TJT;-@YFmrafOwcgPs)$gLNU;Hdo$I(L$HP>ePZ27k%w0-5PnJ#KrKkSGnweD@G z?{+F(X^#A9dv3*@TEZ@*rGk6)>m)dOnoIdRHNQXCKJXfqH$qHJqOwmk`bN8{q|}d2 ztSQv0iGJ`PYnMFVM+uPYO7qam@ii2=nWgB_$t+04pbAXOQmh7Vd*?I%Js9}Zs+2QzoS1oEw`9!KJ6Q;Jdgoz zyNz-|VJ=+ddy2uWy0`L&fqb}1EtS|UiXU2sI1?vqi*Jvkw8)3;S;IJ$u%l=`QBJN> z?nIqo`Mk$%ZuwNhZh>^nte)a_+nKaw=h=22g^w}Xi0o1H$Fh%tXZ_3OV5QOCKe?|TkrV&r zSWc47?cQcR*M(`S-TK6Au|Xz9m)6^ZykCna%N58%Hs+hIT+g?_T;o`2(b!2w%4wja z<@u;5I`;Kqd7C(0O7idZE4k&Q+nhsP8$CsByYA=%$xKUOrh}d)dL-+)4e(R<1!__C z%W_wdQLN zlf3OXO+#%}uE-kJ8zj% zANAdq6Y8U>j?-tCupWWPMGWS-Tb?|S_SlF2;8R|4?Ud?DqntYMb#L{;PM2(LwMHX_ zpG?8oC~+!+-_YlB+C-!f#i{)6AKs~?JE8c8_Vltla#ZLri8(uMLH>Ma_*j{v{>#z3 zl>B6&Px3A6Oh0F3aTI)8`AX}dkcZPg>nZ4U&>R;PoD(QJD@6aMQ>w*Ky-#PQE%xU% z@Ioux`!~UvL+E~JIO)cJ6ICj8Znfh)lB|V(R?W-U%FDT~Dtm?_e)n}zEy=l;!u(Fp z53*o;_kE+2o)5YlMNiru3xmi3cZ z{WOx^sD6IPYN?u)d}pf9^w}eLT{c`F%~S_F`E%TN!gDP|_}fr=+zGFw(O1GY39mJW zgeBgzdR=F Date: Mon, 22 May 2023 12:10:16 +0200 Subject: [PATCH 9/9] Fixed issue reading plugins --- .../DataProcessing/plugins/RGBcalculation.py | Bin 4712 -> 2288 bytes .../plugins/RectangleDetection.py | Bin 9502 -> 4626 bytes .../DataProcessing/plugins/imgtofeatvect.py | Bin 3488 -> 1691 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/vai_lab/DataProcessing/plugins/RGBcalculation.py b/src/vai_lab/DataProcessing/plugins/RGBcalculation.py index daaf9b17cd12f1e518cbcc41876e3324c4ad62c0..4868fb80deba39ced8d220e3cce75bc40b09389f 100644 GIT binary patch literal 2288 zcma)8+iu%95PkPo43vk;g`*D;&;m~F4GbiX*Kq*%No6F;AxcpKNqG|#`S;F{)U8dM zB@mesXU?3tGNNi~Z9%nF^8(C(YG$RbD=^e;ivXflQ5qGzWUaK(E5HTVmxia}q-xE3klVY}147*G4x78J2RWcvltOf8pSUK? zVZDv9U;q2C*{@gG{rYg&Y_B~G@g^8!{}+g7u)LXoOpFgSaQ6BVmk$LyP3_+F&C2W?(hRZS> zl0Xzi8$1w93WEjFFwK<(XCd+0Bt>h7Mp5G1N!E`{nr~2u+(KOflM?b)JH<28f;~!Z zoMBQ!1O48#4cGBJ>B$7Fcs?IwkYCbZF=A}1y5KUN&)ne!kRN%iN^xp6caAK=d+Nvp zw=T#m*}do108<5d<(zZn-$S?pHc&TI$eyrQO;Mgz6KL{ND9g2C5-d9zFx=u7zygNj z?M)DdQb_K@*0TuYiBsqQ;Dp~zJVfBEz{19a1s}uPd+rI9*A@0Jko?GMa3`RoVAvV+ zNUttcD@#4=7&n+)}j{_40H`wXP3W3}s%dzX-ZiLdM--n|Mz}UyYYZ8M} z*m0qHrnx;w<8A@0FyS5gVtb;&jH$B)0@y{*wPNsbeO zRx@SFT34eKr9h$BP?eM{=X1FHJpJNZ(sLrO6V^0b6+YW}E!#@PGzg$6Tp0hsD)e>k z8X_5tlJ-0_jmNtHQTlke2?7FdiQ2-pR(x#sJ>z&cxbgEY0~dN{YRaS0*e0>#H_CwK zw$;iPW*A?-wc;?bzF@#^_F-%Fad_abdjPK?Dgne?X9b=)WI^BpHxb9sVe&g4Z zBY!jU7dc`Y7f{NY+1LJ@tls_>6N;2jO^fqz8k(;6Nv((eg#W!H^cG`B(Fv`PtWM}B DfIpm0 literal 4712 zcmb_g+fLg+5S`~r{RfLYkW|GVfYeG*C_-uy3PGYzA%s995+{li3PSvP+jC|;c)hXR zrc{x!y|X)W_RM9+{Q5bTsmx_23t37eiLCVf5zhljb!0AY<&E56#tgCuqjxfq5#$TZ zkL5~djvy)O+Rw%hv0|yR0q(R^)OU}0W9dWZ0J>s4&GEdFTZ|@nTf!DGQqE_hDq1>F z$v{pN<51_My82dnYK<2AkbjYLIg(@P;O$fnWls)p?PL59BON@q@pJ|YA8?YB@j}%Xcr7c|`InQu( zfwTvdT_EzY^fG<48^KZ+7QRFO;>9X0hri8`kd?1Ma;*FufR!_h^b}7AT=;m5cf&(J z|4%GF4}0LI8jIsR+}7UDO-I@TR#?pRl`a?J6*fQAIJ$lFPbH{ZT#v-=6wamTLyTq2(*$Y<8R z2}Uom`WjY-s&xi*te+G7Xk!J7xlPu)h2pw`ochY_x?Pk>Qs`q3Fa%!V@c{cGh;u%O! zv)T{90__naql$0#U9Ujy&Da zeemt_)rx5T!9v^i&ZKqbvpbdvHo$nAG|izjlS0SY^-b zR^3PPpc%>RA8D-SCqM0uF-A153y5E;V*b=u2ADtjSQVPh;-Si;#*6cCeN-^NmtJPq z`JDW+H%yd?evo^vz7P{DID5T(ejg0YFiy`cASq@ot5_8yxjw67F?2Bp*-Nt8GN)7Q z;2nOsMfxhVvze8UnHX7K&m}t~vXf%}?1iTuuYPthjt?-CRfT7nt2OL1+26Jx-I0&A zbBbqc19Rt{Xlkv;enmYXgQ)k49x3$HQPLs zs<|fGvDVq#w|NIc8FQN*HS25#cg9=wj$gZb_Z7na)^$&>=b>z0^_(!a-Ko=r7hGgi zGss%9)H9i9kj-ZX@$##nf7(}nvxPZtC&uRenUBoRX#d%?hxx?}VTKhwZE@{ucBe3_ zjP2QGneMXaG_Ta#aTPd{Pw;~ORp?F}JMX9P05d(xz9A!2HqKvR+`0%S=hrNI2UdTd z1w&jm#(vb*HTszqv%HVoL7fPHW%@3Fi8bd*^-}XA*0FQGgLd+fX0?%nzJDS92&6wYYkPAO}Bsv5#~_w7)OVcWCads+9CbCgGOBtKw*eCJ(O&Ng$!YAmt3 zez2V3Q=qhL`5PX~7&L$LKMVYyf$`~bmJHs2iJ9{DT6gd`U)ILW{xqh=g ij@CN|v$&3(-vQJd!_y8;^W|)_Rdz4toy|8M$klJd!J)4J diff --git a/src/vai_lab/DataProcessing/plugins/RectangleDetection.py b/src/vai_lab/DataProcessing/plugins/RectangleDetection.py index 4e4ff4d2898ed7f38f8757b43f66be035522ce5c..e9fea98f74eb5a30b394fee64d6929d8cb651eb2 100644 GIT binary patch literal 4626 zcmd57Je^PP+2y2zDb*=AkbF|Juwbml5wGyORgTxH>mYr&)=dX4L2ZRDDp zTsg8EE~Avs!l+0$3zP%{#rI2qVq{qx%A%=31T8kCy*zRhGEeY80^1s;S8ydV!>nOpJF6G46%?NqX+Gnr{=^a)8RsNEHCV@*Fmg9(DQXmH2Ddn3zhMd8NSd= z3qt`%MY8F;Q%}t1G8Hatl@$%arT?`P?yBV>1V;&K6bbV_`nw;wC$l(Tqf;`XZ)o-I zcqv1s`3>?&x70Zu2FtuP*eDXskGL(+&;UK#v>2C(&}d!Z7=r9q6r+vVtA>;^{Vp73 z0Gd8}yIL`rOVb3ZV~#OaGLb|+VZevQN@UL?ou@ZKl@9?LvfXPfAr+Edn_S)B-@hlX z$9fb0ekkO*wWW7a0ObeKec}J$BI-^9Qh}_U}4!Z9>amz76h2GooELL~g@N{f* zw%7Ev$9fXLMl}FBIb!I|LKk5sC*2n0wymI?@TU~Na|K?i@3DEVT<4*p(AWz^(uY&% zDu&GUo(@_V?vW@spB15%**~Gy73PJ=h_?=i=MLE36MSBVMYh-*_?}UkQ1y+FzyopCIQy z$zL7%A7znRN5~7{C2=h?MqRG6K8<2lB(@j5K`VE|!cLX2mQLJl6e(dgDs%nQ4`(-m zTI;LF@Pnxu_))^s(Hjg%;pwP=Ex=sg!u&wF+nK{tTj}Zl8Y4g9AZ(q9#CTr+3fjhPdxkkH{W@%k49kU1Ef`?SM zcDmPglY}cD0(4PUPqUoO$R70>qs_|TSgsliRxLxp87u*nb0qVu3b4wz+6nL$)r|3X z869XJ<6VN+F1anW&J~8_EtaNMwB4b|lWt{#`BDj4>YZ}`$d&w(XP6X}sTd=nX1O7j z`~LqWl#%f+atF>GEEkkvkKK@A`%Gf{+DCXr9`G(`rwQH4ECYJ0UAMO7(pKsN=vD4f z!9jICTVZFZPAOq;q@g9o)^#nf?AAnKip>l?{eCYO2$^Bzs7U?Up3UDzRVG z$LbbK&(O^p{b`Er1+NYC=2cmSJt<)PmI8wJ_)u?V^jfd&Jkd*AEOq ZJ`fGIx(?-9FY|4`5urL29s}-K=WpTp2dMx6 literal 9502 zcmeI2ZEqXL5y$s+fqn;qdSTkJ2s=rUHmC~3kewKaE!S2G6+xiT6h%_i13^-j6C+J<4W&38vR!~Pb0mbYkohyw3#ElsnU9|8h@x2OM5ra6;4%puQYF(jzx1IT7BKk zbbp=38okuhQoO*(d+tG1<+N{a2I-k$9NL_PtsbXC<5r!2Ve_GQC5q>o3Ex@z!MHFM z(lcji6u^8e4qvBl(wFIRda9@A>0$aJ?d$tM;}13RRQFGG_d*o*^nH`Mnl(y8Q5i^@ zvFMGoVx(suoeIOHW__;jiN;Pf@7lO_(_Q1-5uNw_WUkH&`WTOh!we8)e z@vih=mlc`TWvyfR=`Ft<-7U=$BVnFOW0&SLBhy9Q0#DL`wE4|S`v0y|MSJrWZAtm}@fUK+p}vtt zw&S|VgL(I%{!&NPO-H8dZu;lydL*w}TE52yjOXVz88)NBihPH%E_sXWzfDH%DKFp) zZB^m6{JZp!uF!TXBXBtpxAmOXu}odfYSg4l%_hrxeHtitdDaTMtn=`U-;tJ%ZT(b; zx$jBNkt9A%b(~dNkHw8OQ}Lyau?h{%^bBrdw^Qh@ucuP#pcdC>T8ECE%J){UuB^hJ zYxH}qJ`oqHSx2#Us=x3(*LA3>H7|Hl)#rxmrQSoYiSN2b0?9)3W?DCwE^b>_P}dN( zG-G4d?M5D+PlD`lw;>G<2Z*$9q1JI{={PlNANC_v*qTmNnT&3DJ90pcf*wV_$h45hc!{h`gGp%=ICX0IT=mqlc6 zvN`%?2Xv{wC(75=*z^1XCNelXnmqql8oJUrmYc7wB#X6aB-4vL))y^ekR21*mY7~h zf;;+Tj!g*Lxop-uHaN7N`@s6cL-CLOBM8_xx_sBd<+-V&oJk#;TK*soW*Q-Lgmoep zv+wbJBtA{FlsWfAnp{U0-rA-$5T6JWT4EPSyx%2c|*men0xNuj*IC2FsQ#CN&G(5hh z6Lo~$DSC0wtZL1B8M6PC=D74cv5P9Z9j|2mmu_GnkEm9DILF6PP#riGDCtC1!IwCh zz=wOf$7bH=EZ=ImYF)a3YG+sHh@Zj@FTfZ3LK#?l+Ou&wfP{VVBj4c@?cP%5PUX0j z?yWwbiN;Lp#~K4yd(?rkDZV2oIl}h!9vAD;QOdrFetvhfLRpWesGaOi$$N{H)h?u+ zSY6glD%8Ga;+Ky#-#v}f4bEq7>+Np(+r~NVR@^M-j;Q{3h`2`fT;@B#p60CAU;FM?_v^ zDbMTCo%pWCJA%A_VzkVF<$WNG+#Gblb!4ZibK&{S?uNSi)b1v_`y~Cz?r-efvEYuhCAd`RsF6Fn&vII6Rs>Ay58v9DwAkh_hjwy^>U zql*BCkJHoOXPm>3!|^Wida0YxH5G#0Y`GGOKWa_CaYYjqP9yMBIC=f3_7cHuSGA=6 zX`*)|MAW4>M%TNOUCA@mFB&7$_(Jl*6PwbNQolIGcx~sze&VrE*F$tNqo*rx>GT&y zq5k|_=e-YBH8t?ouZL@WRF14px)BHT7~1rmUunHO!@0RVy{mE=yc2KMe4u$DsY#}A_4huTc7xcyp6pA=abuWD0A$#oSun?$9y$MbGk~ei}t`k z&MGr8u{f8-@bA3q@f_)1PwplPh@9$ghRv3Eu6a?7x5_I2E_f2lU?B@8g>14K;^(!^S$2=Mh`Xo4jKlQNp~Ncg%CI zcZ$ur&b-}knaf?19`oF)2ePIdXo@1whG>X1VwFtATU!^kHq(c|S44|+7t zIjVHqq{^86tmaHeJ&$ds2(o591;x0Qdiz$i@5*oRWpsjvXX#5{`=}V7=-hLf$`PWk zK2}E89Z7#*E2f5WI}SbLabGK%IPDgA);aAOCv4Z`521-|uNiL{VgSitFYU49D2s1u94vME{(v6-1Xg7I`K$x|LB;9^rchIy(fy38`-njkDBvCH8+$i GvCpq+lMnI$ diff --git a/src/vai_lab/DataProcessing/plugins/imgtofeatvect.py b/src/vai_lab/DataProcessing/plugins/imgtofeatvect.py index 2a4f4ddd81f37b923ec0f545be156d54efe8292f..d1fb47ab4d750b17ddb212b5bf0dcf62873d7536 100644 GIT binary patch literal 1691 zcmah}QE%EX5Pr|Eu)LRK%JKt{`alI#NG&i*C#tGSO>zld9Xqm3J683-?`(*HmTeIP zbH4lTyZg?rv}(u;Gp!0RhRb>pRW8ar3t(Di zD`uc`yu|g;Sl%$JbhM9dnJk$h_-V_|CO1vHbtc(HQ9irAyBbgO`LI9eUtSOMN&jYe zOD@P89XIttjeyx7P*~cdB~)x9EKMWw*N5_^wyMIQ7av3@6Py{El79t_8&<(lnh(FUvgbBk0VQn@o=s3v& znLSpVBnKblm$i7tu*;1qLBvVaRlNIua<=xRd@3;|sP%NJwuFx#wUvT7JqGkX_#}pIZIIJ0VzIz7e+;%AH&Xyc6YJ&m}fnfQ*;fGi=SV z=h5Vz-T{^jJfnn_bB z-JB71nd3F>(zaP?EGwSlhvfr^$4rAy=|i$IFiZ#n5fD!7u9nBc1b1-$_Y_0!Fpi); QGH=Vwo#c^IKlTX8A334l3jhEB literal 3488 zcmbuC-A>y;5QXQuQs2Q!Z=h=Z07zVbfC{Pn6hf5>AqXLVB{5NKXer{=+kR&^iyg-X zB4lm6yWW{OGdpv3^Y3rZrgmdvi!8Nk8(G)d%p?}uCB2k!WJA7BST(hb(dKM^o3EMs z*5<9R@9mxC`tDdav~zT}(G}C1(7&}lvk9XN-O{V?>Zc2qw%nJt9XgC1wv z^T;NwPwa|c>GHo5#=nc1hm zNADQrF zkJ-CLL^H>8sv^zj<2oOnM-@1x<9IK1OjXYuD^!A*q>ckusV8X+5h?aP-$Wojr{)q` z=K3qx>|(uVW$B=$fv4g#FC!Kamq<#yDwb94Cg_XcH}SXph#3{_2W{bfI9unnT)+Y& zSU{)D2SUR+R_BUNthCau_o50>iav4C)K-XrPNs5% z=CNtjYNd@3$ryb)M?0Whr;X_={v&5W7(&bAHNUF=NtA(aa`wA z@{#V=?tAV{S=xJ5o-M|<*bfMM*w2VS&?{W6TI+tlwuqYK61T59H^k}@W1ZNS^rz0d z9=|Cb`5Q#IJtWKq38Bz!L8px@`}v`h_qPt+D8@8pauI*XSbv+Cmz2go$i$TrRa%ty zQ-`#R9pSIe*Uz^2ROxHsi%!~yr+Rs;#1gy^>xRk-`7m`ipN`88v5V)2gjo68oEdm%wyJSH z#}1wyW9PZ5Ssas)$1QxmrhkuT^_(yu+kVl47ctT5VsjzW{W@l6bQe-CU8Cugh@LtmXM7XF+~3@eq!y#OicXJ7**Rk5Gqt=&XEn@S o#+Q$uLn0;#{@39u#xdmPeDw97Bz`*CcU+9cL&~raHGkgx2P8G)sQ>@~