From 35e83aa37b7f24b541aab14af9e6be4c350bcc37 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Mon, 6 Jun 2022 20:34:24 +0900 Subject: [PATCH 01/79] Fix no geometry input error --- Tunny/Solver/OptunaAlgorithm.cs | 30 ++++++++++++++++++++++-------- Tunny/Util/GrasshopperInOut.cs | 1 + 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Tunny/Solver/OptunaAlgorithm.cs b/Tunny/Solver/OptunaAlgorithm.cs index 90d75311..730e6e82 100644 --- a/Tunny/Solver/OptunaAlgorithm.cs +++ b/Tunny/Solver/OptunaAlgorithm.cs @@ -66,8 +66,7 @@ public void Solve() name.Append(objName + ","); } name.Remove(name.Length - 1, 1); - study.set_user_attr("objective_names", name.ToString()); - study.set_user_attr("tunny_version", Assembly.GetExecutingAssembly().GetName().Version.ToString(3)); + SetStudyUserAttr(study, name); double[] xTest = new double[variableCount]; var result = new EvaluatedGHResult(); @@ -99,12 +98,8 @@ public void Solve() break; } } - var pyJson = new PyList(); - foreach (string json in result.GeometryJson) - { - pyJson.Append(new PyString(json)); - } - trial.set_user_attr("geometry", pyJson); + + SetTrialUserAttrToGeometry(result, trial); try { study.tell(trial, result.ObjectiveValues.ToArray()); @@ -143,6 +138,25 @@ public void Solve() PythonEngine.Shutdown(); } + private static void SetStudyUserAttr(dynamic study, StringBuilder name) + { + study.set_user_attr("objective_names", name.ToString()); + study.set_user_attr("tunny_version", Assembly.GetExecutingAssembly().GetName().Version.ToString(3)); + } + + private static void SetTrialUserAttrToGeometry(EvaluatedGHResult result, dynamic trial) + { + if (result.GeometryJson.Count != 0) + { + var pyJson = new PyList(); + foreach (string json in result.GeometryJson) + { + pyJson.Append(new PyString(json)); + } + trial.set_user_attr("geometry", pyJson); + } + } + private dynamic SetSamplerSettings(int n, int samplerType, ref int nTrials, dynamic optuna) { dynamic sampler; diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index 68abda99..a2d51ede 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -168,6 +168,7 @@ private bool SetModelGeometries() { if (_component.Params.Input[2].SourceCount == 0) { + _geometries = new List(); return false; } From 66aa9db9b256988da9ed71c2114aa831eb8d9c9c Mon Sep 17 00:00:00 2001 From: hrntsm Date: Mon, 6 Jun 2022 20:37:50 +0900 Subject: [PATCH 02/79] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bad6d9c..9e696409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p Please see [here](https://github.com/hrntsm/Tunny/releases) for the data released for each version. +## [UNRELEASED] + +### Fix + +- Stopped sampling when there was no geometry input + ## [0.3.0] -2022-05-03 ### Added From 6117a7bdb3f46803a21daebef5682850e28ea53e Mon Sep 17 00:00:00 2001 From: hrntsm Date: Tue, 7 Jun 2022 17:25:22 +0900 Subject: [PATCH 03/79] Add ci build setting file --- .../workflows/dotnet-grasshopper - Copy.yml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/dotnet-grasshopper - Copy.yml diff --git a/.github/workflows/dotnet-grasshopper - Copy.yml b/.github/workflows/dotnet-grasshopper - Copy.yml new file mode 100644 index 00000000..846f5609 --- /dev/null +++ b/.github/workflows/dotnet-grasshopper - Copy.yml @@ -0,0 +1,56 @@ +name: Build Grasshopper Plugin + +on: + push: + branches: [develop] + pull_request: + branches: [main, develop] + +jobs: + build: + strategy: + matrix: + configuration: [Release] + + runs-on: windows-latest # For a list of available runner types, refer to + + env: + Solution_Name: Tunny.sln + Plugin_File_Name: Tunny + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup MSBuild.exe + uses: microsoft/setup-msbuild@v1 + + - name: Setup NuGet + uses: NuGet/setup-nuget@v1 + + - name: Restore the application + run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration + env: + Configuration: ${{ matrix.configuration }} + + - name: Build the application + run: msbuild $env:Solution_Name /p:Configuration=$env:Configuration + env: + Configuration: ${{ matrix.configuration }} + + - name: Copy Samples & License to /HoaryFox/bin dir + if: ${{ matrix.configuration == 'Release' }} # Only upload gha from a release build + shell: powershell + run: | + cp ./Samples ./Release/Samples -recurse + cp ./LICENSE ./Release/LICENSE + + - name: Upload release build of plugin as artefact + if: ${{ matrix.configuration == 'Release' }} # Only upload gha from a release build + uses: actions/upload-artifact@v2 + with: + name: Tunny + path: | + ./Release From ec4659b9c554cadb1bfa786dd8bdaaf0875c64da Mon Sep 17 00:00:00 2001 From: hrntsm Date: Tue, 7 Jun 2022 17:31:33 +0900 Subject: [PATCH 04/79] Update file name to fit ci settings --- ...t-grasshopper - Copy.yml => build-component.yml} | 0 {Sample => Samples}/sample.gh | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{dotnet-grasshopper - Copy.yml => build-component.yml} (100%) rename {Sample => Samples}/sample.gh (100%) diff --git a/.github/workflows/dotnet-grasshopper - Copy.yml b/.github/workflows/build-component.yml similarity index 100% rename from .github/workflows/dotnet-grasshopper - Copy.yml rename to .github/workflows/build-component.yml diff --git a/Sample/sample.gh b/Samples/sample.gh similarity index 100% rename from Sample/sample.gh rename to Samples/sample.gh From 8f3621008dbd5daebed5ca0049d6d78608340f8c Mon Sep 17 00:00:00 2001 From: hrntsm Date: Tue, 7 Jun 2022 17:43:18 +0900 Subject: [PATCH 05/79] Update artifact path --- .github/workflows/build-component.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-component.yml b/.github/workflows/build-component.yml index 846f5609..56c66f9e 100644 --- a/.github/workflows/build-component.yml +++ b/.github/workflows/build-component.yml @@ -40,7 +40,7 @@ jobs: env: Configuration: ${{ matrix.configuration }} - - name: Copy Samples & License to /HoaryFox/bin dir + - name: Copy Samples & License to /Tunny/bin dir if: ${{ matrix.configuration == 'Release' }} # Only upload gha from a release build shell: powershell run: | @@ -53,4 +53,4 @@ jobs: with: name: Tunny path: | - ./Release + ./ From 7688ef6fb36f5c3dbb9d935353ebd7133409579a Mon Sep 17 00:00:00 2001 From: hrntsm Date: Tue, 7 Jun 2022 17:50:32 +0900 Subject: [PATCH 06/79] Update Release item copy settings --- .github/workflows/build-component.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-component.yml b/.github/workflows/build-component.yml index 56c66f9e..19c85c8b 100644 --- a/.github/workflows/build-component.yml +++ b/.github/workflows/build-component.yml @@ -40,12 +40,13 @@ jobs: env: Configuration: ${{ matrix.configuration }} - - name: Copy Samples & License to /Tunny/bin dir + - name: Copy items to /Release dir if: ${{ matrix.configuration == 'Release' }} # Only upload gha from a release build shell: powershell run: | cp ./Samples ./Release/Samples -recurse cp ./LICENSE ./Release/LICENSE + cp ./Tunny/bin/Release/nwt48 ./Release/Tunny -recurse - name: Upload release build of plugin as artefact if: ${{ matrix.configuration == 'Release' }} # Only upload gha from a release build @@ -53,4 +54,4 @@ jobs: with: name: Tunny path: | - ./ + ./Release From 66efb14320ffc9a341fe5bf0c643dc94b82f259b Mon Sep 17 00:00:00 2001 From: hrntsm Date: Tue, 7 Jun 2022 17:53:46 +0900 Subject: [PATCH 07/79] Fix typo --- .github/workflows/build-component.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-component.yml b/.github/workflows/build-component.yml index 19c85c8b..deecbb3d 100644 --- a/.github/workflows/build-component.yml +++ b/.github/workflows/build-component.yml @@ -46,7 +46,7 @@ jobs: run: | cp ./Samples ./Release/Samples -recurse cp ./LICENSE ./Release/LICENSE - cp ./Tunny/bin/Release/nwt48 ./Release/Tunny -recurse + cp ./Tunny/bin/Release/net48 ./Release/Tunny -recurse - name: Upload release build of plugin as artefact if: ${{ matrix.configuration == 'Release' }} # Only upload gha from a release build From 8c4bfa29beea90e29f4b18157c3b774595317bdf Mon Sep 17 00:00:00 2001 From: hrntsm Date: Tue, 7 Jun 2022 18:05:03 +0900 Subject: [PATCH 08/79] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e696409..dbbc9cfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release ## [UNRELEASED] +### Added + +- Component CI build to get easily development build. + ### Fix - Stopped sampling when there was no geometry input From 51aa0f70e9c72a190044ef9eaf4e28cd452fa020 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Tue, 14 Jun 2022 12:16:15 +0900 Subject: [PATCH 09/79] Rename namespace GHType to Type --- Tunny/Component/FishMarket.cs | 6 +++--- Tunny/Component/Param_Fish.cs | 8 ++++---- Tunny/Component/TunnyComponent.cs | 6 +++--- Tunny/Optimization/RestoreLoop.cs | 11 +++++------ Tunny/{GHType => Type}/Fish.cs | 2 +- Tunny/{GHType => Type}/GH_Fish.cs | 6 +++--- 6 files changed, 19 insertions(+), 20 deletions(-) rename Tunny/{GHType => Type}/Fish.cs (98%) rename Tunny/{GHType => Type}/GH_Fish.cs (96%) diff --git a/Tunny/Component/FishMarket.cs b/Tunny/Component/FishMarket.cs index c84e1149..362fefca 100644 --- a/Tunny/Component/FishMarket.cs +++ b/Tunny/Component/FishMarket.cs @@ -10,8 +10,8 @@ using Rhino.Display; using Rhino.Geometry; -using Tunny.GHType; using Tunny.Resources; +using Tunny.Type; namespace Tunny.Component { @@ -26,8 +26,8 @@ public class FishMarket : GH_Component public FishMarket() : base("FishMarket", "FMarket", - "A place to lay out the solutions we caught.", - "Params", "Tunny") + "A place to lay out the solutions we caught.", + "Params", "Tunny") { } diff --git a/Tunny/Component/Param_Fish.cs b/Tunny/Component/Param_Fish.cs index 08e8beb7..47bec70a 100644 --- a/Tunny/Component/Param_Fish.cs +++ b/Tunny/Component/Param_Fish.cs @@ -4,8 +4,8 @@ using Grasshopper.Kernel; -using Tunny.GHType; using Tunny.Resources; +using Tunny.Type; namespace Tunny.Component { @@ -13,9 +13,9 @@ public class Param_Fish : GH_PersistentParam { public override GH_Exposure Exposure => GH_Exposure.primary; public Param_Fish() - : base("Fish", "Fish", - "Fish caught by the optimization nets", - "Params", "Tunny") + : base("Fish", "Fish", + "Fish caught by the optimization nets", + "Params", "Tunny") { } diff --git a/Tunny/Component/TunnyComponent.cs b/Tunny/Component/TunnyComponent.cs index 72745542..5d594e91 100644 --- a/Tunny/Component/TunnyComponent.cs +++ b/Tunny/Component/TunnyComponent.cs @@ -6,8 +6,8 @@ using Grasshopper.GUI; using Grasshopper.Kernel; -using Tunny.GHType; using Tunny.Resources; +using Tunny.Type; using Tunny.UI; using Tunny.Util; @@ -23,8 +23,8 @@ public partial class TunnyComponent : GH_Component public TunnyComponent() : base("Tunny", "Tunny", - "Tunny is an optimization component wrapped in optuna.", - "Params", "Tunny") + "Tunny is an optimization component wrapped in optuna.", + "Params", "Tunny") { } diff --git a/Tunny/Optimization/RestoreLoop.cs b/Tunny/Optimization/RestoreLoop.cs index f95bfbfd..513190c1 100644 --- a/Tunny/Optimization/RestoreLoop.cs +++ b/Tunny/Optimization/RestoreLoop.cs @@ -3,12 +3,11 @@ using System.Linq; using System.Windows.Forms; -using Rhino.FileIO; using Rhino.Geometry; using Tunny.Component; -using Tunny.GHType; using Tunny.Solver; +using Tunny.Type; using Tunny.UI; using Tunny.Util; @@ -28,7 +27,7 @@ internal static void Run(object sender, DoWorkEventArgs e) s_worker = sender as BackgroundWorker; s_component = e.Argument as TunnyComponent; - var cFishes = new List(); + var fishes = new List(); var optunaSolver = new Optuna(s_component.GhInOut.ComponentFolder); ModelResult[] modelResult = optunaSolver.GetModelResult(Indices, StudyName); @@ -43,7 +42,7 @@ internal static void Run(object sender, DoWorkEventArgs e) case "Restore": for (int i = 0; i < modelResult.Length; i++) { - SetResultToFish(cFishes, modelResult[i], NickNames); + SetResultToFish(fishes, modelResult[i], NickNames); s_worker.ReportProgress(i * 100 / modelResult.Length); } break; @@ -55,12 +54,12 @@ internal static void Run(object sender, DoWorkEventArgs e) "Tunny" ); } - SetResultToFish(cFishes, modelResult[0], NickNames); + SetResultToFish(fishes, modelResult[0], NickNames); s_worker.ReportProgress(100); break; } - s_component.Fishes = cFishes.ToArray(); + s_component.Fishes = fishes.ToArray(); s_worker.ReportProgress(100); if (s_worker != null) diff --git a/Tunny/GHType/Fish.cs b/Tunny/Type/Fish.cs similarity index 98% rename from Tunny/GHType/Fish.cs rename to Tunny/Type/Fish.cs index 97bc5e61..4e586df7 100644 --- a/Tunny/GHType/Fish.cs +++ b/Tunny/Type/Fish.cs @@ -7,7 +7,7 @@ using Rhino.Geometry; -namespace Tunny.GHType +namespace Tunny.Type { [Serializable] public class Fish diff --git a/Tunny/GHType/GH_Fish.cs b/Tunny/Type/GH_Fish.cs similarity index 96% rename from Tunny/GHType/GH_Fish.cs rename to Tunny/Type/GH_Fish.cs index e86bf1c5..c8ff183e 100644 --- a/Tunny/GHType/GH_Fish.cs +++ b/Tunny/Type/GH_Fish.cs @@ -5,7 +5,7 @@ using Grasshopper.Kernel.Types; -namespace Tunny.GHType +namespace Tunny.Type { public class GH_Fish : GH_Goo { @@ -13,8 +13,8 @@ public GH_Fish() { } - public GH_Fish(Fish cFish) - : base(cFish) + public GH_Fish(Fish fish) + : base(fish) { } From ed10f3358de1d749703ddbc88bf01e37b6b7fdef Mon Sep 17 00:00:00 2001 From: hrntsm Date: Tue, 14 Jun 2022 12:19:28 +0900 Subject: [PATCH 10/79] Add fish attribute component --- Tunny/Component/FishAttribute.cs | 132 +++++++++++++++++++++++++ Tunny/Component/Param_FishAttribute.cs | 28 ++++++ Tunny/Type/GH_FishAttribute.cs | 76 ++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 Tunny/Component/FishAttribute.cs create mode 100644 Tunny/Component/Param_FishAttribute.cs create mode 100644 Tunny/Type/GH_FishAttribute.cs diff --git a/Tunny/Component/FishAttribute.cs b/Tunny/Component/FishAttribute.cs new file mode 100644 index 00000000..6653e23f --- /dev/null +++ b/Tunny/Component/FishAttribute.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; + +using Grasshopper.Kernel; +using Grasshopper.Kernel.Parameters; + +using Tunny.Type; + +namespace Tunny.Component +{ + public class FishAttribute : GH_Component, IGH_VariableParameterComponent + { + private readonly string _geomDescription = "Connect model geometries here. Not required. Large size models are not recommended as it affects the speed of analysis.\"Geometry\" is a special nickname that is visualized with the optimization results. Do not change it."; + private readonly string _attrDescription = "Attributes to each trial. Attribute name will be the nickname of the input, so change it to any value."; + public override GH_Exposure Exposure => GH_Exposure.secondary; + + public FishAttribute() + : base("FishAttribute", "FAttribute", + "Description of component", + "Params", "Tunny") + { + } + + protected override void RegisterInputParams(GH_InputParamManager pManager) + { + pManager.AddGeometryParameter("Geometry", "Geometry", _geomDescription, GH_ParamAccess.list); + pManager.AddGenericParameter("Attr1", "Attr1", _attrDescription, GH_ParamAccess.list); + pManager.AddGenericParameter("Attr2", "Attr2", _attrDescription, GH_ParamAccess.list); + Params.Input[0].Optional = true; + Params.Input[1].Optional = true; + Params.Input[2].Optional = true; + } + + protected override void RegisterOutputParams(GH_OutputParamManager pManager) + { + pManager.AddParameter( + new Param_FishAttribute("Attributes", "Attrs", "Attributes to each Trial", "Params", "Tunny", GH_ParamAccess.list)); + } + + protected override void SolveInstance(IGH_DataAccess DA) + { + int paramCount = Params.Input.Count; + + var dict = new Dictionary(); + + for (int i = 0; i < paramCount; i++) + { + var list = new List(); + if (!DA.GetDataList(i, list)) + { + continue; + } + string key = Params.Input[i].NickName; + dict.Add(key, list); + } + + DA.SetData(0, dict); + } + + public bool CanInsertParameter(GH_ParameterSide side, int index) + { + return side == GH_ParameterSide.Input; + } + + public bool CanRemoveParameter(GH_ParameterSide side, int index) + { + return side == GH_ParameterSide.Input; + } + + public IGH_Param CreateParameter(GH_ParameterSide side, int index) + { + if (side == GH_ParameterSide.Input) + { + if (index == 0) + { + var p = new Param_Geometry(); + p.Name = p.NickName = "Geometry"; + p.Description = _geomDescription; + p.Access = GH_ParamAccess.list; + p.Optional = true; + return p; + } + else + { + var p = new Param_GenericObject(); + p.Name = p.NickName = $"Attr{index}"; + p.Description = _attrDescription; + p.Access = GH_ParamAccess.list; + p.Optional = true; + return p; + } + } + else + { + return null; + } + } + + public bool DestroyParameter(GH_ParameterSide side, int index) + { + return true; + } + + public void VariableParameterMaintenance() + { + foreach (IGH_Param param in Params) + { + param.ObjectChanged -= ParamChangedHandler; + param.ObjectChanged += ParamChangedHandler; + } + } + + private void ParamChangedHandler(IGH_DocumentObject sender, GH_ObjectChangedEventArgs e) + { + if (e.Type == GH_ObjectEventType.NickName) + { + if (sender.NickName.Length == 0) + { + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Attribute nickname length must be longer than 0."); + } + else + { + ExpireSolution(true); + } + } + } + + protected override System.Drawing.Bitmap Icon => null; + + public override Guid ComponentGuid => new Guid("0E66E2E8-6A97-45E0-93DF-2251C3949B7D"); + } +} diff --git a/Tunny/Component/Param_FishAttribute.cs b/Tunny/Component/Param_FishAttribute.cs new file mode 100644 index 00000000..4dbd99da --- /dev/null +++ b/Tunny/Component/Param_FishAttribute.cs @@ -0,0 +1,28 @@ +using System; + +using Grasshopper.Kernel; + +namespace Tunny.Type +{ + public class Param_FishAttribute : GH_Param + { + public override GH_Exposure Exposure => GH_Exposure.hidden; + + public Param_FishAttribute(IGH_InstanceDescription tag) + : base(tag) + { + } + + public Param_FishAttribute(IGH_InstanceDescription tag, GH_ParamAccess access) + : base(tag, access) + { + } + + public Param_FishAttribute(string name, string nickname, string description, string category, string subcategory, GH_ParamAccess access) + : base(name, nickname, description, category, subcategory, access) + { + } + + public override Guid ComponentGuid => new Guid("bfbd03eb-492d-4c57-8311-e7452d78a78e"); + } +} diff --git a/Tunny/Type/GH_FishAttribute.cs b/Tunny/Type/GH_FishAttribute.cs new file mode 100644 index 00000000..e8d0c33f --- /dev/null +++ b/Tunny/Type/GH_FishAttribute.cs @@ -0,0 +1,76 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text; + +using Grasshopper.Kernel.Types; + +namespace Tunny.Type +{ + public class GH_FishAttribute : GH_Goo> + { + public GH_FishAttribute() + { + } + + public GH_FishAttribute(Dictionary internal_data) : base(internal_data) + { + } + + public GH_FishAttribute(GH_Goo> other) : base(other) + { + } + + public override bool IsValid => Value != null; + + public override string TypeName => "GH_FishAttributes"; + + public override string TypeDescription => "DictionaryGoo for grasshopper"; + + public override IGH_Goo Duplicate() + { + return new GH_FishAttribute() { Value = Value }; + } + + public override string ToString() + { + var text = new StringBuilder(); + foreach (string key in Value.Keys) + { + text.AppendLine($"{key}:"); + if (Value[key] is IEnumerable iEnum) + { + foreach (object item in iEnum) + text.AppendLine($" {item}"); + } + else + { + text.AppendLine($" {Value[key]}"); + } + } + return text.ToString(); + } + + public override bool CastFrom(object source) + { + if (source is Dictionary dict) + { + Value = dict; + return true; + } + else + return false; + } + + public override bool CastTo(ref Q target) + { + target = default; + if (typeof(Q).IsAssignableFrom(typeof(Dictionary))) + { + target = (Q)(object)Value; + return true; + } + else + return false; + } + } +} From a772f5ac7a1122a3a2f57370fe2f8592827a3ea5 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 16 Jun 2022 19:34:18 +0900 Subject: [PATCH 11/79] Add handle attribute in optimization --- Tunny/Component/TunnyComponent.cs | 6 +- Tunny/Optimization/EvaluateGHResult.cs | 1 + Tunny/Optimization/OptimizeLoop.cs | 3 +- Tunny/Optimization/RestoreLoop.cs | 13 +++- Tunny/Solver/Optuna.cs | 40 +++++++++--- Tunny/Solver/OptunaAlgorithm.cs | 15 ++++- Tunny/Type/Fish.cs | 2 +- Tunny/Type/GH_Fish.cs | 61 +++++++++++++------ Tunny/Util/GrasshopperInOut.cs | 84 ++++++++++++++++++++------ Tunny/Util/ModelResult.cs | 3 +- 10 files changed, 178 insertions(+), 50 deletions(-) diff --git a/Tunny/Component/TunnyComponent.cs b/Tunny/Component/TunnyComponent.cs index 5d594e91..5b5f2693 100644 --- a/Tunny/Component/TunnyComponent.cs +++ b/Tunny/Component/TunnyComponent.cs @@ -30,9 +30,9 @@ public TunnyComponent() protected override void RegisterInputParams(GH_InputParamManager pManager) { - pManager.AddNumberParameter("Variables", "Variables", "Connect variable number slider here.", GH_ParamAccess.tree); - pManager.AddNumberParameter("Objectives", "Objectives", "Connect objective number component here.", GH_ParamAccess.tree); - pManager.AddGeometryParameter("Geometries", "Geometries", "Connect model geometries here. Not required. Large size models are not recommended as it affects the speed of analysis.", GH_ParamAccess.tree); + pManager.AddNumberParameter("Variables", "Vars", "Connect variable number slider here.", GH_ParamAccess.tree); + pManager.AddNumberParameter("Objectives", "Objs", "Connect objective number component here.", GH_ParamAccess.tree); + pManager.AddParameter(new Param_FishAttribute("Attributes", "Attrs", "Connect model attribute like some geometry or values here. Not required.", "Param", "Tunny", GH_ParamAccess.item)); Params.Input[0].Optional = true; Params.Input[1].Optional = true; Params.Input[2].Optional = true; diff --git a/Tunny/Optimization/EvaluateGHResult.cs b/Tunny/Optimization/EvaluateGHResult.cs index cdc0b7f5..95fa0f45 100644 --- a/Tunny/Optimization/EvaluateGHResult.cs +++ b/Tunny/Optimization/EvaluateGHResult.cs @@ -6,5 +6,6 @@ public class EvaluatedGHResult { public List ObjectiveValues { get; set; } public List GeometryJson { get; set; } + public Dictionary> Attribute { get; set; } } } diff --git a/Tunny/Optimization/OptimizeLoop.cs b/Tunny/Optimization/OptimizeLoop.cs index 59d6ffda..eb7b26fd 100644 --- a/Tunny/Optimization/OptimizeLoop.cs +++ b/Tunny/Optimization/OptimizeLoop.cs @@ -69,7 +69,8 @@ private static EvaluatedGHResult EvaluateFunction(IList values, int pro var result = new EvaluatedGHResult { ObjectiveValues = s_component.GhInOut.GetObjectiveValues(), - GeometryJson = s_component.GhInOut.GetGeometryJson() + GeometryJson = s_component.GhInOut.GetGeometryJson(), + Attribute = s_component.GhInOut.GetAttributes() }; return result; } diff --git a/Tunny/Optimization/RestoreLoop.cs b/Tunny/Optimization/RestoreLoop.cs index 513190c1..16f5f491 100644 --- a/Tunny/Optimization/RestoreLoop.cs +++ b/Tunny/Optimization/RestoreLoop.cs @@ -76,7 +76,8 @@ private static void SetResultToFish(ICollection fishes, ModelResult model, ModelNumber = model.Number, Variables = SetVariables(model, nickname), Objectives = SetObjectives(model), - Geometries = SetGeometries(model) + Geometries = SetGeometries(model), + Attributes = SetAttributes(model), }); } @@ -119,5 +120,15 @@ private static List SetGeometries(ModelResult model) } return geometries; } + + private static Dictionary> SetAttributes(ModelResult model) + { + var attribute = new Dictionary>(); + foreach (KeyValuePair> attr in model.Attributes) + { + attribute.Add(attr.Key, attr.Value); + } + return attribute; + } } } diff --git a/Tunny/Solver/Optuna.cs b/Tunny/Solver/Optuna.cs index 7d259f19..7da93faf 100644 --- a/Tunny/Solver/Optuna.cs +++ b/Tunny/Solver/Optuna.cs @@ -198,21 +198,47 @@ public ModelResult[] GetModelResult(int[] resultNum, string studyName) private static void ParseTrial(ICollection modelResult, dynamic trial) { + Dictionary variables = ParseVariables(trial); + Dictionary> attributes = ParseAttributes(trial); + + modelResult.Add(new ModelResult() + { + Number = (int)trial.number, + GeometryJson = (string[])trial.user_attrs["Geometry"], + Attributes = attributes, + Variables = variables, + Objectives = (double[])trial.values, + }); + } + + private static Dictionary ParseVariables(dynamic trial) + { + var variables = new Dictionary(); double[] values = (double[])trial.@params.values(); string[] keys = (string[])trial.@params.keys(); - var variables = new Dictionary(); for (int i = 0; i < keys.Length; i++) { variables.Add(keys[i], values[i]); } - modelResult.Add(new ModelResult() + return variables; + } + + private static Dictionary> ParseAttributes(dynamic trial) + { + var attributes = new Dictionary>(); + string[] keys = (string[])trial.user_attrs.keys(); + for (int i = 0; i < keys.Length; i++) { - Number = (int)trial.number, - GeometryJson = (string[])trial.user_attrs["geometry"], - Variables = variables, - Objectives = (double[])trial.values, - }); + if (keys[i] == "Geometry") + { + continue; + } + string[] values = (string[])trial.user_attrs[keys[i]]; + attributes.Add(keys[i], values.ToList()); + } + + return attributes; } } } diff --git a/Tunny/Solver/OptunaAlgorithm.cs b/Tunny/Solver/OptunaAlgorithm.cs index 730e6e82..2816b748 100644 --- a/Tunny/Solver/OptunaAlgorithm.cs +++ b/Tunny/Solver/OptunaAlgorithm.cs @@ -153,7 +153,20 @@ private static void SetTrialUserAttrToGeometry(EvaluatedGHResult result, dynamic { pyJson.Append(new PyString(json)); } - trial.set_user_attr("geometry", pyJson); + trial.set_user_attr("Geometry", pyJson); + } + + if (result.Attribute != null) + { + foreach (KeyValuePair> pair in result.Attribute) + { + var pyList = new PyList(); + foreach (string str in pair.Value) + { + pyList.Append(new PyString(str)); + } + trial.set_user_attr(pair.Key, pyList); + } } } diff --git a/Tunny/Type/Fish.cs b/Tunny/Type/Fish.cs index 4e586df7..e10c69cf 100644 --- a/Tunny/Type/Fish.cs +++ b/Tunny/Type/Fish.cs @@ -16,6 +16,7 @@ public class Fish public List Geometries; public Dictionary Variables; public Dictionary Objectives; + public Dictionary> Attributes; public string SerializeToJson() { @@ -42,7 +43,6 @@ public static Fish FromBase64(string base64String) { return (Fish)new BinaryFormatter().Deserialize(ms); } - } } } diff --git a/Tunny/Type/GH_Fish.cs b/Tunny/Type/GH_Fish.cs index c8ff183e..6ac06592 100644 --- a/Tunny/Type/GH_Fish.cs +++ b/Tunny/Type/GH_Fish.cs @@ -48,31 +48,58 @@ public override bool Write(GH_IWriter writer) public override string ToString() { var sb = new StringBuilder(); - sb.Append("====================================\n"); - sb.Append("Model Number: " + m_value.ModelNumber + "\n"); - sb.Append("====================================\n"); - sb.Append("Variables:\n"); - sb.Append("------------------------------------\n"); - foreach (KeyValuePair variable in m_value.Variables) + SetModelNumber(sb); + SetVariables(sb); + SetObjectives(sb); + SetAttributes(sb); + return sb.ToString(); + } + + private void SetAttributes(StringBuilder sb) + { + sb.AppendLine("------------------------------------"); + sb.AppendLine("Attributes:"); + sb.AppendLine("------------------------------------"); + bool hasGeometry = m_value.Geometries != null; + sb.AppendLine(" Include Geometry: " + hasGeometry); + + foreach (KeyValuePair> attr in m_value.Attributes) { - sb.Append(" \"" + variable.Key + "\": " + variable.Value + "\n"); + string valueStrings = string.Empty; + foreach (string val in attr.Value) + { + valueStrings += val + ", "; + } + sb.AppendLine(" " + attr.Key + ": " + valueStrings); } + } - sb.Append("------------------------------------\n"); - sb.Append("Objectives:\n"); - sb.Append("------------------------------------\n"); + private void SetObjectives(StringBuilder sb) + { + sb.AppendLine("------------------------------------"); + sb.AppendLine("Objectives:"); + sb.AppendLine("------------------------------------"); foreach (KeyValuePair objective in m_value.Objectives) { - sb.Append(" \"" + objective.Key + "\": " + objective.Value + "\n"); + sb.AppendLine(" \"" + objective.Key + "\": " + objective.Value); } + } - sb.Append("------------------------------------\n"); - sb.Append("Attributes:\n"); - sb.Append("------------------------------------\n"); - bool hasGeometry = m_value.Geometries != null; - sb.Append(" Include Geometry: " + hasGeometry); + private void SetModelNumber(StringBuilder sb) + { + sb.AppendLine("===================================="); + sb.AppendLine("Model Number: " + m_value.ModelNumber + ""); + sb.AppendLine("===================================="); + } - return sb.ToString(); + private void SetVariables(StringBuilder sb) + { + sb.AppendLine("Variables:"); + sb.AppendLine("------------------------------------"); + foreach (KeyValuePair variable in m_value.Variables) + { + sb.AppendLine(" \"" + variable.Key + "\": " + variable.Value + ""); + } } public class GH_FishProxy : GH_GooProxy diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index a2d51ede..3357189e 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -14,6 +14,7 @@ using Rhino.FileIO; using Tunny.Component; +using Tunny.Type; using Tunny.UI; namespace Tunny.Util @@ -24,7 +25,7 @@ public class GrasshopperInOut private readonly List _inputGuids; private readonly TunnyComponent _component; private List _genePool; - private List _geometries; + private GH_FishAttribute _attributes; public List Objectives; public List Sliders; @@ -46,7 +47,7 @@ private bool SetInputs() { SetVariables(); SetObjectives(); - SetModelGeometries(); + SetAttributes(); return true; } @@ -164,15 +165,24 @@ private bool SetObjectives() return true; } - private bool SetModelGeometries() + private bool SetAttributes() { if (_component.Params.Input[2].SourceCount == 0) { - _geometries = new List(); + _attributes = new GH_FishAttribute(); return false; } - _geometries = _component.Params.Input[2].Sources.ToList(); + IGH_StructureEnumerator enumerator = _component.Params.Input[2].Sources[0].VolatileData.AllData(true); + foreach (IGH_Goo goo in enumerator) + { + if (goo is GH_FishAttribute fishAttr) + { + _attributes = fishAttr; + break; + } + } + return true; } @@ -280,46 +290,84 @@ public List GetGeometryJson() var json = new List(); var option = new SerializationOptions(); - if (_geometries.Count == 0) + if (_attributes == null || !_attributes.Value.ContainsKey("Geometry")) { return json; } - foreach (IGH_Param param in _geometries) + var geometries = _attributes.Value["Geometry"] as List; + foreach (object param in geometries) { - IGH_StructureEnumerator ghEnumerator = param.VolatileData.AllData(true); - - foreach (IGH_Goo goo in ghEnumerator) + if (param is IGH_Goo goo) { - AddEachGHParamToJson(json, option, goo); + json.Add(GHParamToString(goo, option)); } } return json; } - private static void AddEachGHParamToJson(List json, SerializationOptions option, IGH_Goo goo) + private static string GHParamToString(IGH_Goo goo) { + var option = new SerializationOptions(); + return GHParamToString(goo, option); + } + + private static string GHParamToString(IGH_Goo goo, SerializationOptions option) + { + string result; switch (goo) { case GH_Mesh mesh: - json.Add(mesh.Value.ToJSON(option)); + result = mesh.Value.ToJSON(option); break; case GH_Brep brep: - json.Add(brep.Value.ToJSON(option)); + result = brep.Value.ToJSON(option); break; case GH_Curve curve: - json.Add(curve.Value.ToJSON(option)); + result = curve.Value.ToJSON(option); break; case GH_Surface surface: - json.Add(surface.Value.ToJSON(option)); + result = surface.Value.ToJSON(option); break; case GH_SubD subd: - json.Add(subd.Value.ToJSON(option)); + result = subd.Value.ToJSON(option); break; default: - throw new Exception("Tunny doesn't handle this type of geometry"); + result = goo.ToString(); + break; } + return result; + } + + public Dictionary> GetAttributes() + { + var attrs = new Dictionary>(); + if (_attributes == null) + { + return attrs; + } + + foreach (string key in _attributes.Value.Keys) + { + if (key == "Geometry") + { + continue; + } + + var value = new List(); + var objList = _attributes.Value[key] as List; + foreach (object param in objList) + { + if (param is IGH_Goo goo) + { + value.Add(GHParamToString(goo)); + } + } + attrs.Add(key, value); + } + + return attrs; } } } diff --git a/Tunny/Util/ModelResult.cs b/Tunny/Util/ModelResult.cs index 0455d109..b045694e 100644 --- a/Tunny/Util/ModelResult.cs +++ b/Tunny/Util/ModelResult.cs @@ -5,8 +5,9 @@ namespace Tunny.Util public class ModelResult { public int Number { get; set; } + public double[] Objectives { get; set; } public string[] GeometryJson { get; set; } public Dictionary Variables { get; set; } - public double[] Objectives { get; set; } + public Dictionary> Attributes { get; set; } } } From 463e1c134edeaab2fa2034b20246d308390744f3 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 16 Jun 2022 20:49:52 +0900 Subject: [PATCH 12/79] Fix FishAttribute deserialize error --- ...Attribute.cs => ConstructFishAttribute.cs} | 12 ++--- Tunny/Component/Param_Fish.cs | 2 +- Tunny/Component/Param_FishAttribute.cs | 25 +++++----- Tunny/Component/TunnyComponent.cs | 2 +- Tunny/Type/GH_FishAttribute.cs | 46 +++++++++++++++++-- 5 files changed, 59 insertions(+), 28 deletions(-) rename Tunny/Component/{FishAttribute.cs => ConstructFishAttribute.cs} (92%) diff --git a/Tunny/Component/FishAttribute.cs b/Tunny/Component/ConstructFishAttribute.cs similarity index 92% rename from Tunny/Component/FishAttribute.cs rename to Tunny/Component/ConstructFishAttribute.cs index 6653e23f..f2e11bed 100644 --- a/Tunny/Component/FishAttribute.cs +++ b/Tunny/Component/ConstructFishAttribute.cs @@ -8,15 +8,15 @@ namespace Tunny.Component { - public class FishAttribute : GH_Component, IGH_VariableParameterComponent + public class ConstructFishAttribute : GH_Component, IGH_VariableParameterComponent { private readonly string _geomDescription = "Connect model geometries here. Not required. Large size models are not recommended as it affects the speed of analysis.\"Geometry\" is a special nickname that is visualized with the optimization results. Do not change it."; private readonly string _attrDescription = "Attributes to each trial. Attribute name will be the nickname of the input, so change it to any value."; public override GH_Exposure Exposure => GH_Exposure.secondary; - public FishAttribute() - : base("FishAttribute", "FAttribute", - "Description of component", + public ConstructFishAttribute() + : base("ConstructFishAttribute", "ConstrFA", + "Construct Fish Attribute.", "Params", "Tunny") { } @@ -33,8 +33,7 @@ protected override void RegisterInputParams(GH_InputParamManager pManager) protected override void RegisterOutputParams(GH_OutputParamManager pManager) { - pManager.AddParameter( - new Param_FishAttribute("Attributes", "Attrs", "Attributes to each Trial", "Params", "Tunny", GH_ParamAccess.list)); + pManager.AddParameter(new Param_FishAttribute(), "Attributes", "Attrs", "Attributes to each Trial", GH_ParamAccess.list); } protected override void SolveInstance(IGH_DataAccess DA) @@ -126,7 +125,6 @@ private void ParamChangedHandler(IGH_DocumentObject sender, GH_ObjectChangedEven } protected override System.Drawing.Bitmap Icon => null; - public override Guid ComponentGuid => new Guid("0E66E2E8-6A97-45E0-93DF-2251C3949B7D"); } } diff --git a/Tunny/Component/Param_Fish.cs b/Tunny/Component/Param_Fish.cs index 47bec70a..f435bb97 100644 --- a/Tunny/Component/Param_Fish.cs +++ b/Tunny/Component/Param_Fish.cs @@ -20,7 +20,7 @@ public Param_Fish() } protected override GH_Fish InstantiateT() => new GH_Fish(); - protected override GH_Fish PreferredCast(object data) => data is Fish cFish ? new GH_Fish(cFish) : (GH_Fish)null; + protected override GH_Fish PreferredCast(object data) => data is Fish fish ? new GH_Fish(fish) : (GH_Fish)null; protected override GH_GetterResult Prompt_Singular(ref GH_Fish value) => GH_GetterResult.success; protected override GH_GetterResult Prompt_Plural(ref List values) => GH_GetterResult.success; diff --git a/Tunny/Component/Param_FishAttribute.cs b/Tunny/Component/Param_FishAttribute.cs index 4dbd99da..8a215fe3 100644 --- a/Tunny/Component/Param_FishAttribute.cs +++ b/Tunny/Component/Param_FishAttribute.cs @@ -1,28 +1,25 @@ using System; +using System.Collections.Generic; +using System.Drawing; using Grasshopper.Kernel; namespace Tunny.Type { - public class Param_FishAttribute : GH_Param + public class Param_FishAttribute : GH_PersistentParam { - public override GH_Exposure Exposure => GH_Exposure.hidden; + public override GH_Exposure Exposure => GH_Exposure.primary; - public Param_FishAttribute(IGH_InstanceDescription tag) - : base(tag) - { - } - - public Param_FishAttribute(IGH_InstanceDescription tag, GH_ParamAccess access) - : base(tag, access) - { - } - - public Param_FishAttribute(string name, string nickname, string description, string category, string subcategory, GH_ParamAccess access) - : base(name, nickname, description, category, subcategory, access) + public Param_FishAttribute() + : base("FishAttributes", "FishAttrs", + "Attribute information to be added to each trial of optimization.", + "Params", "Tunny") { } + protected override GH_GetterResult Prompt_Singular(ref GH_FishAttribute value) => GH_GetterResult.success; + protected override GH_GetterResult Prompt_Plural(ref List values) => GH_GetterResult.success; + protected override Bitmap Icon => null; public override Guid ComponentGuid => new Guid("bfbd03eb-492d-4c57-8311-e7452d78a78e"); } } diff --git a/Tunny/Component/TunnyComponent.cs b/Tunny/Component/TunnyComponent.cs index 5b5f2693..237724b0 100644 --- a/Tunny/Component/TunnyComponent.cs +++ b/Tunny/Component/TunnyComponent.cs @@ -32,7 +32,7 @@ protected override void RegisterInputParams(GH_InputParamManager pManager) { pManager.AddNumberParameter("Variables", "Vars", "Connect variable number slider here.", GH_ParamAccess.tree); pManager.AddNumberParameter("Objectives", "Objs", "Connect objective number component here.", GH_ParamAccess.tree); - pManager.AddParameter(new Param_FishAttribute("Attributes", "Attrs", "Connect model attribute like some geometry or values here. Not required.", "Param", "Tunny", GH_ParamAccess.item)); + pManager.AddParameter(new Param_FishAttribute(), "Attributes", "Attrs", "Connect model attribute like some geometry or values here. Not required.", GH_ParamAccess.item); Params.Input[0].Optional = true; Params.Input[1].Optional = true; Params.Input[2].Optional = true; diff --git a/Tunny/Type/GH_FishAttribute.cs b/Tunny/Type/GH_FishAttribute.cs index e8d0c33f..691eb181 100644 --- a/Tunny/Type/GH_FishAttribute.cs +++ b/Tunny/Type/GH_FishAttribute.cs @@ -1,7 +1,12 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; using System.Text; +using GH_IO.Serialization; + using Grasshopper.Kernel.Types; namespace Tunny.Type @@ -21,16 +26,29 @@ public GH_FishAttribute(GH_Goo> other) : base(other) } public override bool IsValid => Value != null; - - public override string TypeName => "GH_FishAttributes"; - + public override string TypeName => "FishAttribute"; public override string TypeDescription => "DictionaryGoo for grasshopper"; - public override IGH_Goo Duplicate() { return new GH_FishAttribute() { Value = Value }; } + public override bool Read(GH_IReader reader) + { + string base64 = string.Empty; + if (reader.TryGetString("FishAttribute_bin", ref base64)) + { + Value = FromBase64(base64); + } + return base.Read(reader); + } + + public override bool Write(GH_IWriter writer) + { + writer.SetString("FishAttribute_bin", ToBase64(Value)); + return base.Write(writer); + } + public override string ToString() { var text = new StringBuilder(); @@ -72,5 +90,23 @@ public override bool CastTo(ref Q target) else return false; } + + private Dictionary FromBase64(string base64) + { + byte[] bytes = Convert.FromBase64String(base64); + using (var ms = new MemoryStream(bytes)) + { + return (Dictionary)new BinaryFormatter().Deserialize(ms); + } + } + + private string ToBase64(Dictionary value) + { + using (var ms = new MemoryStream()) + { + new BinaryFormatter().Serialize(ms, value); + return Convert.ToBase64String(ms.ToArray()); + } + } } } From 4f6744b783e13a8856d4a4302381c94cec2c8a5d Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 16 Jun 2022 21:40:07 +0900 Subject: [PATCH 13/79] Fix no attribute input error --- Tunny/Solver/Optuna.cs | 15 +++++++++------ Tunny/Solver/OptunaAlgorithm.cs | 4 ++-- Tunny/Type/GH_Fish.cs | 2 +- Tunny/Util/GrasshopperInOut.cs | 4 ++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Tunny/Solver/Optuna.cs b/Tunny/Solver/Optuna.cs index 7da93faf..8bc459fa 100644 --- a/Tunny/Solver/Optuna.cs +++ b/Tunny/Solver/Optuna.cs @@ -198,19 +198,22 @@ public ModelResult[] GetModelResult(int[] resultNum, string studyName) private static void ParseTrial(ICollection modelResult, dynamic trial) { - Dictionary variables = ParseVariables(trial); - Dictionary> attributes = ParseAttributes(trial); - modelResult.Add(new ModelResult() { Number = (int)trial.number, - GeometryJson = (string[])trial.user_attrs["Geometry"], - Attributes = attributes, - Variables = variables, + Variables = ParseVariables(trial), Objectives = (double[])trial.values, + GeometryJson = ParseGeometries(trial), + Attributes = ParseAttributes(trial), }); } + private static string[] ParseGeometries(dynamic trial) + { + string[] keys = (string[])trial.user_attrs.keys(); + return keys.Contains("Geometry") ? (string[])trial.user_attrs["Geometry"] : (new string[0]); + } + private static Dictionary ParseVariables(dynamic trial) { var variables = new Dictionary(); diff --git a/Tunny/Solver/OptunaAlgorithm.cs b/Tunny/Solver/OptunaAlgorithm.cs index 2816b748..b05ded8b 100644 --- a/Tunny/Solver/OptunaAlgorithm.cs +++ b/Tunny/Solver/OptunaAlgorithm.cs @@ -99,7 +99,7 @@ public void Solve() } } - SetTrialUserAttrToGeometry(result, trial); + SetTrialUserAttr(result, trial); try { study.tell(trial, result.ObjectiveValues.ToArray()); @@ -144,7 +144,7 @@ private static void SetStudyUserAttr(dynamic study, StringBuilder name) study.set_user_attr("tunny_version", Assembly.GetExecutingAssembly().GetName().Version.ToString(3)); } - private static void SetTrialUserAttrToGeometry(EvaluatedGHResult result, dynamic trial) + private static void SetTrialUserAttr(EvaluatedGHResult result, dynamic trial) { if (result.GeometryJson.Count != 0) { diff --git a/Tunny/Type/GH_Fish.cs b/Tunny/Type/GH_Fish.cs index 6ac06592..39792544 100644 --- a/Tunny/Type/GH_Fish.cs +++ b/Tunny/Type/GH_Fish.cs @@ -60,7 +60,7 @@ private void SetAttributes(StringBuilder sb) sb.AppendLine("------------------------------------"); sb.AppendLine("Attributes:"); sb.AppendLine("------------------------------------"); - bool hasGeometry = m_value.Geometries != null; + bool hasGeometry = m_value.Geometries.Count != 0; sb.AppendLine(" Include Geometry: " + hasGeometry); foreach (KeyValuePair> attr in m_value.Attributes) diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index 3357189e..bc1163c5 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -290,7 +290,7 @@ public List GetGeometryJson() var json = new List(); var option = new SerializationOptions(); - if (_attributes == null || !_attributes.Value.ContainsKey("Geometry")) + if (_attributes.Value == null || !_attributes.Value.ContainsKey("Geometry")) { return json; } @@ -343,7 +343,7 @@ private static string GHParamToString(IGH_Goo goo, SerializationOptions option) public Dictionary> GetAttributes() { var attrs = new Dictionary>(); - if (_attributes == null) + if (_attributes.Value == null) { return attrs; } From b002c0e0208bd0cacb9a24a1349eb01d443fe7c9 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 16 Jun 2022 21:46:41 +0900 Subject: [PATCH 14/79] Update FishMarket to ExpirePreview each solution --- Tunny/Component/FishMarket.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Tunny/Component/FishMarket.cs b/Tunny/Component/FishMarket.cs index 362fefca..55a434f0 100644 --- a/Tunny/Component/FishMarket.cs +++ b/Tunny/Component/FishMarket.cs @@ -80,6 +80,7 @@ protected override void SolveInstance(IGH_DataAccess DA) return; } DA.SetDataTree(0, ArrayedGeometries(fishObjects)); + ExpirePreview(true); } private GH_Structure ArrayedGeometries(IEnumerable fishObjects) From 5a656874ee9c8bb8722514c6104244812394b5de Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sat, 18 Jun 2022 11:58:14 +0900 Subject: [PATCH 15/79] Add Deconstruct Fish component --- Tunny/Component/ConstructFishAttribute.cs | 2 +- Tunny/Component/DeconstructFish.cs | 80 ++++++++++++++++++++ Tunny/Component/DecontstructFishAttribute.cs | 36 +++++++++ Tunny/Component/FishMarket.cs | 6 +- Tunny/Component/Param_FishAttribute.cs | 2 +- Tunny/Component/TunnyComponent.cs | 2 +- Tunny/Optimization/RestoreLoop.cs | 35 ++++----- Tunny/Solver/Optuna.cs | 11 --- Tunny/Type/Fish.cs | 15 +++- Tunny/Type/GH_Fish.cs | 43 +++++++++-- Tunny/Type/GH_FishAttribute.cs | 54 ++++++++++--- Tunny/Util/ModelResult.cs | 1 - 12 files changed, 232 insertions(+), 55 deletions(-) create mode 100644 Tunny/Component/DeconstructFish.cs create mode 100644 Tunny/Component/DecontstructFishAttribute.cs diff --git a/Tunny/Component/ConstructFishAttribute.cs b/Tunny/Component/ConstructFishAttribute.cs index f2e11bed..f6a12a5d 100644 --- a/Tunny/Component/ConstructFishAttribute.cs +++ b/Tunny/Component/ConstructFishAttribute.cs @@ -15,7 +15,7 @@ public class ConstructFishAttribute : GH_Component, IGH_VariableParameterCompone public override GH_Exposure Exposure => GH_Exposure.secondary; public ConstructFishAttribute() - : base("ConstructFishAttribute", "ConstrFA", + : base("Construct Fish Attribute", "ConstrFA", "Construct Fish Attribute.", "Params", "Tunny") { diff --git a/Tunny/Component/DeconstructFish.cs b/Tunny/Component/DeconstructFish.cs new file mode 100644 index 00000000..99f61bf7 --- /dev/null +++ b/Tunny/Component/DeconstructFish.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Grasshopper.Kernel; +using Grasshopper.Kernel.Data; +using Grasshopper.Kernel.Types; + +using Rhino.Geometry; + +using Tunny.Type; +using Tunny.Util; + +namespace Tunny.Component +{ + public class DeconstructFish : GH_Component + { + public override GH_Exposure Exposure => GH_Exposure.secondary; + + public DeconstructFish() + : base("Deconstruct Fish", "DeconF", + "Deconstruct Fish.", + "Params", "Tunny") + { + } + + protected override void RegisterInputParams(GH_InputParamManager pManager) + { + pManager.AddParameter(new Param_Fish(), "Fishes", "Fishes", "Fishes", GH_ParamAccess.list); + } + + protected override void RegisterOutputParams(GH_OutputParamManager pManager) + { + pManager.AddNumberParameter("Variables", "Vars", "Variables", GH_ParamAccess.tree); + pManager.AddNumberParameter("Objectives", "Objs", "Objectives", GH_ParamAccess.tree); + pManager.AddParameter(new Param_FishAttribute(), "Attributes", "Attrs", "Attributes", GH_ParamAccess.tree); + } + + protected override void SolveInstance(IGH_DataAccess DA) + { + var fishObjects = new List(); + if (!DA.GetDataList(0, fishObjects)) { return; } + + var fishes = fishObjects.Select(x => (GH_Fish)x).ToList(); + + var variables = new GH_Structure(); + var objectives = new GH_Structure(); + var attributes = new GH_Structure(); + + foreach (GH_Fish fish in fishes) + { + Fish value = fish.Value; + var path = new GH_Path(0, value.ModelNumber); + foreach (KeyValuePair variable in value.Variables) + { + variables.Append(new GH_Number(variable.Value), path); + } + foreach (KeyValuePair objective in value.Objectives) + { + objectives.Append(new GH_Number(objective.Value), path); + } + foreach (KeyValuePair attribute in value.Attributes) + { + var attr = new GH_FishAttribute(new Dictionary() + { + { attribute.Key, attribute.Value } + }); + attributes.Append(attr, path); + } + } + + DA.SetDataTree(0, variables); + DA.SetDataTree(1, objectives); + DA.SetDataTree(2, attributes); + } + + protected override System.Drawing.Bitmap Icon => null; + public override Guid ComponentGuid => new Guid("1D0EA5A9-9499-4D70-8505-6AB4B05889F4"); + } +} diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs new file mode 100644 index 00000000..1595a6b2 --- /dev/null +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -0,0 +1,36 @@ +using System; + +using Grasshopper.Kernel; + +using Tunny.Type; + +namespace Tunny.Component +{ + public class DeconstructFishAttribute : GH_Component + { + public override GH_Exposure Exposure => GH_Exposure.secondary; + + public DeconstructFishAttribute() + : base("Deconstruct Fish Attribute", "DeconFA", + "Deconstruct Fish Attribute.", + "Params", "Tunny") + { + } + + protected override void RegisterInputParams(GH_InputParamManager pManager) + { + pManager.AddParameter(new Param_FishAttribute(), "Attributes", "Attrs", "Attributes to each Trial", GH_ParamAccess.list); + } + + protected override void RegisterOutputParams(GH_OutputParamManager pManager) + { + } + + protected override void SolveInstance(IGH_DataAccess DA) + { + } + + protected override System.Drawing.Bitmap Icon => null; + public override Guid ComponentGuid => new Guid("D3B7B64A-71BA-41BE-9B2E-B0F459886B36"); + } +} diff --git a/Tunny/Component/FishMarket.cs b/Tunny/Component/FishMarket.cs index 55a434f0..04e07c1f 100644 --- a/Tunny/Component/FishMarket.cs +++ b/Tunny/Component/FishMarket.cs @@ -22,10 +22,10 @@ public class FishMarket : GH_Component private double _size = 1; private Settings _settings = new Settings(); - public override GH_Exposure Exposure => GH_Exposure.secondary; + public override GH_Exposure Exposure => GH_Exposure.quarternary; public FishMarket() - : base("FishMarket", "FMarket", + : base("Fish Market", "FMarket", "A place to lay out the solutions we caught.", "Params", "Tunny") { @@ -88,7 +88,7 @@ private GH_Structure ArrayedGeometries(IEnumerable fis int countY = 0; var arrayedGeometries = new GH_Structure(); _fishes = fishObjects.Select(x => (GH_Fish)x).ToList(); - var fishGeometries = _fishes.Select(x => x.Value.Geometries).ToList(); + var fishGeometries = _fishes.Select(x => x.Value.GetGeometries()).ToList(); while (true) { diff --git a/Tunny/Component/Param_FishAttribute.cs b/Tunny/Component/Param_FishAttribute.cs index 8a215fe3..0e17e3c9 100644 --- a/Tunny/Component/Param_FishAttribute.cs +++ b/Tunny/Component/Param_FishAttribute.cs @@ -11,7 +11,7 @@ public class Param_FishAttribute : GH_PersistentParam public override GH_Exposure Exposure => GH_Exposure.primary; public Param_FishAttribute() - : base("FishAttributes", "FishAttrs", + : base("Fish Attributes", "FishAttrs", "Attribute information to be added to each trial of optimization.", "Params", "Tunny") { diff --git a/Tunny/Component/TunnyComponent.cs b/Tunny/Component/TunnyComponent.cs index 237724b0..5455c04e 100644 --- a/Tunny/Component/TunnyComponent.cs +++ b/Tunny/Component/TunnyComponent.cs @@ -19,7 +19,7 @@ public partial class TunnyComponent : GH_Component internal GrasshopperInOut GhInOut; internal Fish[] Fishes; - public override GH_Exposure Exposure => GH_Exposure.secondary; + public override GH_Exposure Exposure => GH_Exposure.tertiary; public TunnyComponent() : base("Tunny", "Tunny", diff --git a/Tunny/Optimization/RestoreLoop.cs b/Tunny/Optimization/RestoreLoop.cs index 16f5f491..e71f2a0d 100644 --- a/Tunny/Optimization/RestoreLoop.cs +++ b/Tunny/Optimization/RestoreLoop.cs @@ -76,7 +76,6 @@ private static void SetResultToFish(ICollection fishes, ModelResult model, ModelNumber = model.Number, Variables = SetVariables(model, nickname), Objectives = SetObjectives(model), - Geometries = SetGeometries(model), Attributes = SetAttributes(model), }); } @@ -105,28 +104,24 @@ private static Dictionary SetObjectives(ModelResult model) return objectives; } - private static List SetGeometries(ModelResult model) + private static Dictionary SetAttributes(ModelResult model) { - var geometries = new List(); - if (model.GeometryJson.Length == 0) - { - return geometries; - } - - foreach (string json in model.GeometryJson) - { - var geometry = Rhino.Runtime.CommonObject.FromJSON(json) as GeometryBase; - geometries.Add(geometry); - } - return geometries; - } - - private static Dictionary> SetAttributes(ModelResult model) - { - var attribute = new Dictionary>(); + var attribute = new Dictionary(); foreach (KeyValuePair> attr in model.Attributes) { - attribute.Add(attr.Key, attr.Value); + if (attr.Key == "Geometry") + { + var geometries = new List(); + foreach (string json in attr.Value) + { + geometries.Add(Rhino.Runtime.CommonObject.FromJSON(json) as GeometryBase); + } + attribute.Add(attr.Key, geometries); + } + else + { + attribute.Add(attr.Key, attr.Value); + } } return attribute; } diff --git a/Tunny/Solver/Optuna.cs b/Tunny/Solver/Optuna.cs index 8bc459fa..dca35728 100644 --- a/Tunny/Solver/Optuna.cs +++ b/Tunny/Solver/Optuna.cs @@ -203,17 +203,10 @@ private static void ParseTrial(ICollection modelResult, dynamic tri Number = (int)trial.number, Variables = ParseVariables(trial), Objectives = (double[])trial.values, - GeometryJson = ParseGeometries(trial), Attributes = ParseAttributes(trial), }); } - private static string[] ParseGeometries(dynamic trial) - { - string[] keys = (string[])trial.user_attrs.keys(); - return keys.Contains("Geometry") ? (string[])trial.user_attrs["Geometry"] : (new string[0]); - } - private static Dictionary ParseVariables(dynamic trial) { var variables = new Dictionary(); @@ -233,10 +226,6 @@ private static Dictionary> ParseAttributes(dynamic trial) string[] keys = (string[])trial.user_attrs.keys(); for (int i = 0; i < keys.Length; i++) { - if (keys[i] == "Geometry") - { - continue; - } string[] values = (string[])trial.user_attrs[keys[i]]; attributes.Add(keys[i], values.ToList()); } diff --git a/Tunny/Type/Fish.cs b/Tunny/Type/Fish.cs index e10c69cf..0fb57cda 100644 --- a/Tunny/Type/Fish.cs +++ b/Tunny/Type/Fish.cs @@ -13,10 +13,21 @@ namespace Tunny.Type public class Fish { public int ModelNumber; - public List Geometries; public Dictionary Variables; public Dictionary Objectives; - public Dictionary> Attributes; + public Dictionary Attributes; + + public List GetGeometries() + { + var geometries = new List(); + if (Attributes == null) + { + return geometries; + } + + bool hasGeometry = Attributes.ContainsKey("Geometry"); + return hasGeometry ? Attributes["Geometry"] as List : geometries; + } public string SerializeToJson() { diff --git a/Tunny/Type/GH_Fish.cs b/Tunny/Type/GH_Fish.cs index 39792544..c3ef43ff 100644 --- a/Tunny/Type/GH_Fish.cs +++ b/Tunny/Type/GH_Fish.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Text; @@ -5,6 +6,8 @@ using Grasshopper.Kernel.Types; +using Rhino.Geometry; + namespace Tunny.Type { public class GH_Fish : GH_Goo @@ -60,20 +63,50 @@ private void SetAttributes(StringBuilder sb) sb.AppendLine("------------------------------------"); sb.AppendLine("Attributes:"); sb.AppendLine("------------------------------------"); - bool hasGeometry = m_value.Geometries.Count != 0; - sb.AppendLine(" Include Geometry: " + hasGeometry); - foreach (KeyValuePair> attr in m_value.Attributes) + foreach (KeyValuePair attr in m_value.Attributes) { string valueStrings = string.Empty; - foreach (string val in attr.Value) + if (attr.Key == "Geometry") + { + List geometries = Value.GetGeometries(); + foreach (GeometryBase val in geometries) + { + string geomString = GeometryBaseToGoo(val); + valueStrings += "\n " + geomString; + } + } + else { - valueStrings += val + ", "; + var values = attr.Value as List; + foreach (string val in values) + { + valueStrings += val + ", "; + } } sb.AppendLine(" " + attr.Key + ": " + valueStrings); } } + private string GeometryBaseToGoo(GeometryBase geometryBase) + { + switch (geometryBase) + { + case Mesh mesh: + return new GH_Mesh(mesh).ToString(); + case Curve curve: + return new GH_Curve(curve).ToString(); + case Brep brep: + return new GH_Brep(brep).ToString(); + case Surface surface: + return new GH_Surface(surface).ToString(); + case SubD subD: + return new GH_SubD(subD).ToString(); + default: + throw new Exception("Tunny doesn't handle this type of geometry"); + } + } + private void SetObjectives(StringBuilder sb) { sb.AppendLine("------------------------------------"); diff --git a/Tunny/Type/GH_FishAttribute.cs b/Tunny/Type/GH_FishAttribute.cs index 691eb181..d48b50fa 100644 --- a/Tunny/Type/GH_FishAttribute.cs +++ b/Tunny/Type/GH_FishAttribute.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Formatters.Binary; @@ -9,6 +8,8 @@ using Grasshopper.Kernel.Types; +using Rhino.Geometry; + namespace Tunny.Type { public class GH_FishAttribute : GH_Goo> @@ -51,21 +52,54 @@ public override bool Write(GH_IWriter writer) public override string ToString() { - var text = new StringBuilder(); - foreach (string key in Value.Keys) + var sb = new StringBuilder(); + foreach (KeyValuePair attr in Value) { - text.AppendLine($"{key}:"); - if (Value[key] is IEnumerable iEnum) + string valueStrings = string.Empty; + switch (attr.Value) { - foreach (object item in iEnum) - text.AppendLine($" {item}"); + case List obj: + valueStrings = string.Join(", ", obj); + break; + case List str: + valueStrings = string.Join(", ", str); + break; + case List geo: + valueStrings = string.Join(", ", GeometryBaseToGoo(geo)); + break; } - else + sb.AppendLine(" " + attr.Key + ": " + valueStrings); + } + return sb.ToString(); + } + + private List GeometryBaseToGoo(List geometryBase) + { + var list = new List(); + foreach (GeometryBase geo in geometryBase) + { + switch (geo) { - text.AppendLine($" {Value[key]}"); + case Mesh mesh: + list.Add(new GH_Mesh(mesh).ToString()); + break; + case Curve curve: + list.Add(new GH_Curve(curve).ToString()); + break; + case Brep brep: + list.Add(new GH_Brep(brep).ToString()); + break; + case Surface surface: + list.Add(new GH_Surface(surface).ToString()); + break; + case SubD subD: + list.Add(new GH_SubD(subD).ToString()); + break; + default: + throw new Exception("Tunny doesn't handle this type of geometry"); } } - return text.ToString(); + return list; } public override bool CastFrom(object source) diff --git a/Tunny/Util/ModelResult.cs b/Tunny/Util/ModelResult.cs index b045694e..66ed0e30 100644 --- a/Tunny/Util/ModelResult.cs +++ b/Tunny/Util/ModelResult.cs @@ -6,7 +6,6 @@ public class ModelResult { public int Number { get; set; } public double[] Objectives { get; set; } - public string[] GeometryJson { get; set; } public Dictionary Variables { get; set; } public Dictionary> Attributes { get; set; } } From 333877f03d30143870b7915307d22d71dda6c103 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 19 Jun 2022 13:20:47 +0900 Subject: [PATCH 16/79] Fix Deconstruct Fish to not output attr for tree --- Tunny/Component/DeconstructFish.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Tunny/Component/DeconstructFish.cs b/Tunny/Component/DeconstructFish.cs index 99f61bf7..12cabd34 100644 --- a/Tunny/Component/DeconstructFish.cs +++ b/Tunny/Component/DeconstructFish.cs @@ -6,10 +6,7 @@ using Grasshopper.Kernel.Data; using Grasshopper.Kernel.Types; -using Rhino.Geometry; - using Tunny.Type; -using Tunny.Util; namespace Tunny.Component { @@ -59,14 +56,13 @@ protected override void SolveInstance(IGH_DataAccess DA) { objectives.Append(new GH_Number(objective.Value), path); } + + var attr = new GH_FishAttribute(new Dictionary()); foreach (KeyValuePair attribute in value.Attributes) { - var attr = new GH_FishAttribute(new Dictionary() - { - { attribute.Key, attribute.Value } - }); - attributes.Append(attr, path); + attr.Value.Add(attribute.Key, attribute.Value); } + attributes.Append(attr, path); } DA.SetDataTree(0, variables); From 892f31ac7213a051f2ae9618cdba859261b2b0e3 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 19 Jun 2022 18:51:47 +0900 Subject: [PATCH 17/79] Update FishMarket to deepcopy geometry --- Tunny/Component/FishMarket.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Tunny/Component/FishMarket.cs b/Tunny/Component/FishMarket.cs index 04e07c1f..29183520 100644 --- a/Tunny/Component/FishMarket.cs +++ b/Tunny/Component/FishMarket.cs @@ -88,10 +88,10 @@ private GH_Structure ArrayedGeometries(IEnumerable fis int countY = 0; var arrayedGeometries = new GH_Structure(); _fishes = fishObjects.Select(x => (GH_Fish)x).ToList(); - var fishGeometries = _fishes.Select(x => x.Value.GetGeometries()).ToList(); while (true) { + var fishGeometries = _fishes.Select(x => x.Value.GetGeometries()).ToList(); bool remainGeometry = SetGeometryToResultArray(countY, arrayedGeometries, fishGeometries); if (!remainGeometry) { @@ -119,9 +119,10 @@ private bool SetGeometryToResultArray(int countY, GH_Structure Point3d modelMinPt = GetUnionBoundingBoxMinPt(fishGeometries[index]); foreach (GeometryBase geometry in fishGeometries[index]) { - geometry.Rotate(Vector3d.VectorAngle(Vector3d.XAxis, _settings.Plane.XAxis), Vector3d.ZAxis, modelMinPt); - geometry.Translate(xVec + yVec + new Vector3d(_settings.Plane.Origin) - new Vector3d(modelMinPt)); - arrayedGeometries.Append(CreateGeometricGoo(geometry), new GH_Path(0, _fishes[index].Value.ModelNumber)); + GeometryBase duplicateGeometry = geometry.Duplicate(); + duplicateGeometry.Rotate(Vector3d.VectorAngle(Vector3d.XAxis, _settings.Plane.XAxis), Vector3d.ZAxis, modelMinPt); + duplicateGeometry.Translate(xVec + yVec + new Vector3d(_settings.Plane.Origin) - new Vector3d(modelMinPt)); + arrayedGeometries.Append(CreateGeometricGoo(duplicateGeometry), new GH_Path(0, _fishes[index].Value.ModelNumber)); } _tagPlanes.Add(new Plane(modelMinPt - _settings.Plane.YAxis * 2.5 * _size, _settings.Plane.XAxis, _settings.Plane.YAxis)); } From a8d42f2768e39aec41544680265928cf7db67102 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 19 Jun 2022 18:55:01 +0900 Subject: [PATCH 18/79] Add Deconstruct FishAttribute component --- Tunny/Component/ConstructFishAttribute.cs | 1 - Tunny/Component/DecontstructFishAttribute.cs | 125 ++++++++++++++++++- 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/Tunny/Component/ConstructFishAttribute.cs b/Tunny/Component/ConstructFishAttribute.cs index f6a12a5d..bee89df2 100644 --- a/Tunny/Component/ConstructFishAttribute.cs +++ b/Tunny/Component/ConstructFishAttribute.cs @@ -39,7 +39,6 @@ protected override void RegisterOutputParams(GH_OutputParamManager pManager) protected override void SolveInstance(IGH_DataAccess DA) { int paramCount = Params.Input.Count; - var dict = new Dictionary(); for (int i = 0; i < paramCount; i++) diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs index 1595a6b2..4721c1e9 100644 --- a/Tunny/Component/DecontstructFishAttribute.cs +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -1,13 +1,22 @@ using System; +using System.Collections.Generic; +using System.Linq; using Grasshopper.Kernel; +using Grasshopper.Kernel.Data; +using Grasshopper.Kernel.Parameters; +using Grasshopper.Kernel.Types; + +using Rhino.Geometry; using Tunny.Type; namespace Tunny.Component { - public class DeconstructFishAttribute : GH_Component + public class DeconstructFishAttribute : GH_Component, IGH_VariableParameterComponent { + private int _outputCount = 0; + private List _keys = new List(); public override GH_Exposure Exposure => GH_Exposure.secondary; public DeconstructFishAttribute() @@ -19,7 +28,7 @@ public DeconstructFishAttribute() protected override void RegisterInputParams(GH_InputParamManager pManager) { - pManager.AddParameter(new Param_FishAttribute(), "Attributes", "Attrs", "Attributes to each Trial", GH_ParamAccess.list); + pManager.AddParameter(new Param_FishAttribute(), "Attributes", "Attrs", "Attributes to each Trial", GH_ParamAccess.tree); } protected override void RegisterOutputParams(GH_OutputParamManager pManager) @@ -28,6 +37,118 @@ protected override void RegisterOutputParams(GH_OutputParamManager pManager) protected override void SolveInstance(IGH_DataAccess DA) { + if (!DA.GetDataTree(0, out GH_Structure fishAttributeStructure)) { return; } + + var nicknames = Params.Output.Select(x => x.NickName).ToList(); + var outputValues = new Dictionary>(); + for (int i = 0; i < fishAttributeStructure.PathCount; i++) + { + GH_Path path = fishAttributeStructure.Paths[i]; + Dictionary value = fishAttributeStructure.Branches[i][0].Value; + + //FIXME: This process should be done once, but foreach executes it every time. + _keys = value.Keys.ToList(); + + foreach (KeyValuePair pair in value) + { + if (nicknames.Contains(pair.Key)) + { + if (!outputValues.ContainsKey(pair.Key)) + { + outputValues.Add(pair.Key, new GH_Structure()); + } + + List goo = GetGooFromAttributeObject(pair.Value); + foreach (IGH_Goo g in goo) + { + outputValues[pair.Key].Append(g, path); + } + } + } + } + + if (Params.Output.Count == 0) + { + return; + } + + for (int i = 0; i < Params.Output.Count; i++) + { + foreach (string nickName in nicknames) + { + if (Params.Output[i].NickName == nickName) + { + DA.SetDataTree(i, outputValues[nickName]); + } + } + } + } + + private List GetGooFromAttributeObject(object value) + { + switch (value) + { + case List str: + return str.Select(s => new GH_String(s)).Cast().ToList(); + case List geom: + return geom.Select(g => CreateGeometricGoo(g)).Cast().ToList(); + default: + return new List(); + } + } + + private static IGH_GeometricGoo CreateGeometricGoo(GeometryBase geometry) + { + switch (geometry) + { + case Mesh mesh: + return new GH_Mesh(mesh); + case Curve curve: + return new GH_Curve(curve); + case Brep brep: + return new GH_Brep(brep); + case Surface surface: + return new GH_Surface(surface); + case SubD subD: + return new GH_SubD(subD); + default: + throw new Exception("Tunny doesn't handle this type of geometry"); + } + } + + public bool CanInsertParameter(GH_ParameterSide side, int index) => side != GH_ParameterSide.Input && (Params.Output.Count == 0 || index == Params.Output.Count); + + public bool CanRemoveParameter(GH_ParameterSide side, int index) => (side != GH_ParameterSide.Input) && (index == Params.Output.Count - 1); + + public IGH_Param CreateParameter(GH_ParameterSide side, int index) + { + if (side == GH_ParameterSide.Output && _keys.Count() > index) + { + var p = new Param_GenericObject(); + p.Name = p.NickName = _keys[index]; + p.MutableNickName = false; + p.Access = GH_ParamAccess.tree; + return p; + } + else + { + return null; + } + } + + public bool DestroyParameter(GH_ParameterSide side, int index) + { + return true; + } + + public void VariableParameterMaintenance() + { + if (_outputCount != Params.Output.Count) + { + _outputCount = Params.Output.Count; + _outputNames = Params.Output.Select(x => x.NickName).ToList(); + ExpireSolution(true); + } } protected override System.Drawing.Bitmap Icon => null; From 2990fac94c6169a326a8568b56314eea5caa71e2 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 19 Jun 2022 18:56:37 +0900 Subject: [PATCH 19/79] Fix DeconFAttr build error --- Tunny/Component/DecontstructFishAttribute.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs index 4721c1e9..46830dfd 100644 --- a/Tunny/Component/DecontstructFishAttribute.cs +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -146,7 +146,6 @@ public void VariableParameterMaintenance() if (_outputCount != Params.Output.Count) { _outputCount = Params.Output.Count; - _outputNames = Params.Output.Select(x => x.NickName).ToList(); ExpireSolution(true); } } From 8b5ef9501199e0e4efa89cf6dfb7b7e9dc98532f Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 19 Jun 2022 19:19:07 +0900 Subject: [PATCH 20/79] Add FAttribute to match output menu button --- Tunny/Component/DecontstructFishAttribute.cs | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs index 46830dfd..8a9a0888 100644 --- a/Tunny/Component/DecontstructFishAttribute.cs +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Windows.Forms; using Grasshopper.Kernel; using Grasshopper.Kernel.Data; @@ -150,6 +151,30 @@ public void VariableParameterMaintenance() } } + protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu) => Menu_AppendItem(menu, "Match outputs", new EventHandler(Menu_AutoCreateTree_Clicked)); + + private void Menu_AutoCreateTree_Clicked(object sender, EventArgs e) + { + if (Params.Output.Count == _keys.Count) + { + return; + } + RecordUndoEvent("FishAttribute Match"); + if (Params.Output.Count < _keys.Count) + { + while (Params.Output.Count < _keys.Count) + Params.RegisterOutputParam(CreateParameter(GH_ParameterSide.Output, Params.Output.Count)); + } + else if (Params.Output.Count > _keys.Count) + { + while (Params.Output.Count > _keys.Count) + Params.UnregisterOutputParameter(Params.Output[Params.Output.Count - 1]); + } + Params.OnParametersChanged(); + VariableParameterMaintenance(); + ExpireSolution(true); + } + protected override System.Drawing.Bitmap Icon => null; public override Guid ComponentGuid => new Guid("D3B7B64A-71BA-41BE-9B2E-B0F459886B36"); } From bda05ccb9648ef8501b1ea36bd0c2bf12d2a9068 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 19 Jun 2022 20:22:05 +0900 Subject: [PATCH 21/79] Add check nickname duplication --- Tunny/Component/ConstructFishAttribute.cs | 30 ++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/Tunny/Component/ConstructFishAttribute.cs b/Tunny/Component/ConstructFishAttribute.cs index bee89df2..03128915 100644 --- a/Tunny/Component/ConstructFishAttribute.cs +++ b/Tunny/Component/ConstructFishAttribute.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Grasshopper.Kernel; using Grasshopper.Kernel.Parameters; @@ -41,6 +42,12 @@ protected override void SolveInstance(IGH_DataAccess DA) int paramCount = Params.Input.Count; var dict = new Dictionary(); + if (CheckIsNicknameDuplicated()) + { + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Attribute nickname must be unique."); + return; + } + for (int i = 0; i < paramCount; i++) { var list = new List(); @@ -55,16 +62,26 @@ protected override void SolveInstance(IGH_DataAccess DA) DA.SetData(0, dict); } - public bool CanInsertParameter(GH_ParameterSide side, int index) + //FIXME: Should be modified to capture and check for change events. + private bool CheckIsNicknameDuplicated() { - return side == GH_ParameterSide.Input; - } + var nicknames = Params.Input.Select(x => x.NickName).ToList(); + var hashSet = new HashSet(); - public bool CanRemoveParameter(GH_ParameterSide side, int index) - { - return side == GH_ParameterSide.Input; + foreach (string nickname in nicknames) + { + if (hashSet.Add(nickname) == false) + { + return true; + } + } + return false; } + public bool CanInsertParameter(GH_ParameterSide side, int index) => side != GH_ParameterSide.Output && (Params.Input.Count == 0 || index != 0); + + public bool CanRemoveParameter(GH_ParameterSide side, int index) => side != GH_ParameterSide.Output && index != 0; + public IGH_Param CreateParameter(GH_ParameterSide side, int index) { if (side == GH_ParameterSide.Input) @@ -75,6 +92,7 @@ public IGH_Param CreateParameter(GH_ParameterSide side, int index) p.Name = p.NickName = "Geometry"; p.Description = _geomDescription; p.Access = GH_ParamAccess.list; + p.MutableNickName = false; p.Optional = true; return p; } From b2a36d4315cd4eaa2f32607181a42e0eae9aa417 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 19 Jun 2022 20:26:13 +0900 Subject: [PATCH 22/79] Rename Fish Attribute"s" to Fish Attribute --- Tunny/Component/Param_FishAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tunny/Component/Param_FishAttribute.cs b/Tunny/Component/Param_FishAttribute.cs index 0e17e3c9..ce98122b 100644 --- a/Tunny/Component/Param_FishAttribute.cs +++ b/Tunny/Component/Param_FishAttribute.cs @@ -11,7 +11,7 @@ public class Param_FishAttribute : GH_PersistentParam public override GH_Exposure Exposure => GH_Exposure.primary; public Param_FishAttribute() - : base("Fish Attributes", "FishAttrs", + : base("Fish Attribute", "FishAttr", "Attribute information to be added to each trial of optimization.", "Params", "Tunny") { From c338809b729fa47a5d1e7d7b415cb3be10f6df11 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 19 Jun 2022 20:34:34 +0900 Subject: [PATCH 23/79] Update CHANGELOG.md --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbbc9cfe..1ff926de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,19 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release ### Added - Component CI build to get easily development build. + - If you want to dogfooding, go right ahead! +- Param_FishAttribute component +- Construct Fish Attribute component + - Component that creates attribute information to be attached to each trial of optimization +- Deconstruct Fish component + - Component that separates optimization results into Variables, Objectives, and Attributes +- Deconstruct Fish Attribute component + - Components that output each attribute + +### Changed + +- The output of the Tunny component is made into Fish, a type that summarizes the results. +- The Geometry input of the Tunny component has been changed to Attribute to allow more attribute information to be handled. ### Fix From 1bcd4cfaa5b41b80dac72ab50e1d72a402003659 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Tue, 21 Jun 2022 10:11:31 +0900 Subject: [PATCH 24/79] Fix Attribute not update while optimization --- Tunny/Util/GrasshopperInOut.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index bc1163c5..5e95fefa 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -240,9 +240,7 @@ private decimal GetNormalisedGenePoolValue(decimal unnormalized, GalapagosGeneLi private void Recalculate() { while (_document.SolutionState != GH_ProcessStep.PreProcess || _document.SolutionDepth != 0) { } - _document.NewSolution(true); - while (_document.SolutionState != GH_ProcessStep.PostProcess || _document.SolutionDepth != 0) { } } @@ -250,6 +248,8 @@ public void NewSolution(IList parameters) { SetSliderValues(parameters); Recalculate(); + SetObjectives(); + SetAttributes(); } public List GetObjectiveValues() From 17fea74ac9cba54ac9a91384ace891373fe5dc63 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 22 Jun 2022 18:59:37 +0900 Subject: [PATCH 25/79] Fix roslyn analyzer recommened warning --- .editorconfig | 3 ++ Tunny/Component/DecontstructFishAttribute.cs | 8 +++--- Tunny/Component/FishMarket.cs | 2 +- Tunny/Component/TunnyComponent.cs | 18 ++++++++++-- Tunny/Settings/CmaEs.cs | 4 +-- Tunny/Settings/Optimize.cs | 2 +- Tunny/Settings/Tpe.cs | 8 +++--- Tunny/Settings/TunnySettings.cs | 2 +- Tunny/Solver/Optuna.cs | 8 +++--- Tunny/Solver/OptunaAlgorithm.cs | 4 +-- Tunny/Tunny.csproj | 29 ++++++++++++++++++++ Tunny/Type/Fish.cs | 8 +++--- Tunny/Type/GH_Fish.cs | 4 +-- Tunny/Type/GH_FishAttribute.cs | 16 +++++------ Tunny/Util/GrasshopperInOut.cs | 14 +++++----- Tunny/Util/PythonInstaller.cs | 2 +- Tunny/Util/Variables.cs | 12 ++++---- 17 files changed, 95 insertions(+), 49 deletions(-) diff --git a/.editorconfig b/.editorconfig index 7357d95d..fec9b9c6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -362,3 +362,6 @@ dotnet_naming_style.s_camelcase.required_suffix = dotnet_naming_style.s_camelcase.word_separator = dotnet_naming_style.s_camelcase.capitalization = camel_case +# Additions +dotnet_code_quality.ca1711.allowed_suffixes = Attribute +dotnet_diagnostic.CA1707.severity = none diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs index 8a9a0888..c0ebd102 100644 --- a/Tunny/Component/DecontstructFishAttribute.cs +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -16,7 +16,7 @@ namespace Tunny.Component { public class DeconstructFishAttribute : GH_Component, IGH_VariableParameterComponent { - private int _outputCount = 0; + private int _outputCount; private List _keys = new List(); public override GH_Exposure Exposure => GH_Exposure.secondary; @@ -85,7 +85,7 @@ protected override void SolveInstance(IGH_DataAccess DA) } } - private List GetGooFromAttributeObject(object value) + private static List GetGooFromAttributeObject(object value) { switch (value) { @@ -113,7 +113,7 @@ private static IGH_GeometricGoo CreateGeometricGoo(GeometryBase geometry) case SubD subD: return new GH_SubD(subD); default: - throw new Exception("Tunny doesn't handle this type of geometry"); + throw new ArgumentException("Tunny doesn't handle this type of geometry"); } } @@ -123,7 +123,7 @@ private static IGH_GeometricGoo CreateGeometricGoo(GeometryBase geometry) public IGH_Param CreateParameter(GH_ParameterSide side, int index) { - if (side == GH_ParameterSide.Output && _keys.Count() > index) + if (side == GH_ParameterSide.Output && _keys.Count > index) { var p = new Param_GenericObject(); p.Name = p.NickName = _keys[index]; diff --git a/Tunny/Component/FishMarket.cs b/Tunny/Component/FishMarket.cs index 29183520..489c9d36 100644 --- a/Tunny/Component/FishMarket.cs +++ b/Tunny/Component/FishMarket.cs @@ -145,7 +145,7 @@ private static IGH_GeometricGoo CreateGeometricGoo(GeometryBase geometry) case SubD subD: return new GH_SubD(subD); default: - throw new Exception("Tunny doesn't handle this type of geometry"); + throw new ArgumentException("Tunny doesn't handle this type of geometry"); } } diff --git a/Tunny/Component/TunnyComponent.cs b/Tunny/Component/TunnyComponent.cs index 5455c04e..99e455be 100644 --- a/Tunny/Component/TunnyComponent.cs +++ b/Tunny/Component/TunnyComponent.cs @@ -13,7 +13,7 @@ namespace Tunny.Component { - public partial class TunnyComponent : GH_Component + public partial class TunnyComponent : GH_Component, IDisposable { internal OptimizationWindow OptimizationWindow; internal GrasshopperInOut GhInOut; @@ -53,6 +53,21 @@ public void GhInOutInstantiate() GhInOut = new GrasshopperInOut(this); } + public override void RemovedFromDocument(GH_Document document) + { + base.RemovedFromDocument(document); + OptimizationWindow.Dispose(); + } + + public void Dispose() + { + if (OptimizationWindow != null) + { + OptimizationWindow.Dispose(); + } + GC.SuppressFinalize(this); + } + public override void CreateAttributes() { m_attributes = new TunnyAttributes(this); @@ -76,7 +91,6 @@ private void ShowOptimizationWindow() } protected override Bitmap Icon => Resource.TunnyIcon; - public override Guid ComponentGuid => new Guid("701d2c47-1440-4d09-951c-386200e29b28"); } } diff --git a/Tunny/Settings/CmaEs.cs b/Tunny/Settings/CmaEs.cs index 3136afd8..11e086ae 100644 --- a/Tunny/Settings/CmaEs.cs +++ b/Tunny/Settings/CmaEs.cs @@ -9,9 +9,9 @@ public class CmaEs public double? Sigma0 { get; set; } public int NStartupTrials { get; set; } = 1; public bool WarnIndependentSampling { get; set; } = true; - public bool ConsiderPrunedTrials { get; set; } = false; + public bool ConsiderPrunedTrials { get; set; } public string RestartStrategy { get; set; } public int IncPopsize { get; set; } = 2; - public bool UseSeparableCma { get; set; } = false; + public bool UseSeparableCma { get; set; } } } diff --git a/Tunny/Settings/Optimize.cs b/Tunny/Settings/Optimize.cs index 77e75785..99f7678b 100644 --- a/Tunny/Settings/Optimize.cs +++ b/Tunny/Settings/Optimize.cs @@ -5,6 +5,6 @@ public class Optimize public Sampler Sampler { get; set; } = new Sampler(); public int NumberOfTrials { get; set; } = 100; public bool LoadExistStudy { get; set; } = true; - public int SelectSampler { get; set; } = 0; + public int SelectSampler { get; set; } } } diff --git a/Tunny/Settings/Tpe.cs b/Tunny/Settings/Tpe.cs index 82f14db3..8888b7eb 100644 --- a/Tunny/Settings/Tpe.cs +++ b/Tunny/Settings/Tpe.cs @@ -9,12 +9,12 @@ public class Tpe public bool ConsiderPrior { get; set; } = true; public double PriorWeight { get; set; } = 1.0; public bool ConsiderMagicClip { get; set; } = true; - public bool ConsiderEndpoints { get; set; } = false; + public bool ConsiderEndpoints { get; set; } public int NStartupTrials { get; set; } = 10; public int NEICandidates { get; set; } = 24; - public bool Multivariate { get; set; } = false; - public bool Group { get; set; } = false; + public bool Multivariate { get; set; } + public bool Group { get; set; } public bool WarnIndependentSampling { get; set; } = true; - public bool ConstantLiar { get; set; } = false; + public bool ConstantLiar { get; set; } } } diff --git a/Tunny/Settings/TunnySettings.cs b/Tunny/Settings/TunnySettings.cs index de0a8f15..f62ce2c9 100644 --- a/Tunny/Settings/TunnySettings.cs +++ b/Tunny/Settings/TunnySettings.cs @@ -6,7 +6,7 @@ namespace Tunny.Settings { public class TunnySettings { - public string Version = Assembly.GetExecutingAssembly().GetName().Version.ToString(3); + public string Version { get; set; } = Assembly.GetExecutingAssembly().GetName().Version.ToString(3); public Optimize Optimize { get; set; } = new Optimize(); public Result Result { get; set; } = new Result(); public string StudyName { get; set; } = "study1"; diff --git a/Tunny/Solver/Optuna.cs b/Tunny/Solver/Optuna.cs index dca35728..5d2beb8c 100644 --- a/Tunny/Solver/Optuna.cs +++ b/Tunny/Solver/Optuna.cs @@ -37,7 +37,7 @@ public bool RunSolver( int dVar = variables.Count; double[] lb = new double[dVar]; double[] ub = new double[dVar]; - bool[] integer = new bool[dVar]; + bool[] isInteger = new bool[dVar]; string[] varNickName = new string[dVar]; string[] objNickName = objectives.Select(x => x.NickName).ToArray(); @@ -45,7 +45,7 @@ public bool RunSolver( { lb[i] = Convert.ToDouble(variables[i].LowerBond); ub[i] = Convert.ToDouble(variables[i].UpperBond); - integer[i] = variables[i].Integer; + isInteger[i] = variables[i].IsInteger; varNickName[i] = variables[i].NickName; } @@ -59,8 +59,8 @@ EvaluatedGHResult Eval(double[] x, int progress) { var tpe = new OptunaAlgorithm(lb, ub, varNickName, objNickName, settings, Eval); tpe.Solve(); - XOpt = tpe.Get_XOptimum(); - FxOpt = tpe.Get_fxOptimum(); + XOpt = tpe.GetXOptimum(); + FxOpt = tpe.GetFxOptimum(); TunnyMessageBox.Show("Solver completed successfully.", "Tunny"); diff --git a/Tunny/Solver/OptunaAlgorithm.cs b/Tunny/Solver/OptunaAlgorithm.cs index b05ded8b..a516906f 100644 --- a/Tunny/Solver/OptunaAlgorithm.cs +++ b/Tunny/Solver/OptunaAlgorithm.cs @@ -265,12 +265,12 @@ private dynamic SetTPESamplerSettings(dynamic optuna) ); } - public double[] Get_XOptimum() + public double[] GetXOptimum() { return XOpt; } - public double[] Get_fxOptimum() + public double[] GetFxOptimum() { return FxOpt; } diff --git a/Tunny/Tunny.csproj b/Tunny/Tunny.csproj index 26badf55..da76860f 100644 --- a/Tunny/Tunny.csproj +++ b/Tunny/Tunny.csproj @@ -12,7 +12,22 @@ true Resources\TunnyIcon.ico + en + True + latest-recommended + https://github.com/hrntsm/Tunny + https://github.com/hrntsm/Tunny + git + README.md + TunnyIcon.png + + + + True + \ + + @@ -40,11 +55,25 @@ Resource.Designer.cs + + + True + \ + + C:\Program Files\Rhino 7\System\Rhino.exe Program + + + True + + + + True + \ No newline at end of file diff --git a/Tunny/Type/Fish.cs b/Tunny/Type/Fish.cs index 0fb57cda..2bf85aad 100644 --- a/Tunny/Type/Fish.cs +++ b/Tunny/Type/Fish.cs @@ -12,10 +12,10 @@ namespace Tunny.Type [Serializable] public class Fish { - public int ModelNumber; - public Dictionary Variables; - public Dictionary Objectives; - public Dictionary Attributes; + public int ModelNumber { get; set; } + public Dictionary Variables { get; set; } + public Dictionary Objectives { get; set; } + public Dictionary Attributes { get; set; } public List GetGeometries() { diff --git a/Tunny/Type/GH_Fish.cs b/Tunny/Type/GH_Fish.cs index c3ef43ff..7100dc85 100644 --- a/Tunny/Type/GH_Fish.cs +++ b/Tunny/Type/GH_Fish.cs @@ -88,7 +88,7 @@ private void SetAttributes(StringBuilder sb) } } - private string GeometryBaseToGoo(GeometryBase geometryBase) + private static string GeometryBaseToGoo(GeometryBase geometryBase) { switch (geometryBase) { @@ -103,7 +103,7 @@ private string GeometryBaseToGoo(GeometryBase geometryBase) case SubD subD: return new GH_SubD(subD).ToString(); default: - throw new Exception("Tunny doesn't handle this type of geometry"); + throw new ArgumentException("Tunny doesn't handle this type of geometry"); } } diff --git a/Tunny/Type/GH_FishAttribute.cs b/Tunny/Type/GH_FishAttribute.cs index d48b50fa..4bda256d 100644 --- a/Tunny/Type/GH_FishAttribute.cs +++ b/Tunny/Type/GH_FishAttribute.cs @@ -18,7 +18,7 @@ public GH_FishAttribute() { } - public GH_FishAttribute(Dictionary internal_data) : base(internal_data) + public GH_FishAttribute(Dictionary internalData) : base(internalData) { } @@ -73,7 +73,7 @@ public override string ToString() return sb.ToString(); } - private List GeometryBaseToGoo(List geometryBase) + private static List GeometryBaseToGoo(List geometryBase) { var list = new List(); foreach (GeometryBase geo in geometryBase) @@ -96,7 +96,7 @@ private List GeometryBaseToGoo(List geometryBase) list.Add(new GH_SubD(subD).ToString()); break; default: - throw new Exception("Tunny doesn't handle this type of geometry"); + throw new ArgumentException("Tunny doesn't handle this type of geometry"); } } return list; @@ -113,19 +113,19 @@ public override bool CastFrom(object source) return false; } - public override bool CastTo(ref Q target) + public override bool CastTo(ref T target) { target = default; - if (typeof(Q).IsAssignableFrom(typeof(Dictionary))) + if (typeof(T).IsAssignableFrom(typeof(Dictionary))) { - target = (Q)(object)Value; + target = (T)(object)Value; return true; } else return false; } - private Dictionary FromBase64(string base64) + private static Dictionary FromBase64(string base64) { byte[] bytes = Convert.FromBase64String(base64); using (var ms = new MemoryStream(bytes)) @@ -134,7 +134,7 @@ private Dictionary FromBase64(string base64) } } - private string ToBase64(Dictionary value) + private static string ToBase64(Dictionary value) { using (var ms = new MemoryStream()) { diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index 5e95fefa..78ca8769 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -26,13 +26,13 @@ public class GrasshopperInOut private readonly TunnyComponent _component; private List _genePool; private GH_FishAttribute _attributes; - public List Objectives; - public List Sliders; - public readonly string ComponentFolder; - public List Variables; - public string DocumentPath; - public string DocumentName; + public List Objectives { get; set; } + public List Sliders { get; set; } + public string ComponentFolder { get; } + public List Variables { get; set; } + public string DocumentPath { get; set; } + public string DocumentName { get; set; } public GrasshopperInOut(TunnyComponent component) { @@ -232,7 +232,7 @@ private bool SetSliderValues(IList parameters) return true; } - private decimal GetNormalisedGenePoolValue(decimal unnormalized, GalapagosGeneListObject genePool) + private static decimal GetNormalisedGenePoolValue(decimal unnormalized, GalapagosGeneListObject genePool) { return (unnormalized - genePool.Minimum) / (genePool.Maximum - genePool.Minimum); } diff --git a/Tunny/Util/PythonInstaller.cs b/Tunny/Util/PythonInstaller.cs index 5590df92..c78c3a04 100644 --- a/Tunny/Util/PythonInstaller.cs +++ b/Tunny/Util/PythonInstaller.cs @@ -8,7 +8,7 @@ namespace Tunny.Util { public static class PythonInstaller { - public static string Path = "."; + public static string Path { get; set; } = "."; public static void Run(object sender, DoWorkEventArgs e) { var worker = sender as BackgroundWorker; diff --git a/Tunny/Util/Variables.cs b/Tunny/Util/Variables.cs index eb840e39..20dc5f6c 100644 --- a/Tunny/Util/Variables.cs +++ b/Tunny/Util/Variables.cs @@ -2,16 +2,16 @@ namespace Tunny.Util { public struct Variable { - public readonly decimal LowerBond; - public readonly decimal UpperBond; - public readonly bool Integer; - public readonly string NickName; + public decimal LowerBond { get; } + public decimal UpperBond { get; } + public bool IsInteger { get; } + public string NickName { get; } - public Variable(decimal lowerBond, decimal upperBond, bool integer, string nickName) + public Variable(decimal lowerBond, decimal upperBond, bool isInteger, string nickName) { LowerBond = lowerBond; UpperBond = upperBond; - Integer = integer; + IsInteger = isInteger; NickName = nickName; } } From 0b379bbc31b3ebf67f8afbfc4d7ed91d0343993a Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 23 Jun 2022 09:46:42 +0900 Subject: [PATCH 26/79] Fix error when objectives null in restore fish --- Tunny/Component/DeconstructFish.cs | 7 +++++-- Tunny/Optimization/OptimizeLoop.cs | 4 ++++ Tunny/Optimization/RestoreLoop.cs | 4 ++++ Tunny/Solver/Optuna.cs | 8 ++++++-- Tunny/Type/GH_Fish.cs | 7 +++++-- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Tunny/Component/DeconstructFish.cs b/Tunny/Component/DeconstructFish.cs index 12cabd34..9f8de9f1 100644 --- a/Tunny/Component/DeconstructFish.cs +++ b/Tunny/Component/DeconstructFish.cs @@ -52,9 +52,12 @@ protected override void SolveInstance(IGH_DataAccess DA) { variables.Append(new GH_Number(variable.Value), path); } - foreach (KeyValuePair objective in value.Objectives) + if (value.Objectives != null) { - objectives.Append(new GH_Number(objective.Value), path); + foreach (KeyValuePair objective in value.Objectives) + { + objectives.Append(new GH_Number(objective.Value), path); + } } var attr = new GH_FishAttribute(new Dictionary()); diff --git a/Tunny/Optimization/OptimizeLoop.cs b/Tunny/Optimization/OptimizeLoop.cs index eb7b26fd..cf91ecfb 100644 --- a/Tunny/Optimization/OptimizeLoop.cs +++ b/Tunny/Optimization/OptimizeLoop.cs @@ -27,6 +27,10 @@ internal static void RunMultiple(object sender, DoWorkEventArgs e) s_component.GhInOutInstantiate(); double[] result = RunOptimizationLoop(s_worker); + if (double.IsNaN(result[0])) + { + return; + } var decimalResults = result.Select(Convert.ToDecimal).ToList(); s_component.OptimizationWindow.GrasshopperStatus = OptimizationWindow.GrasshopperStates.RequestSent; diff --git a/Tunny/Optimization/RestoreLoop.cs b/Tunny/Optimization/RestoreLoop.cs index e71f2a0d..aec4306a 100644 --- a/Tunny/Optimization/RestoreLoop.cs +++ b/Tunny/Optimization/RestoreLoop.cs @@ -97,6 +97,10 @@ private static Dictionary SetObjectives(ModelResult model) { string[] nickNames = s_component.GhInOut.Objectives.Select(x => x.NickName).ToArray(); var objectives = new Dictionary(); + if (model.Objectives == null) + { + return null; + } for (int i = 0; i < model.Objectives.Length; i++) { objectives.Add(nickNames[i], model.Objectives[i]); diff --git a/Tunny/Solver/Optuna.cs b/Tunny/Solver/Optuna.cs index 5d2beb8c..3e9a978b 100644 --- a/Tunny/Solver/Optuna.cs +++ b/Tunny/Solver/Optuna.cs @@ -198,13 +198,17 @@ public ModelResult[] GetModelResult(int[] resultNum, string studyName) private static void ParseTrial(ICollection modelResult, dynamic trial) { - modelResult.Add(new ModelResult() + var trialResult = new ModelResult() { Number = (int)trial.number, Variables = ParseVariables(trial), Objectives = (double[])trial.values, Attributes = ParseAttributes(trial), - }); + }; + if (trialResult.Objectives != null) + { + modelResult.Add(trialResult); + } } private static Dictionary ParseVariables(dynamic trial) diff --git a/Tunny/Type/GH_Fish.cs b/Tunny/Type/GH_Fish.cs index 7100dc85..d211322b 100644 --- a/Tunny/Type/GH_Fish.cs +++ b/Tunny/Type/GH_Fish.cs @@ -112,9 +112,12 @@ private void SetObjectives(StringBuilder sb) sb.AppendLine("------------------------------------"); sb.AppendLine("Objectives:"); sb.AppendLine("------------------------------------"); - foreach (KeyValuePair objective in m_value.Objectives) + if (m_value.Objectives != null) { - sb.AppendLine(" \"" + objective.Key + "\": " + objective.Value); + foreach (KeyValuePair objective in m_value.Objectives) + { + sb.AppendLine(" \"" + objective.Key + "\": " + objective.Value); + } } } From 52aee797e7f007de8db8215d6acf0164c3739ed4 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 23 Jun 2022 09:48:34 +0900 Subject: [PATCH 27/79] Update CreateGeometricGoo exception message more helpful to understand what user need to do --- Tunny/Component/DecontstructFishAttribute.cs | 2 +- Tunny/Component/FishMarket.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs index c0ebd102..bec61eb5 100644 --- a/Tunny/Component/DecontstructFishAttribute.cs +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -113,7 +113,7 @@ private static IGH_GeometricGoo CreateGeometricGoo(GeometryBase geometry) case SubD subD: return new GH_SubD(subD); default: - throw new ArgumentException("Tunny doesn't handle this type of geometry"); + throw new ArgumentException("Tunny only supports mesh, curve, brep, surface, subd, so convert it and enter it."); } } diff --git a/Tunny/Component/FishMarket.cs b/Tunny/Component/FishMarket.cs index 489c9d36..f98f4973 100644 --- a/Tunny/Component/FishMarket.cs +++ b/Tunny/Component/FishMarket.cs @@ -145,7 +145,7 @@ private static IGH_GeometricGoo CreateGeometricGoo(GeometryBase geometry) case SubD subD: return new GH_SubD(subD); default: - throw new ArgumentException("Tunny doesn't handle this type of geometry"); + throw new ArgumentException("Tunny only supports mesh, curve, brep, surface, subd, so convert it and enter it."); } } From c04806f12aa52a18a53be40d059b70466fd400c5 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 23 Jun 2022 09:49:03 +0900 Subject: [PATCH 28/79] Add OptimizeWindow to dispose BGworker --- Tunny/Component/TunnyComponent.cs | 1 + Tunny/UI/OptimizationWindow.cs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Tunny/Component/TunnyComponent.cs b/Tunny/Component/TunnyComponent.cs index 99e455be..3563473d 100644 --- a/Tunny/Component/TunnyComponent.cs +++ b/Tunny/Component/TunnyComponent.cs @@ -63,6 +63,7 @@ public void Dispose() { if (OptimizationWindow != null) { + OptimizationWindow.BGDispose(); OptimizationWindow.Dispose(); } GC.SuppressFinalize(this); diff --git a/Tunny/UI/OptimizationWindow.cs b/Tunny/UI/OptimizationWindow.cs index 975d9871..fa01b156 100644 --- a/Tunny/UI/OptimizationWindow.cs +++ b/Tunny/UI/OptimizationWindow.cs @@ -56,6 +56,12 @@ public OptimizationWindow(TunnyComponent component) restoreBackgroundWorker.WorkerSupportsCancellation = true; } + public void BGDispose() + { + optimizeBackgroundWorker.Dispose(); + restoreBackgroundWorker.Dispose(); + } + private void LoadSettingJson() { string settingsPath = _component.GhInOut.ComponentFolder + @"\Settings.json"; @@ -106,14 +112,16 @@ private void FormClosingXButton(object sender, FormClosingEventArgs e) ghCanvas?.EnableUI(); SaveUIValues(); + //TODO: use cancelAsync to stop the background worker safely if (optimizeBackgroundWorker != null) { - optimizeBackgroundWorker.CancelAsync(); + // optimizeBackgroundWorker.CancelAsync(); + optimizeBackgroundWorker.Dispose(); } - if (restoreBackgroundWorker != null) { - restoreBackgroundWorker.CancelAsync(); + // restoreBackgroundWorker.CancelAsync(); + restoreBackgroundWorker.Dispose(); } } From ecdafe8c3607d0f7e29bd1cc8e66063dc0914545 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 23 Jun 2022 09:58:09 +0900 Subject: [PATCH 29/79] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ff926de..7de7a2d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,10 +24,13 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - The output of the Tunny component is made into Fish, a type that summarizes the results. - The Geometry input of the Tunny component has been changed to Attribute to allow more attribute information to be handled. +- When restoring the results from Tunny component as Fishes, those that the trial did not work and Objective could not get are not output. ### Fix - Stopped sampling when there was no geometry input +- Once optimize output error, the component won't run again + - I've tried to do a proper Dispose to fix this problem, but it still doesn't work sometimes. ## [0.3.0] -2022-05-03 From dc3fbdd8b89a51e4fd316d5b89e6a909cdb1ede6 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 23 Jun 2022 20:23:04 +0900 Subject: [PATCH 30/79] Fix solution null error --- Tunny/Solver/OptunaAlgorithm.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tunny/Solver/OptunaAlgorithm.cs b/Tunny/Solver/OptunaAlgorithm.cs index a516906f..a51c9d65 100644 --- a/Tunny/Solver/OptunaAlgorithm.cs +++ b/Tunny/Solver/OptunaAlgorithm.cs @@ -106,7 +106,8 @@ public void Solve() } catch { - break; + // 多分ここでエラーが出てかってにsuccess になっているので もともと breakだったのものをcontinue に変えた + continue; } } From d277871ff9214e9ef36408637b6d1801cbbd94d1 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 23 Jun 2022 23:31:40 +0900 Subject: [PATCH 31/79] Fix code climate issues --- Tunny/Component/ConstructFishAttribute.cs | 48 +++++++------- Tunny/Component/DeconstructFish.cs | 50 +++++++++------ Tunny/Component/DecontstructFishAttribute.cs | 67 ++++++++------------ Tunny/Component/FishMarket.cs | 28 ++------ Tunny/Type/GH_Fish.cs | 54 ++++++---------- Tunny/Type/GH_FishAttribute.cs | 35 ++-------- Tunny/Util/Converter.cs | 63 ++++++++++++++++++ Tunny/Util/GrasshopperInOut.cs | 36 +---------- 8 files changed, 176 insertions(+), 205 deletions(-) create mode 100644 Tunny/Util/Converter.cs diff --git a/Tunny/Component/ConstructFishAttribute.cs b/Tunny/Component/ConstructFishAttribute.cs index 03128915..5fca2666 100644 --- a/Tunny/Component/ConstructFishAttribute.cs +++ b/Tunny/Component/ConstructFishAttribute.cs @@ -84,32 +84,28 @@ private bool CheckIsNicknameDuplicated() public IGH_Param CreateParameter(GH_ParameterSide side, int index) { - if (side == GH_ParameterSide.Input) - { - if (index == 0) - { - var p = new Param_Geometry(); - p.Name = p.NickName = "Geometry"; - p.Description = _geomDescription; - p.Access = GH_ParamAccess.list; - p.MutableNickName = false; - p.Optional = true; - return p; - } - else - { - var p = new Param_GenericObject(); - p.Name = p.NickName = $"Attr{index}"; - p.Description = _attrDescription; - p.Access = GH_ParamAccess.list; - p.Optional = true; - return p; - } - } - else - { - return null; - } + return side == GH_ParameterSide.Input ? index == 0 ? SetGeometryParameterInput() : SetGenericParameterInput(index) : null; + } + + private IGH_Param SetGenericParameterInput(int index) + { + var p = new Param_GenericObject(); + p.Name = p.NickName = $"Attr{index}"; + p.Description = _attrDescription; + p.Access = GH_ParamAccess.list; + p.Optional = true; + return p; + } + + private IGH_Param SetGeometryParameterInput() + { + var p = new Param_Geometry(); + p.Name = p.NickName = "Geometry"; + p.Description = _geomDescription; + p.Access = GH_ParamAccess.list; + p.MutableNickName = false; + p.Optional = true; + return p; } public bool DestroyParameter(GH_ParameterSide side, int index) diff --git a/Tunny/Component/DeconstructFish.cs b/Tunny/Component/DeconstructFish.cs index 9f8de9f1..efed2924 100644 --- a/Tunny/Component/DeconstructFish.cs +++ b/Tunny/Component/DeconstructFish.cs @@ -48,24 +48,9 @@ protected override void SolveInstance(IGH_DataAccess DA) { Fish value = fish.Value; var path = new GH_Path(0, value.ModelNumber); - foreach (KeyValuePair variable in value.Variables) - { - variables.Append(new GH_Number(variable.Value), path); - } - if (value.Objectives != null) - { - foreach (KeyValuePair objective in value.Objectives) - { - objectives.Append(new GH_Number(objective.Value), path); - } - } - - var attr = new GH_FishAttribute(new Dictionary()); - foreach (KeyValuePair attribute in value.Attributes) - { - attr.Value.Add(attribute.Key, attribute.Value); - } - attributes.Append(attr, path); + SetVariables(variables, value, path); + SetObjectives(objectives, value, path); + SetAttributes(attributes, value, path); } DA.SetDataTree(0, variables); @@ -73,6 +58,35 @@ protected override void SolveInstance(IGH_DataAccess DA) DA.SetDataTree(2, attributes); } + private static void SetVariables(GH_Structure variables, Fish value, GH_Path path) + { + foreach (KeyValuePair variable in value.Variables) + { + variables.Append(new GH_Number(variable.Value), path); + } + } + + private static void SetObjectives(GH_Structure objectives, Fish value, GH_Path path) + { + if (value.Objectives != null) + { + foreach (KeyValuePair objective in value.Objectives) + { + objectives.Append(new GH_Number(objective.Value), path); + } + } + } + + private static void SetAttributes(GH_Structure attributes, Fish value, GH_Path path) + { + var attr = new GH_FishAttribute(new Dictionary()); + foreach (KeyValuePair attribute in value.Attributes) + { + attr.Value.Add(attribute.Key, attribute.Value); + } + attributes.Append(attr, path); + } + protected override System.Drawing.Bitmap Icon => null; public override Guid ComponentGuid => new Guid("1D0EA5A9-9499-4D70-8505-6AB4B05889F4"); } diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs index bec61eb5..9dd157a2 100644 --- a/Tunny/Component/DecontstructFishAttribute.cs +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -11,6 +11,7 @@ using Rhino.Geometry; using Tunny.Type; +using Tunny.Util; namespace Tunny.Component { @@ -47,27 +48,33 @@ protected override void SolveInstance(IGH_DataAccess DA) GH_Path path = fishAttributeStructure.Paths[i]; Dictionary value = fishAttributeStructure.Branches[i][0].Value; - //FIXME: This process should be done once, but foreach executes it every time. - _keys = value.Keys.ToList(); + SetOutputValues(nicknames, outputValues, path, value); + } + + SetDataTreeToDataAccess(DA, nicknames, outputValues); + } + + private void SetOutputValues(List nicknames, Dictionary> outputValues, GH_Path path, Dictionary value) + { + //FIXME: This process should be done once, but foreach executes it every time. + _keys = value.Keys.ToList(); + foreach (KeyValuePair pair in value.Where(pair => nicknames.Contains(pair.Key))) + { + if (!outputValues.ContainsKey(pair.Key)) + { + outputValues.Add(pair.Key, new GH_Structure()); + } - foreach (KeyValuePair pair in value) + List goo = GetGooFromAttributeObject(pair.Value); + foreach (IGH_Goo g in goo) { - if (nicknames.Contains(pair.Key)) - { - if (!outputValues.ContainsKey(pair.Key)) - { - outputValues.Add(pair.Key, new GH_Structure()); - } - - List goo = GetGooFromAttributeObject(pair.Value); - foreach (IGH_Goo g in goo) - { - outputValues[pair.Key].Append(g, path); - } - } + outputValues[pair.Key].Append(g, path); } } + } + private void SetDataTreeToDataAccess(IGH_DataAccess DA, List nicknames, Dictionary> outputValues) + { if (Params.Output.Count == 0) { return; @@ -75,12 +82,9 @@ protected override void SolveInstance(IGH_DataAccess DA) for (int i = 0; i < Params.Output.Count; i++) { - foreach (string nickName in nicknames) + foreach (string nickName in nicknames.Where(nickName => Params.Output[i].NickName == nickName)) { - if (Params.Output[i].NickName == nickName) - { - DA.SetDataTree(i, outputValues[nickName]); - } + DA.SetDataTree(i, outputValues[nickName]); } } } @@ -92,31 +96,12 @@ private static List GetGooFromAttributeObject(object value) case List str: return str.Select(s => new GH_String(s)).Cast().ToList(); case List geom: - return geom.Select(g => CreateGeometricGoo(g)).Cast().ToList(); + return geom.Select(g => Converter.GeometryBaseToGoo(g)).Cast().ToList(); default: return new List(); } } - private static IGH_GeometricGoo CreateGeometricGoo(GeometryBase geometry) - { - switch (geometry) - { - case Mesh mesh: - return new GH_Mesh(mesh); - case Curve curve: - return new GH_Curve(curve); - case Brep brep: - return new GH_Brep(brep); - case Surface surface: - return new GH_Surface(surface); - case SubD subD: - return new GH_SubD(subD); - default: - throw new ArgumentException("Tunny only supports mesh, curve, brep, surface, subd, so convert it and enter it."); - } - } - public bool CanInsertParameter(GH_ParameterSide side, int index) => side != GH_ParameterSide.Input && (Params.Output.Count == 0 || index == Params.Output.Count); public bool CanRemoveParameter(GH_ParameterSide side, int index) => (side != GH_ParameterSide.Input) && (index == Params.Output.Count - 1); diff --git a/Tunny/Component/FishMarket.cs b/Tunny/Component/FishMarket.cs index f98f4973..2566d6ad 100644 --- a/Tunny/Component/FishMarket.cs +++ b/Tunny/Component/FishMarket.cs @@ -12,6 +12,7 @@ using Tunny.Resources; using Tunny.Type; +using Tunny.Util; namespace Tunny.Component { @@ -119,10 +120,10 @@ private bool SetGeometryToResultArray(int countY, GH_Structure Point3d modelMinPt = GetUnionBoundingBoxMinPt(fishGeometries[index]); foreach (GeometryBase geometry in fishGeometries[index]) { - GeometryBase duplicateGeometry = geometry.Duplicate(); - duplicateGeometry.Rotate(Vector3d.VectorAngle(Vector3d.XAxis, _settings.Plane.XAxis), Vector3d.ZAxis, modelMinPt); - duplicateGeometry.Translate(xVec + yVec + new Vector3d(_settings.Plane.Origin) - new Vector3d(modelMinPt)); - arrayedGeometries.Append(CreateGeometricGoo(duplicateGeometry), new GH_Path(0, _fishes[index].Value.ModelNumber)); + GeometryBase dupGeometry = geometry.Duplicate(); + dupGeometry.Rotate(Vector3d.VectorAngle(Vector3d.XAxis, _settings.Plane.XAxis), Vector3d.ZAxis, modelMinPt); + dupGeometry.Translate(xVec + yVec + new Vector3d(_settings.Plane.Origin) - new Vector3d(modelMinPt)); + arrayedGeometries.Append(Converter.GeometryBaseToGoo(dupGeometry), new GH_Path(0, _fishes[index].Value.ModelNumber)); } _tagPlanes.Add(new Plane(modelMinPt - _settings.Plane.YAxis * 2.5 * _size, _settings.Plane.XAxis, _settings.Plane.YAxis)); } @@ -130,25 +131,6 @@ private bool SetGeometryToResultArray(int countY, GH_Structure return true; } - private static IGH_GeometricGoo CreateGeometricGoo(GeometryBase geometry) - { - switch (geometry) - { - case Mesh mesh: - return new GH_Mesh(mesh); - case Curve curve: - return new GH_Curve(curve); - case Brep brep: - return new GH_Brep(brep); - case Surface surface: - return new GH_Surface(surface); - case SubD subD: - return new GH_SubD(subD); - default: - throw new ArgumentException("Tunny only supports mesh, curve, brep, surface, subd, so convert it and enter it."); - } - } - private static Point3d GetUnionBoundingBoxMinPt(IEnumerable geometryBases) { var minPt = new Point3d(double.MaxValue, double.MaxValue, double.MaxValue); diff --git a/Tunny/Type/GH_Fish.cs b/Tunny/Type/GH_Fish.cs index d211322b..b7508471 100644 --- a/Tunny/Type/GH_Fish.cs +++ b/Tunny/Type/GH_Fish.cs @@ -8,6 +8,8 @@ using Rhino.Geometry; +using Tunny.Util; + namespace Tunny.Type { public class GH_Fish : GH_Goo @@ -66,45 +68,31 @@ private void SetAttributes(StringBuilder sb) foreach (KeyValuePair attr in m_value.Attributes) { - string valueStrings = string.Empty; - if (attr.Key == "Geometry") - { - List geometries = Value.GetGeometries(); - foreach (GeometryBase val in geometries) - { - string geomString = GeometryBaseToGoo(val); - valueStrings += "\n " + geomString; - } - } - else - { - var values = attr.Value as List; - foreach (string val in values) - { - valueStrings += val + ", "; - } - } - sb.AppendLine(" " + attr.Key + ": " + valueStrings); + SetAttributeEachItem(sb, attr); } } - private static string GeometryBaseToGoo(GeometryBase geometryBase) + private void SetAttributeEachItem(StringBuilder sb, KeyValuePair attr) { - switch (geometryBase) + string valueStrings = string.Empty; + if (attr.Key == "Geometry") { - case Mesh mesh: - return new GH_Mesh(mesh).ToString(); - case Curve curve: - return new GH_Curve(curve).ToString(); - case Brep brep: - return new GH_Brep(brep).ToString(); - case Surface surface: - return new GH_Surface(surface).ToString(); - case SubD subD: - return new GH_SubD(subD).ToString(); - default: - throw new ArgumentException("Tunny doesn't handle this type of geometry"); + List geometries = Value.GetGeometries(); + foreach (GeometryBase geom in geometries) + { + string geomString = Converter.GeometryBaseToGoo(geom).ToString(); + valueStrings += "\n " + geomString; + } + } + else + { + var values = attr.Value as List; + foreach (string val in values) + { + valueStrings += val + ", "; + } } + sb.AppendLine(" " + attr.Key + ": " + valueStrings); } private void SetObjectives(StringBuilder sb) diff --git a/Tunny/Type/GH_FishAttribute.cs b/Tunny/Type/GH_FishAttribute.cs index 4bda256d..7706466e 100644 --- a/Tunny/Type/GH_FishAttribute.cs +++ b/Tunny/Type/GH_FishAttribute.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.Serialization.Formatters.Binary; using System.Text; @@ -10,6 +11,8 @@ using Rhino.Geometry; +using Tunny.Util; + namespace Tunny.Type { public class GH_FishAttribute : GH_Goo> @@ -65,7 +68,8 @@ public override string ToString() valueStrings = string.Join(", ", str); break; case List geo: - valueStrings = string.Join(", ", GeometryBaseToGoo(geo)); + IEnumerable geometryStrings = geo.Select(g => Converter.GeometryBaseToGoo(g).ToString()); + valueStrings = string.Join(", ", geometryStrings); break; } sb.AppendLine(" " + attr.Key + ": " + valueStrings); @@ -73,35 +77,6 @@ public override string ToString() return sb.ToString(); } - private static List GeometryBaseToGoo(List geometryBase) - { - var list = new List(); - foreach (GeometryBase geo in geometryBase) - { - switch (geo) - { - case Mesh mesh: - list.Add(new GH_Mesh(mesh).ToString()); - break; - case Curve curve: - list.Add(new GH_Curve(curve).ToString()); - break; - case Brep brep: - list.Add(new GH_Brep(brep).ToString()); - break; - case Surface surface: - list.Add(new GH_Surface(surface).ToString()); - break; - case SubD subD: - list.Add(new GH_SubD(subD).ToString()); - break; - default: - throw new ArgumentException("Tunny doesn't handle this type of geometry"); - } - } - return list; - } - public override bool CastFrom(object source) { if (source is Dictionary dict) diff --git a/Tunny/Util/Converter.cs b/Tunny/Util/Converter.cs new file mode 100644 index 00000000..e42a11af --- /dev/null +++ b/Tunny/Util/Converter.cs @@ -0,0 +1,63 @@ +using System; + +using Grasshopper.Kernel.Types; + +using Rhino.FileIO; +using Rhino.Geometry; + +namespace Tunny.Util +{ + public static class Converter + { + public static IGH_GeometricGoo GeometryBaseToGoo(GeometryBase geometryBase) + { + switch (geometryBase) + { + case Mesh mesh: + return new GH_Mesh(mesh); + case Curve curve: + return new GH_Curve(curve); + case Brep brep: + return new GH_Brep(brep); + case Surface surface: + return new GH_Surface(surface); + case SubD subD: + return new GH_SubD(subD); + default: + throw new ArgumentException("Tunny only supports mesh, curve, brep, surface, subd, so convert it and enter it."); + } + } + + public static string GooToString(IGH_Goo goo, bool isGeometryBaseToJson) + { + var option = new SerializationOptions(); + return GooToString(goo, isGeometryBaseToJson, option); + } + + public static string GooToString(IGH_Goo goo, bool isGeometryBaseToJson, SerializationOptions option) + { + if (isGeometryBaseToJson) + { + switch (goo) + { + case GH_Mesh mesh: + return mesh.Value.ToJSON(option); + case GH_Brep brep: + return brep.Value.ToJSON(option); + case GH_Curve curve: + return curve.Value.ToJSON(option); + case GH_Surface surface: + return surface.Value.ToJSON(option); + case GH_SubD subd: + return subd.Value.ToJSON(option); + default: + return goo.ToString(); + } + } + else + { + return goo.ToString(); + } + } + } +} diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index 78ca8769..95f6436d 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -300,45 +300,13 @@ public List GetGeometryJson() { if (param is IGH_Goo goo) { - json.Add(GHParamToString(goo, option)); + json.Add(Converter.GooToString(goo, true)); } } return json; } - private static string GHParamToString(IGH_Goo goo) - { - var option = new SerializationOptions(); - return GHParamToString(goo, option); - } - - private static string GHParamToString(IGH_Goo goo, SerializationOptions option) - { - string result; - switch (goo) - { - case GH_Mesh mesh: - result = mesh.Value.ToJSON(option); - break; - case GH_Brep brep: - result = brep.Value.ToJSON(option); - break; - case GH_Curve curve: - result = curve.Value.ToJSON(option); - break; - case GH_Surface surface: - result = surface.Value.ToJSON(option); - break; - case GH_SubD subd: - result = subd.Value.ToJSON(option); - break; - default: - result = goo.ToString(); - break; - } - return result; - } public Dictionary> GetAttributes() { @@ -361,7 +329,7 @@ public Dictionary> GetAttributes() { if (param is IGH_Goo goo) { - value.Add(GHParamToString(goo)); + value.Add(Converter.GooToString(goo, true)); } } attrs.Add(key, value); From 12413b63f513d87c18f19655bedb40c8fe2ca19c Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 26 Jun 2022 23:10:09 +0900 Subject: [PATCH 32/79] Add icon --- Tunny/Component/ConstructFishAttribute.cs | 3 +- Tunny/Component/DeconstructFish.cs | 3 +- Tunny/Component/DecontstructFishAttribute.cs | 3 +- Tunny/Component/Param_FishAttribute.cs | 4 +- Tunny/Resources/ConstructFishAttribute.png | Bin 0 -> 955 bytes Tunny/Resources/ConstructFishAttrubute.svg | 344 +++++++++++++++++ Tunny/Resources/DeconstructFish.png | Bin 0 -> 1476 bytes Tunny/Resources/DeconstructFish.svg | 316 ++++++++++++++++ Tunny/Resources/DeconstructFishAttribute.png | Bin 0 -> 1272 bytes Tunny/Resources/DeconstructFishAttrubute.svg | 350 ++++++++++++++++++ .../{FishMarketIcon.svg => FishMarket.svg} | 0 Tunny/Resources/ParamFishAttribute.png | Bin 0 -> 767 bytes Tunny/Resources/ParamFishAttribute.svg | 296 +++++++++++++++ Tunny/Resources/Resource.Designer.cs | 40 ++ Tunny/Resources/Resource.resx | 12 + Tunny/Resources/icon_link.md | 4 + 16 files changed, 1371 insertions(+), 4 deletions(-) create mode 100644 Tunny/Resources/ConstructFishAttribute.png create mode 100644 Tunny/Resources/ConstructFishAttrubute.svg create mode 100644 Tunny/Resources/DeconstructFish.png create mode 100644 Tunny/Resources/DeconstructFish.svg create mode 100644 Tunny/Resources/DeconstructFishAttribute.png create mode 100644 Tunny/Resources/DeconstructFishAttrubute.svg rename Tunny/Resources/{FishMarketIcon.svg => FishMarket.svg} (100%) create mode 100644 Tunny/Resources/ParamFishAttribute.png create mode 100644 Tunny/Resources/ParamFishAttribute.svg diff --git a/Tunny/Component/ConstructFishAttribute.cs b/Tunny/Component/ConstructFishAttribute.cs index 5fca2666..7403223e 100644 --- a/Tunny/Component/ConstructFishAttribute.cs +++ b/Tunny/Component/ConstructFishAttribute.cs @@ -5,6 +5,7 @@ using Grasshopper.Kernel; using Grasshopper.Kernel.Parameters; +using Tunny.Resources; using Tunny.Type; namespace Tunny.Component @@ -137,7 +138,7 @@ private void ParamChangedHandler(IGH_DocumentObject sender, GH_ObjectChangedEven } } - protected override System.Drawing.Bitmap Icon => null; + protected override System.Drawing.Bitmap Icon => Resource.ConstructFishAttribute; public override Guid ComponentGuid => new Guid("0E66E2E8-6A97-45E0-93DF-2251C3949B7D"); } } diff --git a/Tunny/Component/DeconstructFish.cs b/Tunny/Component/DeconstructFish.cs index efed2924..bc1530e2 100644 --- a/Tunny/Component/DeconstructFish.cs +++ b/Tunny/Component/DeconstructFish.cs @@ -6,6 +6,7 @@ using Grasshopper.Kernel.Data; using Grasshopper.Kernel.Types; +using Tunny.Resources; using Tunny.Type; namespace Tunny.Component @@ -87,7 +88,7 @@ private static void SetAttributes(GH_Structure attributes, Fis attributes.Append(attr, path); } - protected override System.Drawing.Bitmap Icon => null; + protected override System.Drawing.Bitmap Icon => Resource.DeconstructFish; public override Guid ComponentGuid => new Guid("1D0EA5A9-9499-4D70-8505-6AB4B05889F4"); } } diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs index 9dd157a2..0592234d 100644 --- a/Tunny/Component/DecontstructFishAttribute.cs +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -10,6 +10,7 @@ using Rhino.Geometry; +using Tunny.Resources; using Tunny.Type; using Tunny.Util; @@ -160,7 +161,7 @@ private void Menu_AutoCreateTree_Clicked(object sender, EventArgs e) ExpireSolution(true); } - protected override System.Drawing.Bitmap Icon => null; + protected override System.Drawing.Bitmap Icon => Resource.DeconstructFishAttribute; public override Guid ComponentGuid => new Guid("D3B7B64A-71BA-41BE-9B2E-B0F459886B36"); } } diff --git a/Tunny/Component/Param_FishAttribute.cs b/Tunny/Component/Param_FishAttribute.cs index ce98122b..d3da5028 100644 --- a/Tunny/Component/Param_FishAttribute.cs +++ b/Tunny/Component/Param_FishAttribute.cs @@ -4,6 +4,8 @@ using Grasshopper.Kernel; +using Tunny.Resources; + namespace Tunny.Type { public class Param_FishAttribute : GH_PersistentParam @@ -19,7 +21,7 @@ public Param_FishAttribute() protected override GH_GetterResult Prompt_Singular(ref GH_FishAttribute value) => GH_GetterResult.success; protected override GH_GetterResult Prompt_Plural(ref List values) => GH_GetterResult.success; - protected override Bitmap Icon => null; + protected override Bitmap Icon => Resource.ParamFishAttribute; public override Guid ComponentGuid => new Guid("bfbd03eb-492d-4c57-8311-e7452d78a78e"); } } diff --git a/Tunny/Resources/ConstructFishAttribute.png b/Tunny/Resources/ConstructFishAttribute.png new file mode 100644 index 0000000000000000000000000000000000000000..d1d6a68112ced3df29c6f0519fd65e4c44616829 GIT binary patch literal 955 zcmV;s14R6ZP)*C(mzPu)F^@p=~hjadO&-u^24~CB$ zKrIjj9{xk#u+aoaZLL7YaFSdpd`p3m4&P`;Y!@)u5t9$h8LHr6AfiuuOq1pS=YSaf z9kc`2ff0ZJ4gg_oC(@D6)dBV~eAflzHov_{1gJ*oV)>)Ms#FF1z|Ry!|7u~N$Rf21 z)dkc8B|x^#-=jNt1>6TJfNmh5^X(ilJ)+m?#su)*cA7X~07Zp`hx1@xt}QMAgixcj z;y*jdgyY)-Gy;V{0;q(At9-r{OQbYUOUYuFOFWjc_f9;1(>mo-V71PT0^@-WX8{*b z4D1AgfLFIW-RCBkGAbtZX7$l-e3OwV^e)iLuYk$b3rKdaRpG<(eWtTG#f2pJ9Qxs z5OM*ffDagDBbR#i2thu;>GPYX`Te;I#*g)9*49SGOqx^>(Qy;q?#X>ygIbW0nsZI6 zn{16P4?s$C04apX1X#Rm>XenMXV%Z2Uu+tcCk?mT6&FGvgj3GG(Q^eQPO~;^BMbna z@5nx{HzFV=P!iNF6 zI}@$;qe&@S4(+-3WzmwrIMWPU?snj!=JEr(oGp1!>Pys`zRk+oKE>+|N~5x_T%Ww- zQ%&^Mn)SCTs%m5JO4e?WLbyzw(?q|HY4O34O~BM+E06!~CiB)Nk@x5Yxbry>;|qUPQL dJkf=Q#9zNWUZxc+8NUDk002ovPDHLkV1j3yvl9RS literal 0 HcmV?d00001 diff --git a/Tunny/Resources/ConstructFishAttrubute.svg b/Tunny/Resources/ConstructFishAttrubute.svg new file mode 100644 index 00000000..3c497f39 --- /dev/null +++ b/Tunny/Resources/ConstructFishAttrubute.svg @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tunny/Resources/DeconstructFish.png b/Tunny/Resources/DeconstructFish.png new file mode 100644 index 0000000000000000000000000000000000000000..6c89d54a0870bac5752f8658e6613bf2cd130b39 GIT binary patch literal 1476 zcmV;#1v~nQP)ykgph9@rx%<-xym&9ev`n2(?{nV&Isdaf=RAVH z5mOc^LZY&2I5qSgn*I*oU6g--86^b=v<|9`nQW|EPQd?4ph!;N{|pdCF`RJU^gDal2k z=^>5)2fXSg7Vx~Ol{}f_?NjA>s91|$<>G(@!zWk9(q#1|G_W~HrARNFNBVJq*5e%C zY^P)6845gf9?@r!cjQ)7`nj4g_Lal|&_~zK>|tdrh$&)-zOg5s?qU;?q|gt!>P2A6 zA_X}7!r?g-m<9u|c%~0u9<}0dfl5f8c&L;Y6D|>9F!$WN2XZm&1CqK3F1>_iKp;0H zgg^6r8lgjic_|}~(N8Yq__mMe=hsfj%~oE|vjd=0+leqV5^gXPX}CwE!ORf7nFzfZ zoyO5w8p*WK56M>p^zX&fQW6f(Wbq+6+=tO~(*fuesAJdudrTP{R3}88K}xCZaA+7h zYydICVmN)NiEStBI9#34ZkU|UyqIDnWCQuieYW(j0=m@r`aI}qT=JtVO6^U*Q4`88 zm*p1u`}^DhIC0s*ytHwAcI;isZoSE?OZosXFrcN=P%zz?O^Etd4!$%q$z4=mm7SIr zOBaFfpphhHj0BkMa>+YecBU6*q-{@avk1ng9Fpr-l-BmuwGDb_cLPh}3z-;Ju0NVI&Ivtm#25#^J6EU*C@je2 zwmE;@X3u?V7L68P&ec9xv;y?1tVb9Q%y%mX_iE&el6aci2_DH2uZDH20D#{ zl%Y4-_f=71M~D4d_f{PF<(1Ica}%$pr|oz=dPGRgbLkWJ16Ukt7S0UhOtn`ARf@*K zHSpemfm$0uN%7)`7^Gk;pUaTCoer?jxT|YJv)JsiO zQ*Ruu&V45ZmzDloX@#y=tJ8~Jjn+qH%Rg3V3y+ObyQ6 z`<510Ro3ah{JQkv?D>M-Kh)!tf_A$GKsp>8Q~;!8k0*gzk@)+lT&uHZomwzwb5dKY z^#Wk+9TbrG>y5XK0|prURxaB+cFwF#Guzv2LAQ)8eL@EMVQT2SK^nyga5!QUl>%1l z7L7Zjd3)md^FTDfuB&5s@7zR&Mg{*~QGPcPK&@7+k0r+zzV-fmrBC0$@$RHtE~&ck z%^vN#+)KX3KRTa{gy32k?BBlP+iQom3cY@Hq|@ozGooKzY4-Tj2DRdppBIlF{!_(6 z@dD{Yh+|R;Bgj}_0>IR1BhF5nIilX&(5BtH^XK@x_3a^k8oMPv_>R#}ZMPNP9u(AQ e^$C%Z-G2gM*A(HaCezLU0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tunny/Resources/DeconstructFishAttribute.png b/Tunny/Resources/DeconstructFishAttribute.png new file mode 100644 index 0000000000000000000000000000000000000000..d2fe414807d9bd0c93eb2337ba9c9c59a2559d3c GIT binary patch literal 1272 zcmV}YlKy5(`gT{p=;!p}`3Yg*oRzv@^qC}w%B_<|_ z(SXG*(S%3<(O`|zKv)w*1F|Gg)Ea~VAqFWkP@qiL8K#|ieb+zUbQxOgA9$0OoO|AP zzVCeZ+*}xD1>Ry>;N_ryCB^@{fDjx9goL?B4*9**ND%iV zAVK`Ig0%Sd>_9zWRlv)K_axx=Ql?s{Z>*v~!p$Kd)q$0i00g}3zUF1j-2?_%m5ZazEwdAO{iz3Y+dA z2e1Nc_kr&w_}k&Y-_RrB4srmi6x24a!Z}Fbs+U)QQr&<$9|R+bmGy!CSP{_J*1wmS zqOE34L6^?jgd@7Vf>9O==YiB^`{1Wb;LzZ;$Kl0E@ckLs@GAswn?;%hrscxBvrK}e zd!Z==X?FPT1sIz~x3o}@zn*L6*?U>%_aYBeATyGX@us9hLuMr-?P`Cn_Y)v9lg3#ayK|N^j@4@h4M*#vw#=Z(qitbBd;Y0aj_ z_U$;8+R_r9bHmqEAuX@&mHHL~bW39!Oxb82Yw5CVdj$JzMkLkL8&gW}-KnVMim`s+?K`08Ls9CV*FL=pKk|Ll(4E?$b zK#Rx8cAFU%7LA1U@vR&pQ42|N~$ zBb}ix9Y9e;R_Ve=pPx}O@!3i4ywUT^Cf7@8ww-D>IdYsZ_kJkK;CB_M_l$*~$M##R zK8SHUh8=hZ_)1C{xP0lB)l=NxVP|Jo!=dzAsY7YX~+QR>WW?1w)Ob_AHF@&KEspl*|)2<5Mrcg@W#}HQwrp` zIHrg?dZc7yZ}xf!gy<7+0%hUwm&K<}oO~n_>Aq;STI}$18vkcEe^f(EkMw@~3`IFsBy)0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tunny/Resources/FishMarketIcon.svg b/Tunny/Resources/FishMarket.svg similarity index 100% rename from Tunny/Resources/FishMarketIcon.svg rename to Tunny/Resources/FishMarket.svg diff --git a/Tunny/Resources/ParamFishAttribute.png b/Tunny/Resources/ParamFishAttribute.png new file mode 100644 index 0000000000000000000000000000000000000000..58c7f0653dd0ade5536dbc4d182711a5f29b05b0 GIT binary patch literal 767 zcmVpO~mMph?tA6cwZ@A?@a%Ep&_$G<9%r2&FiP#v+tfCo@O~5tIxL z8nEC{>Lfxlm_neXWE6@iQXH&8p_6FrAQeK>w?lg+h8SbJ`ocHd^Yc81^PLMf`H#E; zRARlkF8l%T>ly+9fl}c%AazZ+fG@zUQUuTb0dCgJRd6@(J%3|UQ+Hold8QhliC)rDSGzcUOr-Lg8>&hGD3- zw)PzC_xokH+s*HX`EVQX$y^)?g>-y;tlS(N9H_s)UmF`63I>BpCX;GuX_3WZQCnM^ z;_+4OySHKQn{VRA1JO-GWnqqNrk*cby3nz#~B6z)Cc6N5~csvXZ4bk4-j?HFc za&nUS`FT!GPB=Y11#kdAfe+^XN9O4#CMHxUM@L8M>gtjtB@zjZjg4t#WkuO+R^#L2 z+T7e!LqkJuFP`LI(|dDrWMo8#hll6z_4Rf6d_IN4VQp`3tGBmTv$L~mZf@4X!h&M4 zm^wQ*s0IcG)YsQ1Kt7*OlC-t8 zCAZrxAPd~T>?K7g@bp?+TUA$Ar$8W}{r!CtB$vyjwY4=xqfxnBE>%`m3V3-L9H1Kb zR!9QK>-9>K_V)HPH#etbGAT(K9UWD9dAWczU=$Mo1Pj5dtE)9VJ+1EUZVe6&>g?=H zGcz;tcsyEOUY21P0zy~8&AcllaXOs>)$Z2?fm6esrFbvhy x)R<#g;9en+rHJGOfBuhB5(A!>xb`L&^Bbb8x0%j?tAhXl002ovPDHLkV1m$!QXv2U literal 0 HcmV?d00001 diff --git a/Tunny/Resources/ParamFishAttribute.svg b/Tunny/Resources/ParamFishAttribute.svg new file mode 100644 index 00000000..25daabb6 --- /dev/null +++ b/Tunny/Resources/ParamFishAttribute.svg @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tunny/Resources/Resource.Designer.cs b/Tunny/Resources/Resource.Designer.cs index 40a2ee98..38c33ef7 100644 --- a/Tunny/Resources/Resource.Designer.cs +++ b/Tunny/Resources/Resource.Designer.cs @@ -60,6 +60,36 @@ internal Resource() { } } + /// + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 + /// + internal static System.Drawing.Bitmap ConstructFishAttribute { + get { + object obj = ResourceManager.GetObject("ConstructFishAttribute", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 + /// + internal static System.Drawing.Bitmap DeconstructFish { + get { + object obj = ResourceManager.GetObject("DeconstructFish", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 + /// + internal static System.Drawing.Bitmap DeconstructFishAttribute { + get { + object obj = ResourceManager.GetObject("DeconstructFishAttribute", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 /// @@ -70,6 +100,16 @@ internal static System.Drawing.Bitmap FishMarket { } } + /// + /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 + /// + internal static System.Drawing.Bitmap ParamFishAttribute { + get { + object obj = ResourceManager.GetObject("ParamFishAttribute", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// 型 System.Drawing.Bitmap のローカライズされたリソースを検索します。 /// diff --git a/Tunny/Resources/Resource.resx b/Tunny/Resources/Resource.resx index 8198709c..3553bf04 100644 --- a/Tunny/Resources/Resource.resx +++ b/Tunny/Resources/Resource.resx @@ -118,9 +118,21 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ConstructFishAttribute.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + DeconstructFish.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + DeconstructFishAttribute.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + FishMarket.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ParamFishAttribute.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + paramfishicon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/Tunny/Resources/icon_link.md b/Tunny/Resources/icon_link.md index c9dcfec2..2d573d60 100644 --- a/Tunny/Resources/icon_link.md +++ b/Tunny/Resources/icon_link.md @@ -6,3 +6,7 @@ - https://icooon-mono.com/15801-%e3%83%9e%e3%82%b0%e3%83%ad%e3%82%a2%e3%82%a4%e3%82%b3%e3%83%b31/ - FishMarket Icon - https://icon-rainbow.com/%e3%81%be%e3%81%90%e3%82%8d%e3%81%ae%e7%84%a1%e6%96%99%e3%82%a2%e3%82%a4%e3%82%b3%e3%83%b3%e7%b4%a0%e6%9d%90-1/ +- Attribute gear + - https://icooon-mono.com/14476-%E6%AD%AF%E8%BB%8A%E3%82%A2%E3%82%A4%E3%82%B3%E3%83%B310/ +- Deconstruct Fire + - https://icooon-mono.com/13073-fire-icon/?lang=en \ No newline at end of file From b06bab043dd33f2c47c5433dda3876c4d50cebb4 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 26 Jun 2022 23:10:29 +0900 Subject: [PATCH 33/79] Fix null optimizationWindow dispose error --- Tunny/Component/TunnyComponent.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Tunny/Component/TunnyComponent.cs b/Tunny/Component/TunnyComponent.cs index 3563473d..16815954 100644 --- a/Tunny/Component/TunnyComponent.cs +++ b/Tunny/Component/TunnyComponent.cs @@ -56,7 +56,11 @@ public void GhInOutInstantiate() public override void RemovedFromDocument(GH_Document document) { base.RemovedFromDocument(document); - OptimizationWindow.Dispose(); + if (OptimizationWindow != null) + { + OptimizationWindow.BGDispose(); + OptimizationWindow.Dispose(); + } } public void Dispose() From 345b0c302345aae8bf6511615c95a1cef49b2361 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 26 Jun 2022 23:27:17 +0900 Subject: [PATCH 34/79] Clean codacy issues --- Tunny/Component/DecontstructFishAttribute.cs | 5 +++++ Tunny/Solver/Optuna.cs | 2 +- Tunny/Solver/OptunaAlgorithm.cs | 2 -- Tunny/Type/GH_Fish.cs | 10 ++++++---- Tunny/Type/GH_FishAttribute.cs | 9 +++++---- Tunny/UI/OptimizationWindow.cs | 2 -- Tunny/Util/GrasshopperInOut.cs | 18 ++++-------------- 7 files changed, 21 insertions(+), 27 deletions(-) diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs index 0592234d..4d009446 100644 --- a/Tunny/Component/DecontstructFishAttribute.cs +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -36,6 +36,7 @@ protected override void RegisterInputParams(GH_InputParamManager pManager) protected override void RegisterOutputParams(GH_OutputParamManager pManager) { + // No output parameters } protected override void SolveInstance(IGH_DataAccess DA) @@ -149,12 +150,16 @@ private void Menu_AutoCreateTree_Clicked(object sender, EventArgs e) if (Params.Output.Count < _keys.Count) { while (Params.Output.Count < _keys.Count) + { Params.RegisterOutputParam(CreateParameter(GH_ParameterSide.Output, Params.Output.Count)); + } } else if (Params.Output.Count > _keys.Count) { while (Params.Output.Count > _keys.Count) + { Params.UnregisterOutputParameter(Params.Output[Params.Output.Count - 1]); + } } Params.OnParametersChanged(); VariableParameterMaintenance(); diff --git a/Tunny/Solver/Optuna.cs b/Tunny/Solver/Optuna.cs index 3e9a978b..0807f571 100644 --- a/Tunny/Solver/Optuna.cs +++ b/Tunny/Solver/Optuna.cs @@ -198,7 +198,7 @@ public ModelResult[] GetModelResult(int[] resultNum, string studyName) private static void ParseTrial(ICollection modelResult, dynamic trial) { - var trialResult = new ModelResult() + var trialResult = new ModelResult { Number = (int)trial.number, Variables = ParseVariables(trial), diff --git a/Tunny/Solver/OptunaAlgorithm.cs b/Tunny/Solver/OptunaAlgorithm.cs index a51c9d65..edc01878 100644 --- a/Tunny/Solver/OptunaAlgorithm.cs +++ b/Tunny/Solver/OptunaAlgorithm.cs @@ -106,8 +106,6 @@ public void Solve() } catch { - // 多分ここでエラーが出てかってにsuccess になっているので もともと breakだったのものをcontinue に変えた - continue; } } diff --git a/Tunny/Type/GH_Fish.cs b/Tunny/Type/GH_Fish.cs index b7508471..aa71e49b 100644 --- a/Tunny/Type/GH_Fish.cs +++ b/Tunny/Type/GH_Fish.cs @@ -74,14 +74,15 @@ private void SetAttributes(StringBuilder sb) private void SetAttributeEachItem(StringBuilder sb, KeyValuePair attr) { - string valueStrings = string.Empty; + var valueStrings = new StringBuilder(); if (attr.Key == "Geometry") { List geometries = Value.GetGeometries(); foreach (GeometryBase geom in geometries) { string geomString = Converter.GeometryBaseToGoo(geom).ToString(); - valueStrings += "\n " + geomString; + valueStrings.Append("\n "); + valueStrings.Append(geomString); } } else @@ -89,10 +90,11 @@ private void SetAttributeEachItem(StringBuilder sb, KeyValuePair var values = attr.Value as List; foreach (string val in values) { - valueStrings += val + ", "; + valueStrings.Append(val); + valueStrings.Append(", "); } } - sb.AppendLine(" " + attr.Key + ": " + valueStrings); + sb.AppendLine(" " + attr.Key + ": " + valueStrings.ToString()); } private void SetObjectives(StringBuilder sb) diff --git a/Tunny/Type/GH_FishAttribute.cs b/Tunny/Type/GH_FishAttribute.cs index 7706466e..6a3200ef 100644 --- a/Tunny/Type/GH_FishAttribute.cs +++ b/Tunny/Type/GH_FishAttribute.cs @@ -32,10 +32,7 @@ public GH_FishAttribute(GH_Goo> other) : base(other) public override bool IsValid => Value != null; public override string TypeName => "FishAttribute"; public override string TypeDescription => "DictionaryGoo for grasshopper"; - public override IGH_Goo Duplicate() - { - return new GH_FishAttribute() { Value = Value }; - } + public override IGH_Goo Duplicate() => new GH_FishAttribute { Value = Value }; public override bool Read(GH_IReader reader) { @@ -71,6 +68,8 @@ public override string ToString() IEnumerable geometryStrings = geo.Select(g => Converter.GeometryBaseToGoo(g).ToString()); valueStrings = string.Join(", ", geometryStrings); break; + default: + throw new ArgumentException($"Unsupported type: {attr.Value.GetType()}"); } sb.AppendLine(" " + attr.Key + ": " + valueStrings); } @@ -85,7 +84,9 @@ public override bool CastFrom(object source) return true; } else + { return false; + } } public override bool CastTo(ref T target) diff --git a/Tunny/UI/OptimizationWindow.cs b/Tunny/UI/OptimizationWindow.cs index fa01b156..925e0eb0 100644 --- a/Tunny/UI/OptimizationWindow.cs +++ b/Tunny/UI/OptimizationWindow.cs @@ -115,12 +115,10 @@ private void FormClosingXButton(object sender, FormClosingEventArgs e) //TODO: use cancelAsync to stop the background worker safely if (optimizeBackgroundWorker != null) { - // optimizeBackgroundWorker.CancelAsync(); optimizeBackgroundWorker.Dispose(); } if (restoreBackgroundWorker != null) { - // restoreBackgroundWorker.CancelAsync(); restoreBackgroundWorker.Dispose(); } } diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index 95f6436d..66c1ce37 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -43,16 +43,14 @@ public GrasshopperInOut(TunnyComponent component) SetInputs(); } - private bool SetInputs() + private void SetInputs() { SetVariables(); SetObjectives(); SetAttributes(); - - return true; } - private bool SetVariables() + private void SetVariables() { Sliders = new List(); _genePool = new List(); @@ -65,7 +63,6 @@ private bool SetVariables() if (_inputGuids.Count == 0) { TunnyMessageBox.Show("No input variables found. Please connect a number slider to the input of the component.", "Tunny"); - return false; } foreach (IGH_DocumentObject input in _inputGuids.Select(guid => _document.FindObject(guid, true))) @@ -85,7 +82,6 @@ private bool SetVariables() SetInputSliderValues(variables); SetInputGenePoolValues(variables); Variables = variables; - return true; } private void SetInputSliderValues(ICollection variables) @@ -153,24 +149,21 @@ private void SetInputGenePoolValues(ICollection variables) } - private bool SetObjectives() + private void SetObjectives() { if (_component.Params.Input[1].SourceCount == 0) { TunnyMessageBox.Show("No objective found. Please connect a number to the objective of the component.", "Tunny"); - return false; } Objectives = _component.Params.Input[1].Sources.ToList(); - return true; } - private bool SetAttributes() + private void SetAttributes() { if (_component.Params.Input[2].SourceCount == 0) { _attributes = new GH_FishAttribute(); - return false; } IGH_StructureEnumerator enumerator = _component.Params.Input[2].Sources[0].VolatileData.AllData(true); @@ -182,8 +175,6 @@ private bool SetAttributes() break; } } - - return true; } private bool SetSliderValues(IList parameters) @@ -288,7 +279,6 @@ public List GetObjectiveValues() public List GetGeometryJson() { var json = new List(); - var option = new SerializationOptions(); if (_attributes.Value == null || !_attributes.Value.ContainsKey("Geometry")) { From 0a28c23a74601e78b9a040b4064999ec9a4c0011 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 26 Jun 2022 23:38:42 +0900 Subject: [PATCH 35/79] Clean codacy issues 2 --- Tunny/Type/GH_Fish.cs | 2 +- Tunny/Type/GH_FishAttribute.cs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Tunny/Type/GH_Fish.cs b/Tunny/Type/GH_Fish.cs index aa71e49b..aaa3674f 100644 --- a/Tunny/Type/GH_Fish.cs +++ b/Tunny/Type/GH_Fish.cs @@ -94,7 +94,7 @@ private void SetAttributeEachItem(StringBuilder sb, KeyValuePair valueStrings.Append(", "); } } - sb.AppendLine(" " + attr.Key + ": " + valueStrings.ToString()); + sb.AppendLine(" " + attr.Key + ": " + valueStrings); } private void SetObjectives(StringBuilder sb) diff --git a/Tunny/Type/GH_FishAttribute.cs b/Tunny/Type/GH_FishAttribute.cs index 6a3200ef..6f36e2fe 100644 --- a/Tunny/Type/GH_FishAttribute.cs +++ b/Tunny/Type/GH_FishAttribute.cs @@ -69,7 +69,8 @@ public override string ToString() valueStrings = string.Join(", ", geometryStrings); break; default: - throw new ArgumentException($"Unsupported type: {attr.Value.GetType()}"); + valueStrings = attr.Value.ToString(); + break; } sb.AppendLine(" " + attr.Key + ": " + valueStrings); } @@ -98,7 +99,9 @@ public override bool CastTo(ref T target) return true; } else + { return false; + } } private static Dictionary FromBase64(string base64) From f8e63a7f8919b69690de40cb1f06d6e9673cc785 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Mon, 27 Jun 2022 14:03:03 +0900 Subject: [PATCH 36/79] Update component place to Tunny tab --- Tunny/Component/ConstructFishAttribute.cs | 2 +- Tunny/Component/DeconstructFish.cs | 2 +- Tunny/Component/DecontstructFishAttribute.cs | 2 +- Tunny/Component/FishMarket.cs | 2 +- Tunny/Component/Param_Fish.cs | 2 +- Tunny/Component/Param_FishAttribute.cs | 2 +- Tunny/Component/TunnyComponent.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Tunny/Component/ConstructFishAttribute.cs b/Tunny/Component/ConstructFishAttribute.cs index 7403223e..d3071ecf 100644 --- a/Tunny/Component/ConstructFishAttribute.cs +++ b/Tunny/Component/ConstructFishAttribute.cs @@ -19,7 +19,7 @@ public class ConstructFishAttribute : GH_Component, IGH_VariableParameterCompone public ConstructFishAttribute() : base("Construct Fish Attribute", "ConstrFA", "Construct Fish Attribute.", - "Params", "Tunny") + "Tunny", "Tunny") { } diff --git a/Tunny/Component/DeconstructFish.cs b/Tunny/Component/DeconstructFish.cs index bc1530e2..b8fb50d3 100644 --- a/Tunny/Component/DeconstructFish.cs +++ b/Tunny/Component/DeconstructFish.cs @@ -18,7 +18,7 @@ public class DeconstructFish : GH_Component public DeconstructFish() : base("Deconstruct Fish", "DeconF", "Deconstruct Fish.", - "Params", "Tunny") + "Tunny", "Tunny") { } diff --git a/Tunny/Component/DecontstructFishAttribute.cs b/Tunny/Component/DecontstructFishAttribute.cs index 4d009446..2fe319aa 100644 --- a/Tunny/Component/DecontstructFishAttribute.cs +++ b/Tunny/Component/DecontstructFishAttribute.cs @@ -25,7 +25,7 @@ public class DeconstructFishAttribute : GH_Component, IGH_VariableParameterCompo public DeconstructFishAttribute() : base("Deconstruct Fish Attribute", "DeconFA", "Deconstruct Fish Attribute.", - "Params", "Tunny") + "Tunny", "Tunny") { } diff --git a/Tunny/Component/FishMarket.cs b/Tunny/Component/FishMarket.cs index 2566d6ad..50604269 100644 --- a/Tunny/Component/FishMarket.cs +++ b/Tunny/Component/FishMarket.cs @@ -28,7 +28,7 @@ public class FishMarket : GH_Component public FishMarket() : base("Fish Market", "FMarket", "A place to lay out the solutions we caught.", - "Params", "Tunny") + "Tunny", "Tunny") { } diff --git a/Tunny/Component/Param_Fish.cs b/Tunny/Component/Param_Fish.cs index f435bb97..7cf28ec6 100644 --- a/Tunny/Component/Param_Fish.cs +++ b/Tunny/Component/Param_Fish.cs @@ -15,7 +15,7 @@ public class Param_Fish : GH_PersistentParam public Param_Fish() : base("Fish", "Fish", "Fish caught by the optimization nets", - "Params", "Tunny") + "Tunny", "Tunny") { } diff --git a/Tunny/Component/Param_FishAttribute.cs b/Tunny/Component/Param_FishAttribute.cs index d3da5028..9fab7af3 100644 --- a/Tunny/Component/Param_FishAttribute.cs +++ b/Tunny/Component/Param_FishAttribute.cs @@ -15,7 +15,7 @@ public class Param_FishAttribute : GH_PersistentParam public Param_FishAttribute() : base("Fish Attribute", "FishAttr", "Attribute information to be added to each trial of optimization.", - "Params", "Tunny") + "Tunny", "Tunny") { } diff --git a/Tunny/Component/TunnyComponent.cs b/Tunny/Component/TunnyComponent.cs index 16815954..43f19af1 100644 --- a/Tunny/Component/TunnyComponent.cs +++ b/Tunny/Component/TunnyComponent.cs @@ -24,7 +24,7 @@ public partial class TunnyComponent : GH_Component, IDisposable public TunnyComponent() : base("Tunny", "Tunny", "Tunny is an optimization component wrapped in optuna.", - "Params", "Tunny") + "Tunny", "Tunny") { } From 249baa6a0765d3770e491a0f58ecf25a704f5373 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Mon, 27 Jun 2022 14:03:53 +0900 Subject: [PATCH 37/79] Fix no input error --- Tunny/Util/GrasshopperInOut.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index 66c1ce37..a017fb4d 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -63,6 +63,7 @@ private void SetVariables() if (_inputGuids.Count == 0) { TunnyMessageBox.Show("No input variables found. Please connect a number slider to the input of the component.", "Tunny"); + return; } foreach (IGH_DocumentObject input in _inputGuids.Select(guid => _document.FindObject(guid, true))) @@ -154,6 +155,7 @@ private void SetObjectives() if (_component.Params.Input[1].SourceCount == 0) { TunnyMessageBox.Show("No objective found. Please connect a number to the objective of the component.", "Tunny"); + return; } Objectives = _component.Params.Input[1].Sources.ToList(); @@ -164,6 +166,7 @@ private void SetAttributes() if (_component.Params.Input[2].SourceCount == 0) { _attributes = new GH_FishAttribute(); + return; } IGH_StructureEnumerator enumerator = _component.Params.Input[2].Sources[0].VolatileData.AllData(true); From 04490502e38bda0e2c41eb5036022541bb7c7d88 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Mon, 27 Jun 2022 14:05:06 +0900 Subject: [PATCH 38/79] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7de7a2d5..f5705494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release ### Changed +- Component location on the Tunny tab. - The output of the Tunny component is made into Fish, a type that summarizes the results. - The Geometry input of the Tunny component has been changed to Attribute to allow more attribute information to be handled. - When restoring the results from Tunny component as Fishes, those that the trial did not work and Objective could not get are not output. From 4aa7eb1021044f5fdf5f940b2659c071e805ec7b Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 29 Jun 2022 20:26:39 +0900 Subject: [PATCH 39/79] Add python related items licenses --- PYTHON_PACKAGE_LICENSES | 3766 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 3766 insertions(+) create mode 100644 PYTHON_PACKAGE_LICENSES diff --git a/PYTHON_PACKAGE_LICENSES b/PYTHON_PACKAGE_LICENSES new file mode 100644 index 00000000..46c4ee7c --- /dev/null +++ b/PYTHON_PACKAGE_LICENSES @@ -0,0 +1,3766 @@ +Some distributions of this package include a copy of the C Python dlls and standard library, which are covered by the Python license. +https://docs.python.org/3/license.html + + +autopage +0.5.1 +Apache Software License +Zane Bitter +https://github.com/zaneb/autopage +A library to provide automatic paging for console output + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + + +cliff +3.10.1 +Apache Software License +OpenStack +https://docs.openstack.org/cliff/latest/ +Command Line Interface Formulation Framework + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +pbr +5.9.0 +Apache Software License +OpenStack +https://docs.openstack.org/pbr/latest/ +Python Build Reasonableness + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + + +stevedore +3.5.0 +Apache Software License +OpenStack +https://docs.openstack.org/stevedore/latest/ +Manage dynamic plugins for Python applications + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +tenacity +8.0.1 +Apache Software License +Julien Danjou +https://github.com/jd/tenacity +Retry code until it succeeds + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +packaging +21.3 +Apache Software License; BSD License +Donald Stufft and individual contributors +https://github.com/pypa/packaging +Core utilities for Python packages +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made +under the terms of *both* these licenses. + + +MarkupSafe +2.1.1 +BSD License +Armin Ronacher +https://palletsprojects.com/p/markupsafe/ +Safely add untrusted strings to HTML/XML markup. +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +colorama +0.4.5 +BSD License +Jonathan Hartley +https://github.com/tartley/colorama +Cross-platform colored terminal text. +Copyright (c) 2010 Jonathan Hartley +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +joblib +1.1.0 +BSD License +Gael Varoquaux +https://joblib.readthedocs.io +Lightweight pipelining with Python functions +BSD 3-Clause License + +Copyright (c) 2008-2021, The joblib developers. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +numpy +1.23.0 +BSD License +Travis E. Oliphant et al. +https://www.numpy.org +NumPy is the fundamental package for array computing with Python. +Copyright (c) 2005-2022, NumPy Developers. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the NumPy Developers nor the names of any + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---- + +This binary distribution of NumPy also bundles the following software: + + +Name: OpenBLAS +Files: extra-dll\libopenb*.dll +Description: bundled as a dynamically linked library +Availability: https://github.com/xianyi/OpenBLAS/ +License: 3-clause BSD + Copyright (c) 2011-2014, The OpenBLAS Project + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. Neither the name of the OpenBLAS project nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Name: LAPACK +Files: extra-dll\libopenb*.dll +Description: bundled in OpenBLAS +Availability: https://github.com/xianyi/OpenBLAS/ +License 3-clause BSD + Copyright (c) 1992-2013 The University of Tennessee and The University + of Tennessee Research Foundation. All rights + reserved. + Copyright (c) 2000-2013 The University of California Berkeley. All + rights reserved. + Copyright (c) 2006-2013 The University of Colorado Denver. All rights + reserved. + + $COPYRIGHT$ + + Additional copyrights may follow + + $HEADER$ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + + - Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + The copyright holders provide no reassurances that the source code + provided does not infringe any patent, copyright, or any other + intellectual property rights of third parties. The copyright holders + disclaim any liability to any recipient for claims brought against + recipient by any third party for infringement of that parties + intellectual property rights. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Name: GCC runtime library +Files: extra-dll\*.dll +Description: statically linked, in DLL files compiled with gfortran only +Availability: https://gcc.gnu.org/viewcvs/gcc/ +License: GPLv3 + runtime exception + Copyright (C) 2002-2017 Free Software Foundation, Inc. + + Libgfortran is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgfortran is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . + + +Name: Microsoft Visual C++ Runtime Files +Files: extra-dll\msvcp140.dll +License: MSVC + https://www.visualstudio.com/license-terms/distributable-code-microsoft-visual-studio-2015-rc-microsoft-visual-studio-2015-sdk-rc-includes-utilities-buildserver-files/#visual-c-runtime + + Subject to the License Terms for the software, you may copy and + distribute with your program any of the files within the followng + folder and its subfolders except as noted below. You may not modify + these files. + + C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist + + You may not distribute the contents of the following folders: + + C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\debug_nonredist + C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\onecore\debug_nonredist + + Subject to the License Terms for the software, you may copy and + distribute the following files with your program in your program’s + application local folder or by deploying them into the Global + Assembly Cache (GAC): + + VC\atlmfc\lib\mfcmifc80.dll + VC\atlmfc\lib\amd64\mfcmifc80.dll + + +Name: Microsoft Visual C++ Runtime Files +Files: extra-dll\msvc*90.dll, extra-dll\Microsoft.VC90.CRT.manifest +License: MSVC + For your convenience, we have provided the following folders for + use when redistributing VC++ runtime files. Subject to the license + terms for the software, you may redistribute the folder + (unmodified) in the application local folder as a sub-folder with + no change to the folder name. You may also redistribute all the + files (*.dll and *.manifest) within a folder, listed below the + folder for your convenience, as an entire set. + + \VC\redist\x86\Microsoft.VC90.ATL\ + atl90.dll + Microsoft.VC90.ATL.manifest + \VC\redist\ia64\Microsoft.VC90.ATL\ + atl90.dll + Microsoft.VC90.ATL.manifest + \VC\redist\amd64\Microsoft.VC90.ATL\ + atl90.dll + Microsoft.VC90.ATL.manifest + \VC\redist\x86\Microsoft.VC90.CRT\ + msvcm90.dll + msvcp90.dll + msvcr90.dll + Microsoft.VC90.CRT.manifest + \VC\redist\ia64\Microsoft.VC90.CRT\ + msvcm90.dll + msvcp90.dll + msvcr90.dll + Microsoft.VC90.CRT.manifest + +---- + +Full text of license texts referred to above follows (that they are +listed below does not necessarily imply the conditions apply to the +present binary release): + +---- + +GCC RUNTIME LIBRARY EXCEPTION + +Version 3.1, 31 March 2009 + +Copyright (C) 2009 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +This GCC Runtime Library Exception ("Exception") is an additional +permission under section 7 of the GNU General Public License, version +3 ("GPLv3"). It applies to a given file (the "Runtime Library") that +bears a notice placed by the copyright holder of the file stating that +the file is governed by GPLv3 along with this Exception. + +When you use GCC to compile a program, GCC may combine portions of +certain GCC header files and runtime libraries with the compiled +program. The purpose of this Exception is to allow compilation of +non-GPL (including proprietary) programs to use, in this way, the +header files and runtime libraries covered by this Exception. + +0. Definitions. + +A file is an "Independent Module" if it either requires the Runtime +Library for execution after a Compilation Process, or makes use of an +interface provided by the Runtime Library, but is not otherwise based +on the Runtime Library. + +"GCC" means a version of the GNU Compiler Collection, with or without +modifications, governed by version 3 (or a specified later version) of +the GNU General Public License (GPL) with the option of using any +subsequent versions published by the FSF. + +"GPL-compatible Software" is software whose conditions of propagation, +modification and use would permit combination with GCC in accord with +the license of GCC. + +"Target Code" refers to output from any compiler for a real or virtual +target processor architecture, in executable form or suitable for +input to an assembler, loader, linker and/or execution +phase. Notwithstanding that, Target Code does not include data in any +format that is used as a compiler intermediate representation, or used +for producing a compiler intermediate representation. + +The "Compilation Process" transforms code entirely represented in +non-intermediate languages designed for human-written code, and/or in +Java Virtual Machine byte code, into Target Code. Thus, for example, +use of source code generators and preprocessors need not be considered +part of the Compilation Process, since the Compilation Process can be +understood as starting with the output of the generators or +preprocessors. + +A Compilation Process is "Eligible" if it is done using GCC, alone or +with other GPL-compatible software, or if it is done without using any +work based on GCC. For example, using non-GPL-compatible Software to +optimize any GCC intermediate representations would not qualify as an +Eligible Compilation Process. + +1. Grant of Additional Permission. + +You have permission to propagate a work of Target Code formed by +combining the Runtime Library with Independent Modules, even if such +propagation would otherwise violate the terms of GPLv3, provided that +all Target Code was generated by Eligible Compilation Processes. You +may then convey such a combination under terms of your choice, +consistent with the licensing of the Independent Modules. + +2. No Weakening of GCC Copyleft. + +The availability of this Exception does not imply any general +presumption that third-party software is unaffected by the copyleft +requirements of the license of GCC. + +---- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +prettytable +3.3.0 +BSD License +Luke Maurits +https://github.com/jazzband/prettytable +A simple Python library for easily displaying tabular data in a visually appealing ASCII table format +# Copyright (c) 2009-2014 Luke Maurits +# All rights reserved. +# With contributions from: +# * Chris Clark +# * Klein Stephane +# * John Filleau +# * Vladimir Vrzić +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + + +pyperclip +1.8.2 +BSD License +Al Sweigart +https://github.com/asweigart/pyperclip +A cross-platform clipboard module for Python. (Only handles plain text for now.) +Copyright (c) 2014, Al Sweigart +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +pyreadline3 +3.4.1 +BSD License +Bassem Girgis +https://pypi.python.org/pypi/pyreadline3/ +A python implementation of GNU readline. +# LICENSE + +## pyreadline3 copyright and licensing notes + +Unless indicated otherwise, files in this project are covered by a BSD-type +license, included below. + +Individual authors are the holders of the copyright for their code and are +listed in each file. + +Some files may be licensed under different conditions. Ultimately each file +indicates clearly the conditions under which its author/authors have +decided to publish the code. + +## pyreadline3 license + +pyreadline3 is released under a BSD-type license. + +Copyright (c) 2020 Bassem Girgis . + +Copyright (c) 2006-2020 J�rgen Stenarson . + +Copyright (c) 2003-2006 Gary Bishop + +Copyright (c) 2003-2006 Jack Trainor + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +a. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +b. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +c. Neither the name of the copyright holders nor the names of any + contributors to this software may be used to endorse or promote products + derived from this software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + + +scipy +1.8.1 +BSD License +UNKNOWN +https://www.scipy.org +SciPy: Scientific Library for Python +Copyright (c) 2001-2002 Enthought, Inc. 2003-2019, SciPy Developers. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---- + +This binary distribution of Scipy also bundles the following software: + + +Name: OpenBLAS +Files: extra-dll\libopenb*.dll +Description: bundled as a dynamically linked library +Availability: https://github.com/xianyi/OpenBLAS/ +License: 3-clause BSD + Copyright (c) 2011-2014, The OpenBLAS Project + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. Neither the name of the OpenBLAS project nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Name: LAPACK +Files: extra-dll\libopenb*.dll +Description: bundled in OpenBLAS +Availability: https://github.com/xianyi/OpenBLAS/ +License 3-clause BSD + Copyright (c) 1992-2013 The University of Tennessee and The University + of Tennessee Research Foundation. All rights + reserved. + Copyright (c) 2000-2013 The University of California Berkeley. All + rights reserved. + Copyright (c) 2006-2013 The University of Colorado Denver. All rights + reserved. + + $COPYRIGHT$ + + Additional copyrights may follow + + $HEADER$ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + + - Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + The copyright holders provide no reassurances that the source code + provided does not infringe any patent, copyright, or any other + intellectual property rights of third parties. The copyright holders + disclaim any liability to any recipient for claims brought against + recipient by any third party for infringement of that parties + intellectual property rights. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Name: GCC runtime library +Files: extra-dll\*.dll +Description: statically linked, in DLL files compiled with gfortran only +Availability: https://gcc.gnu.org/viewcvs/gcc/ +License: GPLv3 + runtime exception + Copyright (C) 2002-2017 Free Software Foundation, Inc. + + Libgfortran is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgfortran is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . + + +Name: Microsoft Visual C++ Runtime Files +Files: extra-dll\msvcp140.dll +License: MSVC + https://www.visualstudio.com/license-terms/distributable-code-microsoft-visual-studio-2015-rc-microsoft-visual-studio-2015-sdk-rc-includes-utilities-buildserver-files/#visual-c-runtime + + Subject to the License Terms for the software, you may copy and + distribute with your program any of the files within the followng + folder and its subfolders except as noted below. You may not modify + these files. + + C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist + + You may not distribute the contents of the following folders: + + C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\debug_nonredist + C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\onecore\debug_nonredist + + Subject to the License Terms for the software, you may copy and + distribute the following files with your program in your program’s + application local folder or by deploying them into the Global + Assembly Cache (GAC): + + VC\atlmfc\lib\mfcmifc80.dll + VC\atlmfc\lib\amd64\mfcmifc80.dll + + +Name: Microsoft Visual C++ Runtime Files +Files: extra-dll\msvc*90.dll, extra-dll\Microsoft.VC90.CRT.manifest +License: MSVC + For your convenience, we have provided the following folders for + use when redistributing VC++ runtime files. Subject to the license + terms for the software, you may redistribute the folder + (unmodified) in the application local folder as a sub-folder with + no change to the folder name. You may also redistribute all the + files (*.dll and *.manifest) within a folder, listed below the + folder for your convenience, as an entire set. + + \VC\redist\x86\Microsoft.VC90.ATL\ + atl90.dll + Microsoft.VC90.ATL.manifest + \VC\redist\ia64\Microsoft.VC90.ATL\ + atl90.dll + Microsoft.VC90.ATL.manifest + \VC\redist\amd64\Microsoft.VC90.ATL\ + atl90.dll + Microsoft.VC90.ATL.manifest + \VC\redist\x86\Microsoft.VC90.CRT\ + msvcm90.dll + msvcp90.dll + msvcr90.dll + Microsoft.VC90.CRT.manifest + \VC\redist\ia64\Microsoft.VC90.CRT\ + msvcm90.dll + msvcp90.dll + msvcr90.dll + Microsoft.VC90.CRT.manifest + +---- + +Full text of license texts referred to above follows (that they are +listed below does not necessarily imply the conditions apply to the +present binary release): + +---- + +GCC RUNTIME LIBRARY EXCEPTION + +Version 3.1, 31 March 2009 + +Copyright (C) 2009 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +This GCC Runtime Library Exception ("Exception") is an additional +permission under section 7 of the GNU General Public License, version +3 ("GPLv3"). It applies to a given file (the "Runtime Library") that +bears a notice placed by the copyright holder of the file stating that +the file is governed by GPLv3 along with this Exception. + +When you use GCC to compile a program, GCC may combine portions of +certain GCC header files and runtime libraries with the compiled +program. The purpose of this Exception is to allow compilation of +non-GPL (including proprietary) programs to use, in this way, the +header files and runtime libraries covered by this Exception. + +0. Definitions. + +A file is an "Independent Module" if it either requires the Runtime +Library for execution after a Compilation Process, or makes use of an +interface provided by the Runtime Library, but is not otherwise based +on the Runtime Library. + +"GCC" means a version of the GNU Compiler Collection, with or without +modifications, governed by version 3 (or a specified later version) of +the GNU General Public License (GPL) with the option of using any +subsequent versions published by the FSF. + +"GPL-compatible Software" is software whose conditions of propagation, +modification and use would permit combination with GCC in accord with +the license of GCC. + +"Target Code" refers to output from any compiler for a real or virtual +target processor architecture, in executable form or suitable for +input to an assembler, loader, linker and/or execution +phase. Notwithstanding that, Target Code does not include data in any +format that is used as a compiler intermediate representation, or used +for producing a compiler intermediate representation. + +The "Compilation Process" transforms code entirely represented in +non-intermediate languages designed for human-written code, and/or in +Java Virtual Machine byte code, into Target Code. Thus, for example, +use of source code generators and preprocessors need not be considered +part of the Compilation Process, since the Compilation Process can be +understood as starting with the output of the generators or +preprocessors. + +A Compilation Process is "Eligible" if it is done using GCC, alone or +with other GPL-compatible software, or if it is done without using any +work based on GCC. For example, using non-GPL-compatible Software to +optimize any GCC intermediate representations would not qualify as an +Eligible Compilation Process. + +1. Grant of Additional Permission. + +You have permission to propagate a work of Target Code formed by +combining the Runtime Library with Independent Modules, even if such +propagation would otherwise violate the terms of GPLv3, provided that +all Target Code was generated by Eligible Compilation Processes. You +may then convey such a combination under terms of your choice, +consistent with the licensing of the Independent Modules. + +2. No Weakening of GCC Copyleft. + +The availability of this Exception does not imply any general +presumption that third-party software is unaffected by the copyleft +requirements of the license of GCC. + +---- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + + +threadpoolctl +3.1.0 +BSD License +Thomas Moreau +https://github.com/joblib/threadpoolctl +threadpoolctl +Copyright (c) 2019, threadpoolctl contributors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +plotly +5.9.0 +MIT +Chris P +https://plotly.com/python/ +An open-source, interactive data visualization library for Python +The MIT License (MIT) + +Copyright (c) 2016-2018 Plotly, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +Mako +1.2.0 +MIT License +Mike Bayer +https://www.makotemplates.org/ +A super-fast templating language that borrows the best ideas from the existing templating languages. +Copyright 2006-2021 the Mako authors and contributors . + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +PyYAML +6.0 +MIT License +Kirill Simonov +https://pyyaml.org/ +YAML parser and emitter for Python +Copyright (c) 2017-2021 Ingy döt Net +Copyright (c) 2006-2016 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +SQLAlchemy +1.4.39 +MIT License +Mike Bayer +https://www.sqlalchemy.org +Database Abstraction Library +Copyright 2005-2022 SQLAlchemy authors and contributors . + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +alembic +1.8.0 +MIT License +Mike Bayer +https://alembic.sqlalchemy.org +A database migration tool for SQLAlchemy. +Copyright 2009-2022 Michael Bayer. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +attrs +21.4.0 +MIT License +Hynek Schlawack +https://www.attrs.org/ +Classes Without Boilerplate +The MIT License (MIT) + +Copyright (c) 2015 Hynek Schlawack + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +bottle +0.12.21 +MIT License +Marcel Hellkamp +http://bottlepy.org/ +Fast and simple WSGI-framework for small web-applications. +Copyright (c) 2012, Marcel Hellkamp. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +cmaes +0.8.2 +MIT License +Masashi Shibata +https://github.com/CyberAgent/cmaes +Lightweight Covariance Matrix Adaptation Evolution Strategy (CMA-ES) implementation for Python 3. +MIT License + +Copyright (c) 2020-2021 CyberAgent, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +cmd2 +2.4.1 +MIT License +Catherine Devlin +https://github.com/python-cmd2/cmd2 +cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python +The MIT License (MIT) + +Copyright (c) 2008-2022 Catherine Devlin and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +colorlog +6.6.0 +MIT License +Sam Clements +https://github.com/borntyping/python-colorlog +Add colours to the output of Python's logging module. +The MIT License (MIT) + +Copyright (c) 2012-2021 Sam Clements + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +greenlet +1.1.2 +MIT License +Alexey Borzenkov +https://greenlet.readthedocs.io/ +Lightweight in-process concurrent programming +The following files are derived from Stackless Python and are subject to the +same license as Stackless Python: + + src/greenlet/slp_platformselect.h + files in src/greenlet/platform/ directory + +See LICENSE.PSF and http://www.stackless.com/ for details. + +Unless otherwise noted, the files in greenlet have been released under the +following MIT license: + +Copyright (c) Armin Rigo, Christian Tismer and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +optuna +2.10.1 +MIT License +Takuya Akiba +https://optuna.org/ +A hyperparameter optimization framework +MIT License + +Copyright (c) 2018 Preferred Networks, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +optuna-dashboard +0.7.1 +MIT License +Masashi Shibata +https://github.com/optuna/optuna-dashboard +Real-time dashboard for Optuna. +MIT License + +Copyright (c) 2020-2021 Masashi SHIBATA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +pyparsing +3.0.9 +MIT License +UNKNOWN +UNKNOWN +pyparsing module - Classes and methods to define and execute parsing grammars +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +wcwidth +0.2.5 +MIT License +Jeff Quast +https://github.com/jquast/wcwidth +Measures the displayed width of unicode strings in a terminal +The MIT License (MIT) + +Copyright (c) 2014 Jeff Quast + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Markus Kuhn -- 2007-05-26 (Unicode 5.0) + +Permission to use, copy, modify, and distribute this software +for any purpose and without fee is hereby granted. The author +disclaims all warranties with regard to this software. + + +tqdm +4.64.0 +MIT License; Mozilla Public License 2.0 (MPL 2.0) +UNKNOWN +https://tqdm.github.io +Fast, Extensible Progress Meter +`tqdm` is a product of collaborative work. +Unless otherwise stated, all authors (see commit logs) retain copyright +for their respective work, and release the work under the MIT licence +(text below). + +Exceptions or notable authors are listed below +in reverse chronological order: + +* files: * + MPLv2.0 2015-2021 (c) Casper da Costa-Luis + [casperdcl](https://github.com/casperdcl). +* files: tqdm/_tqdm.py + MIT 2016 (c) [PR #96] on behalf of Google Inc. +* files: tqdm/_tqdm.py setup.py README.rst MANIFEST.in .gitignore + MIT 2013 (c) Noam Yorav-Raphael, original author. + +[PR #96]: https://github.com/tqdm/tqdm/pull/96 + + +Mozilla Public Licence (MPL) v. 2.0 - Exhibit A +----------------------------------------------- + +This Source Code Form is subject to the terms of the +Mozilla Public License, v. 2.0. +If a copy of the MPL was not distributed with this project, +You can obtain one at https://mozilla.org/MPL/2.0/. + + +MIT License (MIT) +----------------- + +Copyright (c) 2013 noamraph + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +scikit-learn +1.1.1 +new BSD +UNKNOWN +http://scikit-learn.org +A set of python modules for machine learning and data mining +BSD 3-Clause License + +Copyright (c) 2007-2021 The scikit-learn developers. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + From ce801263bc42d4bb75bb312092a859e7ddf8b3e2 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 29 Jun 2022 21:26:11 +0900 Subject: [PATCH 40/79] Add requirement.txt to spcify package version --- Tunny/Lib/requirements.txt | 32 ++++++++++++++++++++++++++++++++ Tunny/Tunny.csproj | 3 +++ Tunny/Util/PythonInstaller.cs | 26 ++++++++++++-------------- 3 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 Tunny/Lib/requirements.txt diff --git a/Tunny/Lib/requirements.txt b/Tunny/Lib/requirements.txt new file mode 100644 index 00000000..27e34278 --- /dev/null +++ b/Tunny/Lib/requirements.txt @@ -0,0 +1,32 @@ +alembic==1.8.0 +attrs==21.4.0 +autopage==0.5.1 +bottle==0.12.21 +cliff==3.10.1 +cmaes==0.8.2 +cmd2==2.4.1 +colorama==0.4.5 +colorlog==6.6.0 +greenlet==1.1.2 +joblib==1.1.0 +Mako==1.2.0 +MarkupSafe==2.1.1 +numpy==1.23.0 +optuna==2.10.1 +optuna-dashboard==0.7.1 +packaging==21.3 +pbr==5.9.0 +plotly==5.9.0 +prettytable==3.3.0 +pyparsing==3.0.9 +pyperclip==1.8.2 +pyreadline3==3.4.1 +PyYAML==6.0 +scikit-learn==1.1.1 +scipy==1.8.1 +SQLAlchemy==1.4.39 +stevedore==3.5.0 +tenacity==8.0.1 +threadpoolctl==3.1.0 +tqdm==4.64.0 +wcwidth==0.2.5 diff --git a/Tunny/Tunny.csproj b/Tunny/Tunny.csproj index da76860f..23d08b99 100644 --- a/Tunny/Tunny.csproj +++ b/Tunny/Tunny.csproj @@ -56,6 +56,9 @@ + + PreserveNewest + True \ diff --git a/Tunny/Util/PythonInstaller.cs b/Tunny/Util/PythonInstaller.cs index c78c3a04..67de980b 100644 --- a/Tunny/Util/PythonInstaller.cs +++ b/Tunny/Util/PythonInstaller.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.ComponentModel; +using System.IO; using System.Linq; using Python.Included; @@ -69,21 +71,17 @@ internal static string GetEmbeddedPythonPath() private static string[] GetTunnyPackageList() { - return new[] + string line = string.Empty; + var pipPackages = new List(); + + using (var sr = new StreamReader("./Lib/requirements.txt")) { - "alembic", "attrs", "autopage", - "bottle", "cliff", "cmaes", - "cmd2", "colorama", "colorlog", - "greenlet", "joblib", "Mako", - "MarkupSafe", "numpy", "optuna", - "optuna-dashboard", "packaging", - "pbr", "plotly", "prettytable", - "pyparsing", "pyperclip","pyreadline3", - "PyYAML", "scikit-learn", - "scipy", "six", "sklearn", - "SQLAlchemy", "stevedore","tenacity", - "threadpoolctl", "tqdm", "wcwidth", - }; + while ((line = sr.ReadLine()) != null) + { + pipPackages.Add(line); + } + } + return pipPackages.ToArray(); } } } From d4817b1120b0b344e7705e6b5015e5b8117c1aa1 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 29 Jun 2022 21:37:14 +0900 Subject: [PATCH 41/79] Clean variable name --- Tunny/Util/PythonInstaller.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tunny/Util/PythonInstaller.cs b/Tunny/Util/PythonInstaller.cs index 67de980b..c692472f 100644 --- a/Tunny/Util/PythonInstaller.cs +++ b/Tunny/Util/PythonInstaller.cs @@ -54,8 +54,8 @@ internal static bool CheckPackagesIsInstalled() } foreach (string package in packageList) { - string[] aa = { "bottle", "optuna-dashboard", "six", "PyYAML", "scikit-learn", "threadpoolctl" }; - if (!Installer.IsModuleInstalled(package) && !aa.Contains(package)) + string[] singleFilePackages = { "bottle", "optuna-dashboard", "six", "PyYAML", "scikit-learn", "threadpoolctl" }; + if (!Installer.IsModuleInstalled(package) && !singleFilePackages.Contains(package)) { return false; } From f8466663fbe5b762af10b440d0a18842797e6882 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 29 Jun 2022 21:40:27 +0900 Subject: [PATCH 42/79] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5705494..81923e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - Component that separates optimization results into Variables, Objectives, and Attributes - Deconstruct Fish Attribute component - Components that output each attribute +- Python package licenses to clearly state the license of each package. +- requirements.txt file to avoid conflict python packages versions. ### Changed From 89c997577a49a7e1011469a1863990cdc2778203 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 29 Jun 2022 22:23:54 +0900 Subject: [PATCH 43/79] Fix FishMarket tag isnot arrayed --- Tunny/Component/FishMarket.cs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Tunny/Component/FishMarket.cs b/Tunny/Component/FishMarket.cs index 50604269..53b6955b 100644 --- a/Tunny/Component/FishMarket.cs +++ b/Tunny/Component/FishMarket.cs @@ -114,23 +114,32 @@ private bool SetGeometryToResultArray(int countY, GH_Structure { return false; } - Vector3d xVec = _settings.Plane.XAxis * (_settings.XInterval * countX); + Vector3d moveVec = _settings.Plane.XAxis * (_settings.XInterval * countX) + yVec; if (fishGeometries[index] != null) { - Point3d modelMinPt = GetUnionBoundingBoxMinPt(fishGeometries[index]); - foreach (GeometryBase geometry in fishGeometries[index]) - { - GeometryBase dupGeometry = geometry.Duplicate(); - dupGeometry.Rotate(Vector3d.VectorAngle(Vector3d.XAxis, _settings.Plane.XAxis), Vector3d.ZAxis, modelMinPt); - dupGeometry.Translate(xVec + yVec + new Vector3d(_settings.Plane.Origin) - new Vector3d(modelMinPt)); - arrayedGeometries.Append(Converter.GeometryBaseToGoo(dupGeometry), new GH_Path(0, _fishes[index].Value.ModelNumber)); - } - _tagPlanes.Add(new Plane(modelMinPt - _settings.Plane.YAxis * 2.5 * _size, _settings.Plane.XAxis, _settings.Plane.YAxis)); + MoveGeometries(index, moveVec, fishGeometries, arrayedGeometries); } } return true; } + //FIXME: Possibly heavy because deep copying here and doing it for all geometry every time. + private void MoveGeometries(int index, Vector3d moveVec, IReadOnlyList> fishGeometries, GH_Structure arrayedGeometries) + { + var movedGeometries = new GeometryBase[fishGeometries[index].Count]; + Point3d modelMinPt = GetUnionBoundingBoxMinPt(fishGeometries[index]); + foreach ((GeometryBase geometry, int i) in fishGeometries[index].Select((g, i) => (g, i))) + { + GeometryBase dupGeometry = geometry.Duplicate(); + dupGeometry.Rotate(Vector3d.VectorAngle(Vector3d.XAxis, _settings.Plane.XAxis), Vector3d.ZAxis, modelMinPt); + dupGeometry.Translate(moveVec + new Vector3d(_settings.Plane.Origin) - new Vector3d(modelMinPt)); + arrayedGeometries.Append(Converter.GeometryBaseToGoo(dupGeometry), new GH_Path(0, _fishes[index].Value.ModelNumber)); + movedGeometries[i] = dupGeometry; + } + modelMinPt = GetUnionBoundingBoxMinPt(movedGeometries); + _tagPlanes.Add(new Plane(modelMinPt - _settings.Plane.YAxis * 2.5 * _size, _settings.Plane.XAxis, _settings.Plane.YAxis)); + } + private static Point3d GetUnionBoundingBoxMinPt(IEnumerable geometryBases) { var minPt = new Point3d(double.MaxValue, double.MaxValue, double.MaxValue); From 8d4336344d287ce7fc40da3acfcc9bad576f5d15 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 29 Jun 2022 23:33:30 +0900 Subject: [PATCH 44/79] Fix required.txt path error --- Tunny/Util/PythonInstaller.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tunny/Util/PythonInstaller.cs b/Tunny/Util/PythonInstaller.cs index c692472f..d9c0fc86 100644 --- a/Tunny/Util/PythonInstaller.cs +++ b/Tunny/Util/PythonInstaller.cs @@ -32,7 +32,7 @@ private static void InstallPackages(BackgroundWorker worker, string[] packageLis { for (int i = 0; i < packageList.Length; i++) { - string packageName = packageList[i] == "plotly" + string packageName = packageList[i].Split('=')[0] == "plotly" ? packageList[i] + "... This package will take time to install. Please wait" : packageList[i]; worker.ReportProgress((i + 2) * 100 / installItems, "Now installing " + packageName + "..."); @@ -43,7 +43,7 @@ private static void InstallPackages(BackgroundWorker worker, string[] packageLis internal static bool CheckPackagesIsInstalled() { Installer.InstallPath = Path; - string[] packageList = GetTunnyPackageList(); + string[] packageList = GetTunnyPackageList().Select(s => s.Split('=')[0]).ToArray(); if (!Installer.IsPythonInstalled()) { return false; @@ -74,7 +74,7 @@ private static string[] GetTunnyPackageList() string line = string.Empty; var pipPackages = new List(); - using (var sr = new StreamReader("./Lib/requirements.txt")) + using (var sr = new StreamReader(Path + "/Lib/requirements.txt")) { while ((line = sr.ReadLine()) != null) { From 135445073785aeefc68bd8abdf3071965eb90638 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 30 Jun 2022 00:03:47 +0900 Subject: [PATCH 45/79] Update version related files --- .github/workflows/build-component.yml | 1 + README.md | 1 + Samples/sample.gh | Bin 155316 -> 202055 bytes Tunny/Tunny.csproj | 2 +- 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-component.yml b/.github/workflows/build-component.yml index deecbb3d..26e4d2bc 100644 --- a/.github/workflows/build-component.yml +++ b/.github/workflows/build-component.yml @@ -46,6 +46,7 @@ jobs: run: | cp ./Samples ./Release/Samples -recurse cp ./LICENSE ./Release/LICENSE + cp ./PYTHON_PACKAGE_LICENSE ./Release/PYTHON_PACKAGE_LICENSE cp ./Tunny/bin/Release/net48 ./Release/Tunny -recurse - name: Upload release build of plugin as artefact diff --git a/README.md b/README.md index c4a2bd21..93892471 100644 --- a/README.md +++ b/README.md @@ -193,3 +193,4 @@ Copyright© 2022, hrntsm Release package is embedded Python runtime & optuna libraries. These depend on their own licenses. +Please see PYTHON_PACKAGE_LICENSE for more license informations. diff --git a/Samples/sample.gh b/Samples/sample.gh index ef0a428f3164d1909da836a6dc71805fc8677661..798c6506775b13c7e1c9d40d3f939ea02bbf179b 100644 GIT binary patch literal 202055 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%0D99svT3xr7 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�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

ma#%g_~48@AnBl{IX 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 Z1}9cYCv)fbJBh_^vw+kaM5NvG{vQ^6n1cWS diff --git a/Tunny/Tunny.csproj b/Tunny/Tunny.csproj index 23d08b99..f4a07bf1 100644 --- a/Tunny/Tunny.csproj +++ b/Tunny/Tunny.csproj @@ -2,7 +2,7 @@ net48 - 0.3.0 + 0.4.0 Tunny Tunny is an optimization component wrapped in optuna. .gha From ec0fa4d90910069ac37669737c4e5d8c7fca9cfd Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 3 Jul 2022 16:21:31 +0900 Subject: [PATCH 46/79] Update ui to clear meanings of restore tab ui --- .../{RestoreLoop.cs => OutputLoop.cs} | 30 +- Tunny/Settings/Result.cs | 2 +- Tunny/Solver/Optuna.cs | 2 +- Tunny/UI/OptimizationWindow.Designer.cs | 499 ++++++++++-------- Tunny/UI/OptimizationWindow.cs | 20 +- Tunny/UI/OptimizationWindow.resx | 6 +- Tunny/UI/OptimizeWindowTab/FileTab.cs | 20 + Tunny/UI/OptimizeWindowTab/OutputTab.cs | 96 ++++ Tunny/UI/OptimizeWindowTab/ResultTab.cs | 91 ---- Tunny/UI/OptimizeWindowTab/VisualizeTab.cs | 33 ++ Tunny/Util/OutputMode.cs | 10 + 11 files changed, 469 insertions(+), 340 deletions(-) rename Tunny/Optimization/{RestoreLoop.cs => OutputLoop.cs} (74%) create mode 100644 Tunny/UI/OptimizeWindowTab/FileTab.cs create mode 100644 Tunny/UI/OptimizeWindowTab/OutputTab.cs delete mode 100644 Tunny/UI/OptimizeWindowTab/ResultTab.cs create mode 100644 Tunny/UI/OptimizeWindowTab/VisualizeTab.cs create mode 100644 Tunny/Util/OutputMode.cs diff --git a/Tunny/Optimization/RestoreLoop.cs b/Tunny/Optimization/OutputLoop.cs similarity index 74% rename from Tunny/Optimization/RestoreLoop.cs rename to Tunny/Optimization/OutputLoop.cs index aec4306a..477cfda2 100644 --- a/Tunny/Optimization/RestoreLoop.cs +++ b/Tunny/Optimization/OutputLoop.cs @@ -13,14 +13,14 @@ namespace Tunny.Optimization { - internal static class RestoreLoop + internal static class OutputLoop { private static BackgroundWorker s_worker; private static TunnyComponent s_component; public static string StudyName; public static string[] NickNames; public static int[] Indices; - public static string Mode; + public static OutputMode Mode; internal static void Run(object sender, DoWorkEventArgs e) { @@ -33,30 +33,14 @@ internal static void Run(object sender, DoWorkEventArgs e) ModelResult[] modelResult = optunaSolver.GetModelResult(Indices, StudyName); if (modelResult.Length == 0) { - TunnyMessageBox.Show("There are no restore models. Please check study name.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); + TunnyMessageBox.Show("There are no output models. Please check study name.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } - switch (Mode) + for (int i = 0; i < modelResult.Length; i++) { - case "Restore": - for (int i = 0; i < modelResult.Length; i++) - { - SetResultToFish(fishes, modelResult[i], NickNames); - s_worker.ReportProgress(i * 100 / modelResult.Length); - } - break; - case "Reflect": - if (modelResult.Length > 1) - { - TunnyMessageBox.Show( - "You input multi restore model numbers, but this function only reflect variables to slider or genepool to first one.", - "Tunny" - ); - } - SetResultToFish(fishes, modelResult[0], NickNames); - s_worker.ReportProgress(100); - break; + SetResultToFish(fishes, modelResult[i], NickNames); + s_worker.ReportProgress(i * 100 / modelResult.Length); } s_component.Fishes = fishes.ToArray(); @@ -66,7 +50,7 @@ internal static void Run(object sender, DoWorkEventArgs e) { s_worker.CancelAsync(); } - TunnyMessageBox.Show("Restore completed successfully.", "Tunny"); + TunnyMessageBox.Show("Output result to fish completed successfully.", "Tunny"); } private static void SetResultToFish(ICollection fishes, ModelResult model, IEnumerable nickname) diff --git a/Tunny/Settings/Result.cs b/Tunny/Settings/Result.cs index 83f6bdb2..3d721548 100644 --- a/Tunny/Settings/Result.cs +++ b/Tunny/Settings/Result.cs @@ -2,7 +2,7 @@ namespace Tunny.Settings { public class Result { - public string RestoreNumberString { get; set; } = "-1"; + public string OutputNumberString { get; set; } = "0"; public int SelectVisualizeType { get; set; } = 3; } } diff --git a/Tunny/Solver/Optuna.cs b/Tunny/Solver/Optuna.cs index 0807f571..cf475fbf 100644 --- a/Tunny/Solver/Optuna.cs +++ b/Tunny/Solver/Optuna.cs @@ -77,7 +77,7 @@ EvaluatedGHResult Eval(double[] x, int progress) } } - public void ShowResultVisualize(string visualize, string studyName) + public void ShowSelectedTypePlot(string visualize, string studyName) { string storage = "sqlite:///" + _componentFolder + "/Tunny_Opt_Result.db"; PythonEngine.Initialize(); diff --git a/Tunny/UI/OptimizationWindow.Designer.cs b/Tunny/UI/OptimizationWindow.Designer.cs index b9f18a3a..bce846f9 100644 --- a/Tunny/UI/OptimizationWindow.Designer.cs +++ b/Tunny/UI/OptimizationWindow.Designer.cs @@ -43,6 +43,20 @@ private void InitializeComponent() this.studyNameTextBox = new System.Windows.Forms.TextBox(); this.optimizeTabControl = new System.Windows.Forms.TabControl(); this.optimizeTabPage = new System.Windows.Forms.TabPage(); + this.visualizeTabPage = new System.Windows.Forms.TabPage(); + this.dashboardButton = new System.Windows.Forms.Button(); + this.visualizeButton = new System.Windows.Forms.Button(); + this.visualizeTypeLabel = new System.Windows.Forms.Label(); + this.visualizeTypeComboBox = new System.Windows.Forms.ComboBox(); + this.outputTabPage = new System.Windows.Forms.TabPage(); + this.outputAllTrialsButton = new System.Windows.Forms.Button(); + this.outputParatoSolutionButton = new System.Windows.Forms.Button(); + this.reflectToSliderButton = new System.Windows.Forms.Button(); + this.outputStopButton = new System.Windows.Forms.Button(); + this.outputProgressBar = new System.Windows.Forms.ProgressBar(); + this.outputModelNumberButton = new System.Windows.Forms.Button(); + this.outputModelNumTextBox = new System.Windows.Forms.TextBox(); + this.outputModelLabel = new System.Windows.Forms.Label(); this.settingsTabPage = new System.Windows.Forms.TabPage(); this.settingsToJson = new System.Windows.Forms.Button(); this.settingsOpenAPIPage = new System.Windows.Forms.Button(); @@ -50,33 +64,25 @@ private void InitializeComponent() this.settingsFolderOpen = new System.Windows.Forms.Button(); this.settingLabel = new System.Windows.Forms.Label(); this.settingsFromJson = new System.Windows.Forms.Button(); - this.resultTabPage = new System.Windows.Forms.TabPage(); - this.restoreReflectButton = new System.Windows.Forms.Button(); - this.restoreStopButton = new System.Windows.Forms.Button(); - this.restoreProgressBar = new System.Windows.Forms.ProgressBar(); - this.restoreRunButton = new System.Windows.Forms.Button(); - this.restoreModelNumTextBox = new System.Windows.Forms.TextBox(); - this.restoreModelLabel = new System.Windows.Forms.Label(); + this.fileTabPage = new System.Windows.Forms.TabPage(); this.openResultFolderButton = new System.Windows.Forms.Button(); this.clearResultButton = new System.Windows.Forms.Button(); - this.visualizeButton = new System.Windows.Forms.Button(); - this.visualizeTypeLabel = new System.Windows.Forms.Label(); - this.visualizeTypeComboBox = new System.Windows.Forms.ComboBox(); - this.restoreBackgroundWorker = new System.ComponentModel.BackgroundWorker(); - this.dashboardButton = new System.Windows.Forms.Button(); + this.outputResultBackgroundWorker = new System.ComponentModel.BackgroundWorker(); ((System.ComponentModel.ISupportInitialize)(this.nTrialNumUpDown)).BeginInit(); this.optimizeTabControl.SuspendLayout(); this.optimizeTabPage.SuspendLayout(); + this.visualizeTabPage.SuspendLayout(); + this.outputTabPage.SuspendLayout(); this.settingsTabPage.SuspendLayout(); - this.resultTabPage.SuspendLayout(); + this.fileTabPage.SuspendLayout(); this.SuspendLayout(); // // optimizeRunButton // - this.optimizeRunButton.Location = new System.Drawing.Point(13, 168); - this.optimizeRunButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.optimizeRunButton.Location = new System.Drawing.Point(20, 252); + this.optimizeRunButton.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.optimizeRunButton.Name = "optimizeRunButton"; - this.optimizeRunButton.Size = new System.Drawing.Size(120, 29); + this.optimizeRunButton.Size = new System.Drawing.Size(180, 44); this.optimizeRunButton.TabIndex = 0; this.optimizeRunButton.Text = "RunOptimize"; this.optimizeRunButton.UseVisualStyleBackColor = true; @@ -85,10 +91,10 @@ private void InitializeComponent() // optimizeStopButton // this.optimizeStopButton.Enabled = false; - this.optimizeStopButton.Location = new System.Drawing.Point(155, 168); - this.optimizeStopButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.optimizeStopButton.Location = new System.Drawing.Point(232, 252); + this.optimizeStopButton.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.optimizeStopButton.Name = "optimizeStopButton"; - this.optimizeStopButton.Size = new System.Drawing.Size(87, 29); + this.optimizeStopButton.Size = new System.Drawing.Size(130, 44); this.optimizeStopButton.TabIndex = 1; this.optimizeStopButton.Text = "Stop"; this.optimizeStopButton.UseVisualStyleBackColor = true; @@ -96,15 +102,15 @@ private void InitializeComponent() // // nTrialNumUpDown // - this.nTrialNumUpDown.Location = new System.Drawing.Point(131, 40); - this.nTrialNumUpDown.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.nTrialNumUpDown.Location = new System.Drawing.Point(196, 60); + this.nTrialNumUpDown.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.nTrialNumUpDown.Maximum = new decimal(new int[] { 10000, 0, 0, 0}); this.nTrialNumUpDown.Name = "nTrialNumUpDown"; - this.nTrialNumUpDown.Size = new System.Drawing.Size(110, 23); + this.nTrialNumUpDown.Size = new System.Drawing.Size(165, 30); this.nTrialNumUpDown.TabIndex = 2; this.nTrialNumUpDown.ThousandsSeparator = true; this.nTrialNumUpDown.Value = new decimal(new int[] { @@ -116,9 +122,10 @@ private void InitializeComponent() // nTrialText // this.nTrialText.AutoSize = true; - this.nTrialText.Location = new System.Drawing.Point(10, 42); + this.nTrialText.Location = new System.Drawing.Point(15, 63); + this.nTrialText.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.nTrialText.Name = "nTrialText"; - this.nTrialText.Size = new System.Drawing.Size(96, 15); + this.nTrialText.Size = new System.Drawing.Size(142, 23); this.nTrialText.TabIndex = 3; this.nTrialText.Text = "Number of trial"; // @@ -127,20 +134,20 @@ private void InitializeComponent() this.loadIfExistsCheckBox.AutoSize = true; this.loadIfExistsCheckBox.Checked = true; this.loadIfExistsCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; - this.loadIfExistsCheckBox.Location = new System.Drawing.Point(13, 90); - this.loadIfExistsCheckBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.loadIfExistsCheckBox.Location = new System.Drawing.Point(20, 135); + this.loadIfExistsCheckBox.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.loadIfExistsCheckBox.Name = "loadIfExistsCheckBox"; - this.loadIfExistsCheckBox.Size = new System.Drawing.Size(160, 19); + this.loadIfExistsCheckBox.Size = new System.Drawing.Size(237, 27); this.loadIfExistsCheckBox.TabIndex = 5; this.loadIfExistsCheckBox.Text = "Load if study file exists"; this.loadIfExistsCheckBox.UseVisualStyleBackColor = true; // // optimizeProgressBar // - this.optimizeProgressBar.Location = new System.Drawing.Point(13, 205); - this.optimizeProgressBar.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.optimizeProgressBar.Location = new System.Drawing.Point(20, 308); + this.optimizeProgressBar.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.optimizeProgressBar.Name = "optimizeProgressBar"; - this.optimizeProgressBar.Size = new System.Drawing.Size(227, 28); + this.optimizeProgressBar.Size = new System.Drawing.Size(340, 42); this.optimizeProgressBar.TabIndex = 6; // // samplerComboBox @@ -152,49 +159,54 @@ private void InitializeComponent() "CMA-ES", "Random", "Grid"}); - this.samplerComboBox.Location = new System.Drawing.Point(99, 8); - this.samplerComboBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.samplerComboBox.Location = new System.Drawing.Point(148, 12); + this.samplerComboBox.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.samplerComboBox.Name = "samplerComboBox"; - this.samplerComboBox.Size = new System.Drawing.Size(140, 23); + this.samplerComboBox.Size = new System.Drawing.Size(208, 31); this.samplerComboBox.TabIndex = 7; // // samplerTypeText // this.samplerTypeText.AutoSize = true; - this.samplerTypeText.Location = new System.Drawing.Point(10, 11); + this.samplerTypeText.Location = new System.Drawing.Point(15, 16); + this.samplerTypeText.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.samplerTypeText.Name = "samplerTypeText"; - this.samplerTypeText.Size = new System.Drawing.Size(56, 15); + this.samplerTypeText.Size = new System.Drawing.Size(81, 23); this.samplerTypeText.TabIndex = 8; this.samplerTypeText.Text = "Sampler"; // // studyNameLabel // this.studyNameLabel.AutoSize = true; - this.studyNameLabel.Location = new System.Drawing.Point(10, 115); + this.studyNameLabel.Location = new System.Drawing.Point(15, 172); + this.studyNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.studyNameLabel.Name = "studyNameLabel"; - this.studyNameLabel.Size = new System.Drawing.Size(80, 15); + this.studyNameLabel.Size = new System.Drawing.Size(116, 23); this.studyNameLabel.TabIndex = 9; this.studyNameLabel.Text = "Study Name"; // // studyNameTextBox // - this.studyNameTextBox.Location = new System.Drawing.Point(101, 111); - this.studyNameTextBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.studyNameTextBox.Location = new System.Drawing.Point(152, 166); + this.studyNameTextBox.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.studyNameTextBox.Name = "studyNameTextBox"; - this.studyNameTextBox.Size = new System.Drawing.Size(138, 23); + this.studyNameTextBox.Size = new System.Drawing.Size(205, 30); this.studyNameTextBox.TabIndex = 10; this.studyNameTextBox.Text = "study1"; // // optimizeTabControl // this.optimizeTabControl.Controls.Add(this.optimizeTabPage); + this.optimizeTabControl.Controls.Add(this.visualizeTabPage); + this.optimizeTabControl.Controls.Add(this.outputTabPage); this.optimizeTabControl.Controls.Add(this.settingsTabPage); - this.optimizeTabControl.Controls.Add(this.resultTabPage); - this.optimizeTabControl.Location = new System.Drawing.Point(14, 15); - this.optimizeTabControl.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.optimizeTabControl.Controls.Add(this.fileTabPage); + this.optimizeTabControl.Location = new System.Drawing.Point(21, 22); + this.optimizeTabControl.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); + this.optimizeTabControl.Multiline = true; this.optimizeTabControl.Name = "optimizeTabControl"; this.optimizeTabControl.SelectedIndex = 0; - this.optimizeTabControl.Size = new System.Drawing.Size(258, 270); + this.optimizeTabControl.Size = new System.Drawing.Size(387, 438); this.optimizeTabControl.TabIndex = 11; // // optimizeTabPage @@ -209,15 +221,181 @@ private void InitializeComponent() this.optimizeTabPage.Controls.Add(this.optimizeProgressBar); this.optimizeTabPage.Controls.Add(this.nTrialText); this.optimizeTabPage.Controls.Add(this.loadIfExistsCheckBox); - this.optimizeTabPage.Location = new System.Drawing.Point(4, 24); - this.optimizeTabPage.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.optimizeTabPage.Location = new System.Drawing.Point(4, 60); + this.optimizeTabPage.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.optimizeTabPage.Name = "optimizeTabPage"; - this.optimizeTabPage.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.optimizeTabPage.Size = new System.Drawing.Size(250, 242); + this.optimizeTabPage.Padding = new System.Windows.Forms.Padding(4, 6, 4, 6); + this.optimizeTabPage.Size = new System.Drawing.Size(379, 374); this.optimizeTabPage.TabIndex = 0; this.optimizeTabPage.Text = "Optimize"; this.optimizeTabPage.UseVisualStyleBackColor = true; // + // visualizeTabPage + // + this.visualizeTabPage.Controls.Add(this.dashboardButton); + this.visualizeTabPage.Controls.Add(this.visualizeButton); + this.visualizeTabPage.Controls.Add(this.visualizeTypeLabel); + this.visualizeTabPage.Controls.Add(this.visualizeTypeComboBox); + this.visualizeTabPage.Location = new System.Drawing.Point(4, 60); + this.visualizeTabPage.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); + this.visualizeTabPage.Name = "visualizeTabPage"; + this.visualizeTabPage.Padding = new System.Windows.Forms.Padding(4, 6, 4, 6); + this.visualizeTabPage.Size = new System.Drawing.Size(379, 374); + this.visualizeTabPage.TabIndex = 1; + this.visualizeTabPage.Text = "Visualize"; + this.visualizeTabPage.UseVisualStyleBackColor = true; + // + // dashboardButton + // + this.dashboardButton.Location = new System.Drawing.Point(33, 36); + this.dashboardButton.Margin = new System.Windows.Forms.Padding(4); + this.dashboardButton.Name = "dashboardButton"; + this.dashboardButton.Size = new System.Drawing.Size(323, 39); + this.dashboardButton.TabIndex = 11; + this.dashboardButton.Text = "Open Optuna-Dashboard"; + this.dashboardButton.UseVisualStyleBackColor = true; + this.dashboardButton.Click += new System.EventHandler(this.DashboardButton_Click); + // + // visualizeButton + // + this.visualizeButton.Location = new System.Drawing.Point(33, 203); + this.visualizeButton.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); + this.visualizeButton.Name = "visualizeButton"; + this.visualizeButton.Size = new System.Drawing.Size(323, 39); + this.visualizeButton.TabIndex = 2; + this.visualizeButton.Text = "Show selected type of plots"; + this.visualizeButton.UseVisualStyleBackColor = true; + this.visualizeButton.Click += new System.EventHandler(this.SelectedTypePlotButton_Click); + // + // visualizeTypeLabel + // + this.visualizeTypeLabel.AutoSize = true; + this.visualizeTypeLabel.Location = new System.Drawing.Point(4, 121); + this.visualizeTypeLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.visualizeTypeLabel.Name = "visualizeTypeLabel"; + this.visualizeTypeLabel.Size = new System.Drawing.Size(130, 23); + this.visualizeTypeLabel.TabIndex = 1; + this.visualizeTypeLabel.Text = "Visualize type"; + // + // visualizeTypeComboBox + // + this.visualizeTypeComboBox.FormattingEnabled = true; + this.visualizeTypeComboBox.Items.AddRange(new object[] { + "contour", + "EDF", + "intermediate values", + "optimization history", + "parallel coordinate", + "param importances", + "pareto front", + "slice"}); + this.visualizeTypeComboBox.Location = new System.Drawing.Point(33, 160); + this.visualizeTypeComboBox.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); + this.visualizeTypeComboBox.Name = "visualizeTypeComboBox"; + this.visualizeTypeComboBox.Size = new System.Drawing.Size(323, 31); + this.visualizeTypeComboBox.TabIndex = 0; + // + // outputTabPage + // + this.outputTabPage.Controls.Add(this.outputAllTrialsButton); + this.outputTabPage.Controls.Add(this.outputParatoSolutionButton); + this.outputTabPage.Controls.Add(this.reflectToSliderButton); + this.outputTabPage.Controls.Add(this.outputStopButton); + this.outputTabPage.Controls.Add(this.outputProgressBar); + this.outputTabPage.Controls.Add(this.outputModelNumberButton); + this.outputTabPage.Controls.Add(this.outputModelNumTextBox); + this.outputTabPage.Controls.Add(this.outputModelLabel); + this.outputTabPage.Location = new System.Drawing.Point(4, 60); + this.outputTabPage.Name = "outputTabPage"; + this.outputTabPage.Padding = new System.Windows.Forms.Padding(3); + this.outputTabPage.Size = new System.Drawing.Size(379, 374); + this.outputTabPage.TabIndex = 3; + this.outputTabPage.Text = "Output"; + this.outputTabPage.UseVisualStyleBackColor = true; + // + // outputAllTrialsButton + // + this.outputAllTrialsButton.Location = new System.Drawing.Point(35, 74); + this.outputAllTrialsButton.Margin = new System.Windows.Forms.Padding(4); + this.outputAllTrialsButton.Name = "outputAllTrialsButton"; + this.outputAllTrialsButton.Size = new System.Drawing.Size(297, 34); + this.outputAllTrialsButton.TabIndex = 18; + this.outputAllTrialsButton.Text = "All trials"; + this.outputAllTrialsButton.UseVisualStyleBackColor = true; + this.outputAllTrialsButton.Click += new System.EventHandler(this.OutputAllTrialsButton_Click); + // + // outputParatoSolutionButton + // + this.outputParatoSolutionButton.Location = new System.Drawing.Point(35, 22); + this.outputParatoSolutionButton.Margin = new System.Windows.Forms.Padding(4); + this.outputParatoSolutionButton.Name = "outputParatoSolutionButton"; + this.outputParatoSolutionButton.Size = new System.Drawing.Size(297, 34); + this.outputParatoSolutionButton.TabIndex = 17; + this.outputParatoSolutionButton.Text = "Parato solutions"; + this.outputParatoSolutionButton.UseVisualStyleBackColor = true; + this.outputParatoSolutionButton.Click += new System.EventHandler(this.OutputParatoSolutionButton_Click); + // + // reflectToSliderButton + // + this.reflectToSliderButton.Location = new System.Drawing.Point(35, 230); + this.reflectToSliderButton.Margin = new System.Windows.Forms.Padding(4); + this.reflectToSliderButton.Name = "reflectToSliderButton"; + this.reflectToSliderButton.Size = new System.Drawing.Size(325, 41); + this.reflectToSliderButton.TabIndex = 16; + this.reflectToSliderButton.Text = "Reflect the result on the sliders"; + this.reflectToSliderButton.UseVisualStyleBackColor = true; + this.reflectToSliderButton.Click += new System.EventHandler(this.ReflectToSliderButton_Click); + // + // outputStopButton + // + this.outputStopButton.Enabled = false; + this.outputStopButton.Location = new System.Drawing.Point(297, 320); + this.outputStopButton.Margin = new System.Windows.Forms.Padding(4); + this.outputStopButton.Name = "outputStopButton"; + this.outputStopButton.Size = new System.Drawing.Size(75, 40); + this.outputStopButton.TabIndex = 15; + this.outputStopButton.Text = "Stop"; + this.outputStopButton.UseVisualStyleBackColor = true; + this.outputStopButton.Click += new System.EventHandler(this.OutputStopButton_Click); + // + // outputProgressBar + // + this.outputProgressBar.Location = new System.Drawing.Point(35, 320); + this.outputProgressBar.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); + this.outputProgressBar.Name = "outputProgressBar"; + this.outputProgressBar.Size = new System.Drawing.Size(238, 40); + this.outputProgressBar.TabIndex = 14; + // + // outputModelNumberButton + // + this.outputModelNumberButton.Location = new System.Drawing.Point(243, 179); + this.outputModelNumberButton.Margin = new System.Windows.Forms.Padding(4); + this.outputModelNumberButton.Name = "outputModelNumberButton"; + this.outputModelNumberButton.Size = new System.Drawing.Size(117, 30); + this.outputModelNumberButton.TabIndex = 13; + this.outputModelNumberButton.Text = "Output"; + this.outputModelNumberButton.UseVisualStyleBackColor = true; + this.outputModelNumberButton.Click += new System.EventHandler(this.OutputModelNumberButton_Click); + // + // outputModelNumTextBox + // + this.outputModelNumTextBox.Location = new System.Drawing.Point(35, 179); + this.outputModelNumTextBox.Margin = new System.Windows.Forms.Padding(4); + this.outputModelNumTextBox.Name = "outputModelNumTextBox"; + this.outputModelNumTextBox.Size = new System.Drawing.Size(200, 30); + this.outputModelNumTextBox.TabIndex = 12; + this.outputModelNumTextBox.Text = "0"; + // + // outputModelLabel + // + this.outputModelLabel.AutoSize = true; + this.outputModelLabel.Location = new System.Drawing.Point(38, 152); + this.outputModelLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.outputModelLabel.Name = "outputModelLabel"; + this.outputModelLabel.Size = new System.Drawing.Size(175, 23); + this.outputModelLabel.TabIndex = 11; + this.outputModelLabel.Text = "Use model number"; + // // settingsTabPage // this.settingsTabPage.Controls.Add(this.settingsToJson); @@ -226,18 +404,20 @@ private void InitializeComponent() this.settingsTabPage.Controls.Add(this.settingsFolderOpen); this.settingsTabPage.Controls.Add(this.settingLabel); this.settingsTabPage.Controls.Add(this.settingsFromJson); - this.settingsTabPage.Location = new System.Drawing.Point(4, 24); + this.settingsTabPage.Location = new System.Drawing.Point(4, 60); + this.settingsTabPage.Margin = new System.Windows.Forms.Padding(4); this.settingsTabPage.Name = "settingsTabPage"; - this.settingsTabPage.Size = new System.Drawing.Size(250, 242); + this.settingsTabPage.Size = new System.Drawing.Size(379, 374); this.settingsTabPage.TabIndex = 2; this.settingsTabPage.Text = "Settings"; this.settingsTabPage.UseVisualStyleBackColor = true; // // settingsToJson // - this.settingsToJson.Location = new System.Drawing.Point(33, 136); + this.settingsToJson.Location = new System.Drawing.Point(50, 204); + this.settingsToJson.Margin = new System.Windows.Forms.Padding(4); this.settingsToJson.Name = "settingsToJson"; - this.settingsToJson.Size = new System.Drawing.Size(185, 23); + this.settingsToJson.Size = new System.Drawing.Size(278, 34); this.settingsToJson.TabIndex = 5; this.settingsToJson.Text = "Save settings to json"; this.settingsToJson.UseVisualStyleBackColor = true; @@ -245,9 +425,10 @@ private void InitializeComponent() // // settingsOpenAPIPage // - this.settingsOpenAPIPage.Location = new System.Drawing.Point(122, 88); + this.settingsOpenAPIPage.Location = new System.Drawing.Point(183, 132); + this.settingsOpenAPIPage.Margin = new System.Windows.Forms.Padding(4); this.settingsOpenAPIPage.Name = "settingsOpenAPIPage"; - this.settingsOpenAPIPage.Size = new System.Drawing.Size(110, 23); + this.settingsOpenAPIPage.Size = new System.Drawing.Size(165, 34); this.settingsOpenAPIPage.TabIndex = 4; this.settingsOpenAPIPage.Text = "Open API page"; this.settingsOpenAPIPage.UseVisualStyleBackColor = true; @@ -261,16 +442,18 @@ private void InitializeComponent() "NSGA-II", "CMA-ES", "Random"}); - this.settingsAPIComboBox.Location = new System.Drawing.Point(17, 88); + this.settingsAPIComboBox.Location = new System.Drawing.Point(26, 132); + this.settingsAPIComboBox.Margin = new System.Windows.Forms.Padding(4); this.settingsAPIComboBox.Name = "settingsAPIComboBox"; - this.settingsAPIComboBox.Size = new System.Drawing.Size(96, 23); + this.settingsAPIComboBox.Size = new System.Drawing.Size(142, 31); this.settingsAPIComboBox.TabIndex = 3; // // settingsFolderOpen // - this.settingsFolderOpen.Location = new System.Drawing.Point(33, 194); + this.settingsFolderOpen.Location = new System.Drawing.Point(50, 291); + this.settingsFolderOpen.Margin = new System.Windows.Forms.Padding(4); this.settingsFolderOpen.Name = "settingsFolderOpen"; - this.settingsFolderOpen.Size = new System.Drawing.Size(185, 23); + this.settingsFolderOpen.Size = new System.Drawing.Size(278, 34); this.settingsFolderOpen.TabIndex = 2; this.settingsFolderOpen.Text = "Open Settings.json folder"; this.settingsFolderOpen.UseVisualStyleBackColor = true; @@ -278,181 +461,68 @@ private void InitializeComponent() // // settingLabel // - this.settingLabel.Location = new System.Drawing.Point(14, 12); + this.settingLabel.Location = new System.Drawing.Point(21, 18); + this.settingLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.settingLabel.Name = "settingLabel"; - this.settingLabel.Size = new System.Drawing.Size(218, 73); + this.settingLabel.Size = new System.Drawing.Size(327, 110); this.settingLabel.TabIndex = 1; this.settingLabel.Text = "Detailed optimization settings can be configured in the \"Settings.json\" file in t" + "he following folder."; // // settingsFromJson // - this.settingsFromJson.Location = new System.Drawing.Point(33, 165); + this.settingsFromJson.Location = new System.Drawing.Point(50, 248); + this.settingsFromJson.Margin = new System.Windows.Forms.Padding(4); this.settingsFromJson.Name = "settingsFromJson"; - this.settingsFromJson.Size = new System.Drawing.Size(185, 23); + this.settingsFromJson.Size = new System.Drawing.Size(278, 34); this.settingsFromJson.TabIndex = 0; this.settingsFromJson.Text = "Load settings from json"; this.settingsFromJson.UseVisualStyleBackColor = true; this.settingsFromJson.Click += new System.EventHandler(this.SettingsFromJson_Click); // - // resultTabPage - // - this.resultTabPage.Controls.Add(this.dashboardButton); - this.resultTabPage.Controls.Add(this.restoreReflectButton); - this.resultTabPage.Controls.Add(this.restoreStopButton); - this.resultTabPage.Controls.Add(this.restoreProgressBar); - this.resultTabPage.Controls.Add(this.restoreRunButton); - this.resultTabPage.Controls.Add(this.restoreModelNumTextBox); - this.resultTabPage.Controls.Add(this.restoreModelLabel); - this.resultTabPage.Controls.Add(this.openResultFolderButton); - this.resultTabPage.Controls.Add(this.clearResultButton); - this.resultTabPage.Controls.Add(this.visualizeButton); - this.resultTabPage.Controls.Add(this.visualizeTypeLabel); - this.resultTabPage.Controls.Add(this.visualizeTypeComboBox); - this.resultTabPage.Location = new System.Drawing.Point(4, 24); - this.resultTabPage.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.resultTabPage.Name = "resultTabPage"; - this.resultTabPage.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.resultTabPage.Size = new System.Drawing.Size(250, 242); - this.resultTabPage.TabIndex = 1; - this.resultTabPage.Text = "Result"; - this.resultTabPage.UseVisualStyleBackColor = true; - // - // restoreReflectButton - // - this.restoreReflectButton.Location = new System.Drawing.Point(152, 177); - this.restoreReflectButton.Name = "restoreReflectButton"; - this.restoreReflectButton.Size = new System.Drawing.Size(62, 23); - this.restoreReflectButton.TabIndex = 10; - this.restoreReflectButton.Text = "Reflect"; - this.restoreReflectButton.UseVisualStyleBackColor = true; - this.restoreReflectButton.Click += new System.EventHandler(this.RestoreReflectButton_Click); - // - // restoreStopButton - // - this.restoreStopButton.Enabled = false; - this.restoreStopButton.Location = new System.Drawing.Point(96, 177); - this.restoreStopButton.Name = "restoreStopButton"; - this.restoreStopButton.Size = new System.Drawing.Size(50, 23); - this.restoreStopButton.TabIndex = 9; - this.restoreStopButton.Text = "Stop"; - this.restoreStopButton.UseVisualStyleBackColor = true; - this.restoreStopButton.Click += new System.EventHandler(this.RestoreStopButton_Click); - // - // restoreProgressBar - // - this.restoreProgressBar.Location = new System.Drawing.Point(9, 207); - this.restoreProgressBar.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.restoreProgressBar.Name = "restoreProgressBar"; - this.restoreProgressBar.Size = new System.Drawing.Size(235, 27); - this.restoreProgressBar.TabIndex = 8; - // - // restoreRunButton - // - this.restoreRunButton.Location = new System.Drawing.Point(21, 177); - this.restoreRunButton.Name = "restoreRunButton"; - this.restoreRunButton.Size = new System.Drawing.Size(69, 23); - this.restoreRunButton.TabIndex = 7; - this.restoreRunButton.Text = "Restore"; - this.restoreRunButton.UseVisualStyleBackColor = true; - this.restoreRunButton.Click += new System.EventHandler(this.RestoreRunButton_Click); - // - // restoreModelNumTextBox - // - this.restoreModelNumTextBox.Location = new System.Drawing.Point(9, 148); - this.restoreModelNumTextBox.Name = "restoreModelNumTextBox"; - this.restoreModelNumTextBox.Size = new System.Drawing.Size(172, 23); - this.restoreModelNumTextBox.TabIndex = 6; - this.restoreModelNumTextBox.Text = "-1"; - // - // restoreModelLabel - // - this.restoreModelLabel.AutoSize = true; - this.restoreModelLabel.Location = new System.Drawing.Point(3, 130); - this.restoreModelLabel.Name = "restoreModelLabel"; - this.restoreModelLabel.Size = new System.Drawing.Size(162, 15); - this.restoreModelLabel.TabIndex = 5; - this.restoreModelLabel.Text = "Set restore model number"; + // fileTabPage + // + this.fileTabPage.Controls.Add(this.openResultFolderButton); + this.fileTabPage.Controls.Add(this.clearResultButton); + this.fileTabPage.Location = new System.Drawing.Point(4, 60); + this.fileTabPage.Name = "fileTabPage"; + this.fileTabPage.Padding = new System.Windows.Forms.Padding(3); + this.fileTabPage.Size = new System.Drawing.Size(379, 374); + this.fileTabPage.TabIndex = 4; + this.fileTabPage.Text = "File"; + this.fileTabPage.UseVisualStyleBackColor = true; // // openResultFolderButton // - this.openResultFolderButton.Location = new System.Drawing.Point(31, 61); - this.openResultFolderButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.openResultFolderButton.Location = new System.Drawing.Point(62, 27); + this.openResultFolderButton.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.openResultFolderButton.Name = "openResultFolderButton"; - this.openResultFolderButton.Size = new System.Drawing.Size(176, 29); - this.openResultFolderButton.TabIndex = 4; + this.openResultFolderButton.Size = new System.Drawing.Size(264, 39); + this.openResultFolderButton.TabIndex = 6; this.openResultFolderButton.Text = "Open result file folder"; this.openResultFolderButton.UseVisualStyleBackColor = true; this.openResultFolderButton.Click += new System.EventHandler(this.OpenResultFolderButton_Click); // // clearResultButton // - this.clearResultButton.Location = new System.Drawing.Point(31, 97); - this.clearResultButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.clearResultButton.Location = new System.Drawing.Point(62, 92); + this.clearResultButton.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.clearResultButton.Name = "clearResultButton"; - this.clearResultButton.Size = new System.Drawing.Size(176, 29); - this.clearResultButton.TabIndex = 3; + this.clearResultButton.Size = new System.Drawing.Size(264, 42); + this.clearResultButton.TabIndex = 5; this.clearResultButton.Text = "Clear result flie"; this.clearResultButton.UseVisualStyleBackColor = true; this.clearResultButton.Click += new System.EventHandler(this.ClearResultButton_Click); // - // visualizeButton - // - this.visualizeButton.Location = new System.Drawing.Point(187, 30); - this.visualizeButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.visualizeButton.Name = "visualizeButton"; - this.visualizeButton.Size = new System.Drawing.Size(57, 23); - this.visualizeButton.TabIndex = 2; - this.visualizeButton.Text = "Show"; - this.visualizeButton.UseVisualStyleBackColor = true; - this.visualizeButton.Click += new System.EventHandler(this.VisualizeButton_Click); - // - // visualizeTypeLabel - // - this.visualizeTypeLabel.AutoSize = true; - this.visualizeTypeLabel.Location = new System.Drawing.Point(3, 4); - this.visualizeTypeLabel.Name = "visualizeTypeLabel"; - this.visualizeTypeLabel.Size = new System.Drawing.Size(87, 15); - this.visualizeTypeLabel.TabIndex = 1; - this.visualizeTypeLabel.Text = "Visualize type"; - // - // visualizeTypeComboBox - // - this.visualizeTypeComboBox.FormattingEnabled = true; - this.visualizeTypeComboBox.Items.AddRange(new object[] { - "contour", - "EDF", - "intermediate values", - "optimization history", - "parallel coordinate", - "param importances", - "pareto front", - "slice"}); - this.visualizeTypeComboBox.Location = new System.Drawing.Point(6, 30); - this.visualizeTypeComboBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.visualizeTypeComboBox.Name = "visualizeTypeComboBox"; - this.visualizeTypeComboBox.Size = new System.Drawing.Size(175, 23); - this.visualizeTypeComboBox.TabIndex = 0; - // - // dashboardButton - // - this.dashboardButton.Location = new System.Drawing.Point(136, 3); - this.dashboardButton.Name = "dashboardButton"; - this.dashboardButton.Size = new System.Drawing.Size(108, 23); - this.dashboardButton.TabIndex = 11; - this.dashboardButton.Text = "Dashboard"; - this.dashboardButton.UseVisualStyleBackColor = true; - this.dashboardButton.Click += new System.EventHandler(this.DashboardButton_Click); - // // OptimizationWindow // - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleDimensions = new System.Drawing.SizeF(144F, 144F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(285, 298); + this.ClientSize = new System.Drawing.Size(428, 475); this.Controls.Add(this.optimizeTabControl); this.Font = new System.Drawing.Font("Meiryo UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128))); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.Name = "OptimizationWindow"; this.Text = "Tunny"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormClosingXButton); @@ -461,9 +531,12 @@ private void InitializeComponent() this.optimizeTabControl.ResumeLayout(false); this.optimizeTabPage.ResumeLayout(false); this.optimizeTabPage.PerformLayout(); + this.visualizeTabPage.ResumeLayout(false); + this.visualizeTabPage.PerformLayout(); + this.outputTabPage.ResumeLayout(false); + this.outputTabPage.PerformLayout(); this.settingsTabPage.ResumeLayout(false); - this.resultTabPage.ResumeLayout(false); - this.resultTabPage.PerformLayout(); + this.fileTabPage.ResumeLayout(false); this.ResumeLayout(false); } @@ -483,19 +556,11 @@ private void InitializeComponent() private System.Windows.Forms.TextBox studyNameTextBox; private System.Windows.Forms.TabControl optimizeTabControl; private System.Windows.Forms.TabPage optimizeTabPage; - private System.Windows.Forms.TabPage resultTabPage; + private System.Windows.Forms.TabPage visualizeTabPage; private System.Windows.Forms.Button visualizeButton; private System.Windows.Forms.Label visualizeTypeLabel; private System.Windows.Forms.ComboBox visualizeTypeComboBox; - private System.Windows.Forms.Button clearResultButton; - private System.Windows.Forms.Button openResultFolderButton; - private System.Windows.Forms.Button restoreRunButton; - private System.Windows.Forms.TextBox restoreModelNumTextBox; - private System.Windows.Forms.Label restoreModelLabel; - private System.Windows.Forms.ProgressBar restoreProgressBar; - private System.ComponentModel.BackgroundWorker restoreBackgroundWorker; - private System.Windows.Forms.Button restoreStopButton; - private System.Windows.Forms.Button restoreReflectButton; + private System.ComponentModel.BackgroundWorker outputResultBackgroundWorker; private System.Windows.Forms.TabPage settingsTabPage; private System.Windows.Forms.Button settingsOpenAPIPage; private System.Windows.Forms.ComboBox settingsAPIComboBox; @@ -504,6 +569,18 @@ private void InitializeComponent() private System.Windows.Forms.Button settingsFromJson; private System.Windows.Forms.Button settingsToJson; private System.Windows.Forms.Button dashboardButton; + private System.Windows.Forms.TabPage outputTabPage; + private System.Windows.Forms.Button outputAllTrialsButton; + private System.Windows.Forms.Button outputParatoSolutionButton; + private System.Windows.Forms.Button reflectToSliderButton; + private System.Windows.Forms.Button outputStopButton; + private System.Windows.Forms.ProgressBar outputProgressBar; + private System.Windows.Forms.Button outputModelNumberButton; + private System.Windows.Forms.TextBox outputModelNumTextBox; + private System.Windows.Forms.Label outputModelLabel; + private System.Windows.Forms.TabPage fileTabPage; + private System.Windows.Forms.Button openResultFolderButton; + private System.Windows.Forms.Button clearResultButton; } } diff --git a/Tunny/UI/OptimizationWindow.cs b/Tunny/UI/OptimizationWindow.cs index 925e0eb0..92fff058 100644 --- a/Tunny/UI/OptimizationWindow.cs +++ b/Tunny/UI/OptimizationWindow.cs @@ -49,17 +49,17 @@ public OptimizationWindow(TunnyComponent component) optimizeBackgroundWorker.WorkerReportsProgress = true; optimizeBackgroundWorker.WorkerSupportsCancellation = true; - restoreBackgroundWorker.DoWork += RestoreLoop.Run; - restoreBackgroundWorker.ProgressChanged += RestoreProgressChangedHandler; - restoreBackgroundWorker.RunWorkerCompleted += RestoreStopButton_Click; - restoreBackgroundWorker.WorkerReportsProgress = true; - restoreBackgroundWorker.WorkerSupportsCancellation = true; + outputResultBackgroundWorker.DoWork += OutputLoop.Run; + outputResultBackgroundWorker.ProgressChanged += OutputProgressChangedHandler; + outputResultBackgroundWorker.RunWorkerCompleted += OutputStopButton_Click; + outputResultBackgroundWorker.WorkerReportsProgress = true; + outputResultBackgroundWorker.WorkerSupportsCancellation = true; } public void BGDispose() { optimizeBackgroundWorker.Dispose(); - restoreBackgroundWorker.Dispose(); + outputResultBackgroundWorker.Dispose(); } private void LoadSettingJson() @@ -85,7 +85,7 @@ private void InitializeUIValues() nTrialNumUpDown.Value = _settings.Optimize.NumberOfTrials; loadIfExistsCheckBox.Checked = _settings.Optimize.LoadExistStudy; studyNameTextBox.Text = _settings.StudyName; - restoreModelNumTextBox.Text = _settings.Result.RestoreNumberString; + outputModelNumTextBox.Text = _settings.Result.OutputNumberString; visualizeTypeComboBox.SelectedIndex = _settings.Result.SelectVisualizeType; } @@ -117,9 +117,9 @@ private void FormClosingXButton(object sender, FormClosingEventArgs e) { optimizeBackgroundWorker.Dispose(); } - if (restoreBackgroundWorker != null) + if (outputResultBackgroundWorker != null) { - restoreBackgroundWorker.Dispose(); + outputResultBackgroundWorker.Dispose(); } } @@ -129,7 +129,7 @@ private void SaveUIValues() _settings.Optimize.NumberOfTrials = (int)nTrialNumUpDown.Value; _settings.Optimize.LoadExistStudy = loadIfExistsCheckBox.Checked; _settings.StudyName = studyNameTextBox.Text; - _settings.Result.RestoreNumberString = restoreModelNumTextBox.Text; + _settings.Result.OutputNumberString = outputModelNumTextBox.Text; _settings.Result.SelectVisualizeType = visualizeTypeComboBox.SelectedIndex; _settings.Serialize(_component.GhInOut.ComponentFolder + @"\Settings.json"); } diff --git a/Tunny/UI/OptimizationWindow.resx b/Tunny/UI/OptimizationWindow.resx index 9b924b14..c54977f1 100644 --- a/Tunny/UI/OptimizationWindow.resx +++ b/Tunny/UI/OptimizationWindow.resx @@ -118,10 +118,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 17, 17 + 342, 17 - - 255, 17 + + 17, 17 diff --git a/Tunny/UI/OptimizeWindowTab/FileTab.cs b/Tunny/UI/OptimizeWindowTab/FileTab.cs new file mode 100644 index 00000000..6120187f --- /dev/null +++ b/Tunny/UI/OptimizeWindowTab/FileTab.cs @@ -0,0 +1,20 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Windows.Forms; + +namespace Tunny.UI +{ + public partial class OptimizationWindow : Form + { + private void OpenResultFolderButton_Click(object sender, EventArgs e) + { + Process.Start("EXPLORER.EXE", _component.GhInOut.ComponentFolder); + } + + private void ClearResultButton_Click(object sender, EventArgs e) + { + File.Delete(_settings.Storage); + } + } +} diff --git a/Tunny/UI/OptimizeWindowTab/OutputTab.cs b/Tunny/UI/OptimizeWindowTab/OutputTab.cs new file mode 100644 index 00000000..b1b19864 --- /dev/null +++ b/Tunny/UI/OptimizeWindowTab/OutputTab.cs @@ -0,0 +1,96 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Windows.Forms; + +using Tunny.Optimization; +using Tunny.Util; + +namespace Tunny.UI +{ + public partial class OptimizationWindow : Form + { + private void OutputParatoSolutionButton_Click(object sender, EventArgs e) + { + RunOutputLoop(OutputMode.ParatoSolutions); + } + + private void OutputAllTrialsButton_Click(object sender, EventArgs e) + { + RunOutputLoop(OutputMode.AllTrials); + } + + private void OutputModelNumberButton_Click(object sender, EventArgs e) + { + RunOutputLoop(OutputMode.ModelNumber); + } + + private void ReflectToSliderButton_Click(object sender, EventArgs e) + { + RunOutputLoop(OutputMode.ReflectToSliders); + } + + private void RunOutputLoop(OutputMode mode) + { + OutputLoop.Mode = mode; + OutputLoop.StudyName = studyNameTextBox.Text; + OutputLoop.NickNames = _component.GhInOut.Variables.Select(x => x.NickName).ToArray(); + int[] indices = outputModelNumTextBox.Text.Split(',').Select(int.Parse).ToArray(); + switch (mode) + { + case OutputMode.ParatoSolutions: + OutputLoop.Indices = new[] { -1 }; + break; + case OutputMode.AllTrials: + OutputLoop.Indices = new[] { -10 }; + break; + case OutputMode.ModelNumber: + OutputLoop.Indices = indices; + break; + case OutputMode.ReflectToSliders: + CheckIndicesLength(indices); + OutputLoop.Indices = new[] { indices[0] }; + break; + } + outputResultBackgroundWorker.RunWorkerAsync(_component); + } + + private static void CheckIndicesLength(int[] indices) + { + if (indices.Length > 1) + { + TunnyMessageBox.Show( + "You input multi model numbers, but this function only reflect variables to slider or gene pool to first one.", + "Tunny" + ); + } + } + + private void OutputStopButton_Click(object sender, EventArgs e) + { + if (outputResultBackgroundWorker != null) + { + outputResultBackgroundWorker.Dispose(); + } + switch (OutputLoop.Mode) + { + case OutputMode.ParatoSolutions: + case OutputMode.AllTrials: + case OutputMode.ModelNumber: + _component.ExpireSolution(true); + break; + case OutputMode.ReflectToSliders: + var decimalVar = _component.Fishes[0].Variables + .Select(x => (decimal)x.Value).ToList(); + _component.GhInOut.NewSolution(decimalVar); + break; + } + } + + private void OutputProgressChangedHandler(object sender, ProgressChangedEventArgs e) + { + outputProgressBar.Value = e.ProgressPercentage; + outputProgressBar.Update(); + } + } +} diff --git a/Tunny/UI/OptimizeWindowTab/ResultTab.cs b/Tunny/UI/OptimizeWindowTab/ResultTab.cs deleted file mode 100644 index 72988281..00000000 --- a/Tunny/UI/OptimizeWindowTab/ResultTab.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Windows.Forms; - -using Tunny.Optimization; -using Tunny.Solver; -using Tunny.Util; - -namespace Tunny.UI -{ - public partial class OptimizationWindow : Form - { - private void DashboardButton_Click(object sender, EventArgs e) - { - var dashboard = new Process(); - dashboard.StartInfo.FileName = PythonInstaller.GetEmbeddedPythonPath() + @"\Scripts\optuna-dashboard.exe"; - dashboard.StartInfo.Arguments = @"sqlite:///" + _component.GhInOut.ComponentFolder + @"\Tunny_Opt_Result.db"; - dashboard.StartInfo.UseShellExecute = false; - dashboard.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; - dashboard.Start(); - - var browser = new Process(); - browser.StartInfo.FileName = @"http://127.0.0.1:8080/"; - browser.StartInfo.UseShellExecute = true; - browser.Start(); - } - - private void VisualizeButton_Click(object sender, EventArgs e) - { - var optuna = new Optuna(_component.GhInOut.ComponentFolder); - optuna.ShowResultVisualize(visualizeTypeComboBox.Text, studyNameTextBox.Text); - } - - private void OpenResultFolderButton_Click(object sender, EventArgs e) - { - Process.Start("EXPLORER.EXE", _component.GhInOut.ComponentFolder); - } - - private void ClearResultButton_Click(object sender, EventArgs e) - { - File.Delete(_component.GhInOut.ComponentFolder + "/Tunny_Opt_Result.db"); - } - - private void RestoreRunButton_Click(object sender, EventArgs e) - { - RunRestoreLoop("Restore"); - } - - private void RestoreReflectButton_Click(object sender, EventArgs e) - { - RunRestoreLoop("Reflect"); - } - - private void RunRestoreLoop(string mode) - { - RestoreLoop.Mode = mode; - RestoreLoop.StudyName = studyNameTextBox.Text; - RestoreLoop.NickNames = _component.GhInOut.Variables.Select(x => x.NickName).ToArray(); - RestoreLoop.Indices = restoreModelNumTextBox.Text.Split(',').Select(int.Parse).ToArray(); - restoreBackgroundWorker.RunWorkerAsync(_component); - } - - private void RestoreStopButton_Click(object sender, EventArgs e) - { - if (restoreBackgroundWorker != null) - { - restoreBackgroundWorker.CancelAsync(); - } - switch (RestoreLoop.Mode) - { - case "Restore": - _component.ExpireSolution(true); - break; - case "Reflect": - var decimalVar = _component.Fishes[0].Variables - .Select(x => (decimal)x.Value).ToList(); - _component.GhInOut.NewSolution(decimalVar); - break; - } - } - - private void RestoreProgressChangedHandler(object sender, ProgressChangedEventArgs e) - { - restoreProgressBar.Value = e.ProgressPercentage; - restoreProgressBar.Update(); - } - } -} diff --git a/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs b/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs new file mode 100644 index 00000000..d11ecc43 --- /dev/null +++ b/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs @@ -0,0 +1,33 @@ +using System; +using System.Diagnostics; +using System.Windows.Forms; + +using Tunny.Solver; +using Tunny.Util; + +namespace Tunny.UI +{ + public partial class OptimizationWindow : Form + { + private void DashboardButton_Click(object sender, EventArgs e) + { + var dashboard = new Process(); + dashboard.StartInfo.FileName = PythonInstaller.GetEmbeddedPythonPath() + @"\Scripts\optuna-dashboard.exe"; + dashboard.StartInfo.Arguments = @"sqlite:///" + _settings.Storage; + dashboard.StartInfo.UseShellExecute = false; + dashboard.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; + dashboard.Start(); + + var browser = new Process(); + browser.StartInfo.FileName = @"http://127.0.0.1:8080/"; + browser.StartInfo.UseShellExecute = true; + browser.Start(); + } + + private void SelectedTypePlotButton_Click(object sender, EventArgs e) + { + var optuna = new Optuna(_component.GhInOut.ComponentFolder); + optuna.ShowSelectedTypePlot(visualizeTypeComboBox.Text, studyNameTextBox.Text); + } + } +} diff --git a/Tunny/Util/OutputMode.cs b/Tunny/Util/OutputMode.cs new file mode 100644 index 00000000..006f9fef --- /dev/null +++ b/Tunny/Util/OutputMode.cs @@ -0,0 +1,10 @@ +namespace Tunny.Util +{ + public enum OutputMode + { + ParatoSolutions, + AllTrials, + ModelNumber, + ReflectToSliders + } +} From f8c1000bcd0c6d10aa7057554baaa9c8783b6dd3 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 3 Jul 2022 16:24:29 +0900 Subject: [PATCH 47/79] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81923e88..aa01958e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,12 +28,15 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - The output of the Tunny component is made into Fish, a type that summarizes the results. - The Geometry input of the Tunny component has been changed to Attribute to allow more attribute information to be handled. - When restoring the results from Tunny component as Fishes, those that the trial did not work and Objective could not get are not output. +- Update UI + - The UI of the Restore tab was confusing, so the UI was modified to make it easier to understand which button to press and how the results are output. ### Fix - Stopped sampling when there was no geometry input - Once optimize output error, the component won't run again - I've tried to do a proper Dispose to fix this problem, but it still doesn't work sometimes. +- Optuna-DashBoard and Delete Result Files functions do not work properly when a different Storage path is specified in Settings than the default. ## [0.3.0] -2022-05-03 From e1e2225e0803e5db8f34386bd3f170954ee4ec7e Mon Sep 17 00:00:00 2001 From: hiron Date: Sun, 3 Jul 2022 08:48:14 +0000 Subject: [PATCH 48/79] Fix Resource.resx to be enable to build ci --- Tunny/Resources/Resource.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tunny/Resources/Resource.resx b/Tunny/Resources/Resource.resx index 3553bf04..1fe23b08 100644 --- a/Tunny/Resources/Resource.resx +++ b/Tunny/Resources/Resource.resx @@ -134,7 +134,7 @@ ParamFishAttribute.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - paramfishicon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ParamFishIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a TunnyIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a From 8fcdde42ece0fc2e13fa33a243fab9dc5508753a Mon Sep 17 00:00:00 2001 From: hiron Date: Sun, 3 Jul 2022 08:53:46 +0000 Subject: [PATCH 49/79] Fix ci build settings --- .github/workflows/build-component.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-component.yml b/.github/workflows/build-component.yml index 26e4d2bc..242a4231 100644 --- a/.github/workflows/build-component.yml +++ b/.github/workflows/build-component.yml @@ -45,8 +45,8 @@ jobs: shell: powershell run: | cp ./Samples ./Release/Samples -recurse - cp ./LICENSE ./Release/LICENSE - cp ./PYTHON_PACKAGE_LICENSE ./Release/PYTHON_PACKAGE_LICENSE + cp ./LICENSE ./Release/LICENSE.txt + cp ./PYTHON_PACKAGE_LICENSES ./Release/PYTHON_PACKAGE_LICENSES.txt cp ./Tunny/bin/Release/net48 ./Release/Tunny -recurse - name: Upload release build of plugin as artefact From de47e595a48a55c1ba676e70771840e9e7ed821e Mon Sep 17 00:00:00 2001 From: hiron Date: Sun, 3 Jul 2022 09:02:38 +0000 Subject: [PATCH 50/79] Fix codacy issue --- Tunny/UI/OptimizeWindowTab/OutputTab.cs | 196 ++++++++++++------------ 1 file changed, 100 insertions(+), 96 deletions(-) diff --git a/Tunny/UI/OptimizeWindowTab/OutputTab.cs b/Tunny/UI/OptimizeWindowTab/OutputTab.cs index b1b19864..a451e20c 100644 --- a/Tunny/UI/OptimizeWindowTab/OutputTab.cs +++ b/Tunny/UI/OptimizeWindowTab/OutputTab.cs @@ -1,96 +1,100 @@ -using System; -using System.ComponentModel; -using System.Linq; -using System.Windows.Forms; - -using Tunny.Optimization; -using Tunny.Util; - -namespace Tunny.UI -{ - public partial class OptimizationWindow : Form - { - private void OutputParatoSolutionButton_Click(object sender, EventArgs e) - { - RunOutputLoop(OutputMode.ParatoSolutions); - } - - private void OutputAllTrialsButton_Click(object sender, EventArgs e) - { - RunOutputLoop(OutputMode.AllTrials); - } - - private void OutputModelNumberButton_Click(object sender, EventArgs e) - { - RunOutputLoop(OutputMode.ModelNumber); - } - - private void ReflectToSliderButton_Click(object sender, EventArgs e) - { - RunOutputLoop(OutputMode.ReflectToSliders); - } - - private void RunOutputLoop(OutputMode mode) - { - OutputLoop.Mode = mode; - OutputLoop.StudyName = studyNameTextBox.Text; - OutputLoop.NickNames = _component.GhInOut.Variables.Select(x => x.NickName).ToArray(); - int[] indices = outputModelNumTextBox.Text.Split(',').Select(int.Parse).ToArray(); - switch (mode) - { - case OutputMode.ParatoSolutions: - OutputLoop.Indices = new[] { -1 }; - break; - case OutputMode.AllTrials: - OutputLoop.Indices = new[] { -10 }; - break; - case OutputMode.ModelNumber: - OutputLoop.Indices = indices; - break; - case OutputMode.ReflectToSliders: - CheckIndicesLength(indices); - OutputLoop.Indices = new[] { indices[0] }; - break; - } - outputResultBackgroundWorker.RunWorkerAsync(_component); - } - - private static void CheckIndicesLength(int[] indices) - { - if (indices.Length > 1) - { - TunnyMessageBox.Show( - "You input multi model numbers, but this function only reflect variables to slider or gene pool to first one.", - "Tunny" - ); - } - } - - private void OutputStopButton_Click(object sender, EventArgs e) - { - if (outputResultBackgroundWorker != null) - { - outputResultBackgroundWorker.Dispose(); - } - switch (OutputLoop.Mode) - { - case OutputMode.ParatoSolutions: - case OutputMode.AllTrials: - case OutputMode.ModelNumber: - _component.ExpireSolution(true); - break; - case OutputMode.ReflectToSliders: - var decimalVar = _component.Fishes[0].Variables - .Select(x => (decimal)x.Value).ToList(); - _component.GhInOut.NewSolution(decimalVar); - break; - } - } - - private void OutputProgressChangedHandler(object sender, ProgressChangedEventArgs e) - { - outputProgressBar.Value = e.ProgressPercentage; - outputProgressBar.Update(); - } - } -} +using System; +using System.ComponentModel; +using System.Linq; +using System.Windows.Forms; + +using Tunny.Optimization; +using Tunny.Util; + +namespace Tunny.UI +{ + public partial class OptimizationWindow : Form + { + private void OutputParatoSolutionButton_Click(object sender, EventArgs e) + { + RunOutputLoop(OutputMode.ParatoSolutions); + } + + private void OutputAllTrialsButton_Click(object sender, EventArgs e) + { + RunOutputLoop(OutputMode.AllTrials); + } + + private void OutputModelNumberButton_Click(object sender, EventArgs e) + { + RunOutputLoop(OutputMode.ModelNumber); + } + + private void ReflectToSliderButton_Click(object sender, EventArgs e) + { + RunOutputLoop(OutputMode.ReflectToSliders); + } + + private void RunOutputLoop(OutputMode mode) + { + OutputLoop.Mode = mode; + OutputLoop.StudyName = studyNameTextBox.Text; + OutputLoop.NickNames = _component.GhInOut.Variables.Select(x => x.NickName).ToArray(); + int[] indices = outputModelNumTextBox.Text.Split(',').Select(int.Parse).ToArray(); + switch (mode) + { + case OutputMode.ParatoSolutions: + OutputLoop.Indices = new[] { -1 }; + break; + case OutputMode.AllTrials: + OutputLoop.Indices = new[] { -10 }; + break; + case OutputMode.ModelNumber: + OutputLoop.Indices = indices; + break; + case OutputMode.ReflectToSliders: + CheckIndicesLength(indices); + OutputLoop.Indices = new[] { indices[0] }; + break; + default: + throw new ArgumentException("Unsupported output mode."); + } + outputResultBackgroundWorker.RunWorkerAsync(_component); + } + + private static void CheckIndicesLength(int[] indices) + { + if (indices.Length > 1) + { + TunnyMessageBox.Show( + "You input multi model numbers, but this function only reflect variables to slider or gene pool to first one.", + "Tunny" + ); + } + } + + private void OutputStopButton_Click(object sender, EventArgs e) + { + if (outputResultBackgroundWorker != null) + { + outputResultBackgroundWorker.Dispose(); + } + switch (OutputLoop.Mode) + { + case OutputMode.ParatoSolutions: + case OutputMode.AllTrials: + case OutputMode.ModelNumber: + _component.ExpireSolution(true); + break; + case OutputMode.ReflectToSliders: + var decimalVar = _component.Fishes[0].Variables + .Select(x => (decimal)x.Value).ToList(); + _component.GhInOut.NewSolution(decimalVar); + break; + default: + throw new ArgumentException("Unsupported output mode."); + } + } + + private void OutputProgressChangedHandler(object sender, ProgressChangedEventArgs e) + { + outputProgressBar.Value = e.ProgressPercentage; + outputProgressBar.Update(); + } + } +} From f661eb07563f9a51012dad4550fc01f7b7f170a2 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Mon, 4 Jul 2022 10:43:21 +0900 Subject: [PATCH 51/79] Add code-ql settings --- .github/workflows/code-ql.yml | 72 +++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/workflows/code-ql.yml diff --git a/.github/workflows/code-ql.yml b/.github/workflows/code-ql.yml new file mode 100644 index 00000000..c39d72b5 --- /dev/null +++ b/.github/workflows/code-ql.yml @@ -0,0 +1,72 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "main", "develop" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main", "develop" ] + schedule: + - cron: '37 22 * * 1' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'csharp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 From 750d0925ef2799d667123535deff14b88e0753ec Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 6 Jul 2022 23:08:58 +0900 Subject: [PATCH 52/79] Add timeout settings to optimize --- Tunny/Settings/Optimize.cs | 1 + Tunny/Solver/OptunaAlgorithm.cs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Tunny/Settings/Optimize.cs b/Tunny/Settings/Optimize.cs index 99f7678b..2f0cc53e 100644 --- a/Tunny/Settings/Optimize.cs +++ b/Tunny/Settings/Optimize.cs @@ -6,5 +6,6 @@ public class Optimize public int NumberOfTrials { get; set; } = 100; public bool LoadExistStudy { get; set; } = true; public int SelectSampler { get; set; } + public double Timeout { get; set; } = double.MaxValue; } } diff --git a/Tunny/Solver/OptunaAlgorithm.cs b/Tunny/Solver/OptunaAlgorithm.cs index edc01878..4c887f7d 100644 --- a/Tunny/Solver/OptunaAlgorithm.cs +++ b/Tunny/Solver/OptunaAlgorithm.cs @@ -39,6 +39,7 @@ public void Solve() int variableCount = Lb.Length; int samplerType = Settings.Optimize.SelectSampler; int nTrials = Settings.Optimize.NumberOfTrials; + double timeout = Settings.Optimize.Timeout; int nObjective = ObjNickName.Length; string[] directions = new string[nObjective]; for (int i = 0; i < nObjective; i++) @@ -70,9 +71,17 @@ public void Solve() double[] xTest = new double[variableCount]; var result = new EvaluatedGHResult(); - for (int i = 0; i < nTrials; i++) + + int trialNum = 0; + DateTime startTime = DateTime.Now; + while (true) { - int progress = i * 100 / nTrials; + if (trialNum == nTrials || (DateTime.Now - startTime).TotalSeconds >= timeout) + { + break; + } + + int progress = trialNum * 100 / nTrials; dynamic trial = study.ask(); //TODO: Is this the correct way to handle the case of null? @@ -107,6 +116,7 @@ public void Solve() catch { } + trialNum++; } if (nObjective == 1) From e59d85a61cef5884ca1007335362b9b19aed6bf0 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 6 Jul 2022 23:44:59 +0900 Subject: [PATCH 53/79] Add timeout UI --- Tunny/Settings/Optimize.cs | 2 +- Tunny/Solver/OptunaAlgorithm.cs | 2 +- Tunny/UI/OptimizationWindow.Designer.cs | 45 +++++++++++++++++++++-- Tunny/UI/OptimizationWindow.cs | 2 + Tunny/UI/OptimizationWindow.resx | 3 ++ Tunny/UI/OptimizeWindowTab/OptimizeTab.cs | 3 +- 6 files changed, 50 insertions(+), 7 deletions(-) diff --git a/Tunny/Settings/Optimize.cs b/Tunny/Settings/Optimize.cs index 2f0cc53e..a4d59f8f 100644 --- a/Tunny/Settings/Optimize.cs +++ b/Tunny/Settings/Optimize.cs @@ -6,6 +6,6 @@ public class Optimize public int NumberOfTrials { get; set; } = 100; public bool LoadExistStudy { get; set; } = true; public int SelectSampler { get; set; } - public double Timeout { get; set; } = double.MaxValue; + public double Timeout { get; set; } } } diff --git a/Tunny/Solver/OptunaAlgorithm.cs b/Tunny/Solver/OptunaAlgorithm.cs index 4c887f7d..2528626b 100644 --- a/Tunny/Solver/OptunaAlgorithm.cs +++ b/Tunny/Solver/OptunaAlgorithm.cs @@ -39,7 +39,7 @@ public void Solve() int variableCount = Lb.Length; int samplerType = Settings.Optimize.SelectSampler; int nTrials = Settings.Optimize.NumberOfTrials; - double timeout = Settings.Optimize.Timeout; + double timeout = Settings.Optimize.Timeout <= 0 ? double.MaxValue : Settings.Optimize.Timeout; int nObjective = ObjNickName.Length; string[] directions = new string[nObjective]; for (int i = 0; i < nObjective; i++) diff --git a/Tunny/UI/OptimizationWindow.Designer.cs b/Tunny/UI/OptimizationWindow.Designer.cs index bce846f9..0611b898 100644 --- a/Tunny/UI/OptimizationWindow.Designer.cs +++ b/Tunny/UI/OptimizationWindow.Designer.cs @@ -29,6 +29,7 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OptimizationWindow)); this.optimizeRunButton = new System.Windows.Forms.Button(); this.optimizeBackgroundWorker = new System.ComponentModel.BackgroundWorker(); @@ -68,6 +69,9 @@ private void InitializeComponent() this.openResultFolderButton = new System.Windows.Forms.Button(); this.clearResultButton = new System.Windows.Forms.Button(); this.outputResultBackgroundWorker = new System.ComponentModel.BackgroundWorker(); + this.Timeout = new System.Windows.Forms.Label(); + this.timeoutNumUpDown = new System.Windows.Forms.NumericUpDown(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); ((System.ComponentModel.ISupportInitialize)(this.nTrialNumUpDown)).BeginInit(); this.optimizeTabControl.SuspendLayout(); this.optimizeTabPage.SuspendLayout(); @@ -75,6 +79,7 @@ private void InitializeComponent() this.outputTabPage.SuspendLayout(); this.settingsTabPage.SuspendLayout(); this.fileTabPage.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.timeoutNumUpDown)).BeginInit(); this.SuspendLayout(); // // optimizeRunButton @@ -134,7 +139,7 @@ private void InitializeComponent() this.loadIfExistsCheckBox.AutoSize = true; this.loadIfExistsCheckBox.Checked = true; this.loadIfExistsCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; - this.loadIfExistsCheckBox.Location = new System.Drawing.Point(20, 135); + this.loadIfExistsCheckBox.Location = new System.Drawing.Point(19, 156); this.loadIfExistsCheckBox.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.loadIfExistsCheckBox.Name = "loadIfExistsCheckBox"; this.loadIfExistsCheckBox.Size = new System.Drawing.Size(237, 27); @@ -162,7 +167,7 @@ private void InitializeComponent() this.samplerComboBox.Location = new System.Drawing.Point(148, 12); this.samplerComboBox.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.samplerComboBox.Name = "samplerComboBox"; - this.samplerComboBox.Size = new System.Drawing.Size(208, 31); + this.samplerComboBox.Size = new System.Drawing.Size(214, 31); this.samplerComboBox.TabIndex = 7; // // samplerTypeText @@ -178,7 +183,7 @@ private void InitializeComponent() // studyNameLabel // this.studyNameLabel.AutoSize = true; - this.studyNameLabel.Location = new System.Drawing.Point(15, 172); + this.studyNameLabel.Location = new System.Drawing.Point(14, 193); this.studyNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.studyNameLabel.Name = "studyNameLabel"; this.studyNameLabel.Size = new System.Drawing.Size(116, 23); @@ -187,7 +192,7 @@ private void InitializeComponent() // // studyNameTextBox // - this.studyNameTextBox.Location = new System.Drawing.Point(152, 166); + this.studyNameTextBox.Location = new System.Drawing.Point(151, 187); this.studyNameTextBox.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); this.studyNameTextBox.Name = "studyNameTextBox"; this.studyNameTextBox.Size = new System.Drawing.Size(205, 30); @@ -211,6 +216,8 @@ private void InitializeComponent() // // optimizeTabPage // + this.optimizeTabPage.Controls.Add(this.timeoutNumUpDown); + this.optimizeTabPage.Controls.Add(this.Timeout); this.optimizeTabPage.Controls.Add(this.studyNameTextBox); this.optimizeTabPage.Controls.Add(this.samplerComboBox); this.optimizeTabPage.Controls.Add(this.studyNameLabel); @@ -513,6 +520,32 @@ private void InitializeComponent() this.clearResultButton.Text = "Clear result flie"; this.clearResultButton.UseVisualStyleBackColor = true; this.clearResultButton.Click += new System.EventHandler(this.ClearResultButton_Click); + // + // Timeout + // + this.Timeout.AutoSize = true; + this.Timeout.Location = new System.Drawing.Point(16, 102); + this.Timeout.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.Timeout.Name = "Timeout"; + this.Timeout.Size = new System.Drawing.Size(132, 23); + this.Timeout.TabIndex = 11; + this.Timeout.Text = "Timeout (sec)"; + // + // timeoutNumUpDown + // + this.timeoutNumUpDown.Location = new System.Drawing.Point(197, 102); + this.timeoutNumUpDown.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); + this.timeoutNumUpDown.Maximum = new decimal(new int[] { + 10000000, + 0, + 0, + 0}); + this.timeoutNumUpDown.Name = "timeoutNumUpDown"; + this.timeoutNumUpDown.Size = new System.Drawing.Size(165, 30); + this.timeoutNumUpDown.TabIndex = 12; + this.timeoutNumUpDown.ThousandsSeparator = true; + this.toolTip1.SetToolTip(this.timeoutNumUpDown, "After this time has elapsed from the start of optimization, optimization is stopp" + + "ed."); // // OptimizationWindow // @@ -537,6 +570,7 @@ private void InitializeComponent() this.outputTabPage.PerformLayout(); this.settingsTabPage.ResumeLayout(false); this.fileTabPage.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.timeoutNumUpDown)).EndInit(); this.ResumeLayout(false); } @@ -581,6 +615,9 @@ private void InitializeComponent() private System.Windows.Forms.TabPage fileTabPage; private System.Windows.Forms.Button openResultFolderButton; private System.Windows.Forms.Button clearResultButton; + private System.Windows.Forms.NumericUpDown timeoutNumUpDown; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.Label Timeout; } } diff --git a/Tunny/UI/OptimizationWindow.cs b/Tunny/UI/OptimizationWindow.cs index 92fff058..eaed97a8 100644 --- a/Tunny/UI/OptimizationWindow.cs +++ b/Tunny/UI/OptimizationWindow.cs @@ -83,6 +83,7 @@ private void InitializeUIValues() { samplerComboBox.SelectedIndex = _settings.Optimize.SelectSampler; nTrialNumUpDown.Value = _settings.Optimize.NumberOfTrials; + timeoutNumUpDown.Value = (decimal)_settings.Optimize.Timeout; loadIfExistsCheckBox.Checked = _settings.Optimize.LoadExistStudy; studyNameTextBox.Text = _settings.StudyName; outputModelNumTextBox.Text = _settings.Result.OutputNumberString; @@ -127,6 +128,7 @@ private void SaveUIValues() { _settings.Optimize.SelectSampler = samplerComboBox.SelectedIndex; _settings.Optimize.NumberOfTrials = (int)nTrialNumUpDown.Value; + _settings.Optimize.Timeout = (double)timeoutNumUpDown.Value; _settings.Optimize.LoadExistStudy = loadIfExistsCheckBox.Checked; _settings.StudyName = studyNameTextBox.Text; _settings.Result.OutputNumberString = outputModelNumTextBox.Text; diff --git a/Tunny/UI/OptimizationWindow.resx b/Tunny/UI/OptimizationWindow.resx index c54977f1..1796dc55 100644 --- a/Tunny/UI/OptimizationWindow.resx +++ b/Tunny/UI/OptimizationWindow.resx @@ -120,6 +120,9 @@ 342, 17 + + 633, 17 + 17, 17 diff --git a/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs b/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs index c7733e8f..209b7ff9 100644 --- a/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs +++ b/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs @@ -20,6 +20,7 @@ private void OptimizeRunButton_Click(object sender, EventArgs e) _settings.Optimize.NumberOfTrials = (int)nTrialNumUpDown.Value; _settings.Optimize.SelectSampler = samplerComboBox.SelectedIndex; _settings.StudyName = studyNameTextBox.Text; + _settings.Optimize.Timeout = (double)timeoutNumUpDown.Value; OptimizeLoop.Settings = _settings; if (!CheckInputValue(ghCanvas)) @@ -64,7 +65,7 @@ private void OptimizeStopButton_Click(object sender, EventArgs e) if (optimizeBackgroundWorker != null) { - optimizeBackgroundWorker.CancelAsync(); + optimizeBackgroundWorker.Dispose(); } //Enable GUI From b994f19919d49b5b31d07bdec8dde690c286105c Mon Sep 17 00:00:00 2001 From: hrntsm Date: Wed, 6 Jul 2022 23:48:43 +0900 Subject: [PATCH 54/79] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa01958e..9d20e9ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - Components that output each attribute - Python package licenses to clearly state the license of each package. - requirements.txt file to avoid conflict python packages versions. +- Implemented Timeout to stop optimization over time. ### Changed From 245c543d3f2724b031f875283b39a96320352553 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 7 Jul 2022 09:48:48 +0900 Subject: [PATCH 55/79] Clean sampler settings --- Tunny/Optimization/OptimizeLoop.cs | 4 +- Tunny/Optimization/OutputLoop.cs | 4 +- .../Algorithm.cs} | 117 ++++-------------- Tunny/Solver/Optuna/Sampler.cs | 80 ++++++++++++ Tunny/Solver/{Optuna.cs => Optuna/optuna.cs} | 8 +- Tunny/UI/OptimizeWindowTab/VisualizeTab.cs | 4 +- Tunny/Util/SliderValueParameters.cs | 10 ++ 7 files changed, 126 insertions(+), 101 deletions(-) rename Tunny/Solver/{OptunaAlgorithm.cs => Optuna/Algorithm.cs} (61%) create mode 100644 Tunny/Solver/Optuna/Sampler.cs rename Tunny/Solver/{Optuna.cs => Optuna/optuna.cs} (97%) create mode 100644 Tunny/Util/SliderValueParameters.cs diff --git a/Tunny/Optimization/OptimizeLoop.cs b/Tunny/Optimization/OptimizeLoop.cs index cf91ecfb..ab4e27c6 100644 --- a/Tunny/Optimization/OptimizeLoop.cs +++ b/Tunny/Optimization/OptimizeLoop.cs @@ -7,7 +7,7 @@ using Tunny.Component; using Tunny.Settings; -using Tunny.Solver; +using Tunny.Solver.Optuna; using Tunny.UI; using Tunny.Util; @@ -54,7 +54,7 @@ private static double[] RunOptimizationLoop(BackgroundWorker worker) return new[] { double.NaN }; } - var optunaSolver = new Optuna(s_component.GhInOut.ComponentFolder); + var optunaSolver = new optuna(s_component.GhInOut.ComponentFolder); bool solverStarted = optunaSolver.RunSolver( variables, objectives, EvaluateFunction, Settings); diff --git a/Tunny/Optimization/OutputLoop.cs b/Tunny/Optimization/OutputLoop.cs index 477cfda2..640887ac 100644 --- a/Tunny/Optimization/OutputLoop.cs +++ b/Tunny/Optimization/OutputLoop.cs @@ -6,7 +6,7 @@ using Rhino.Geometry; using Tunny.Component; -using Tunny.Solver; +using Tunny.Solver.Optuna; using Tunny.Type; using Tunny.UI; using Tunny.Util; @@ -29,7 +29,7 @@ internal static void Run(object sender, DoWorkEventArgs e) var fishes = new List(); - var optunaSolver = new Optuna(s_component.GhInOut.ComponentFolder); + var optunaSolver = new optuna(s_component.GhInOut.ComponentFolder); ModelResult[] modelResult = optunaSolver.GetModelResult(Indices, StudyName); if (modelResult.Length == 0) { diff --git a/Tunny/Solver/OptunaAlgorithm.cs b/Tunny/Solver/Optuna/Algorithm.cs similarity index 61% rename from Tunny/Solver/OptunaAlgorithm.cs rename to Tunny/Solver/Optuna/Algorithm.cs index 2528626b..25f016b5 100644 --- a/Tunny/Solver/OptunaAlgorithm.cs +++ b/Tunny/Solver/Optuna/Algorithm.cs @@ -7,36 +7,39 @@ using Tunny.Optimization; using Tunny.Settings; +using Tunny.Util; -namespace Tunny.Solver +namespace Tunny.Solver.Optuna { - public class OptunaAlgorithm + public class Algorithm { - private double[] Lb { get; set; } - private double[] Ub { get; set; } - private string[] VarNickName { get; set; } + private SliderValueParameters Sliders { get; set; } private string[] ObjNickName { get; set; } private TunnySettings Settings { get; set; } private Func EvalFunc { get; set; } private double[] XOpt { get; set; } private double[] FxOpt { get; set; } - public OptunaAlgorithm( + public Algorithm( double[] lb, double[] ub, string[] varNickName, string[] objNickName, TunnySettings settings, Func evalFunc) { - Lb = lb; - Ub = ub; - VarNickName = varNickName; + Sliders = new SliderValueParameters + { + LowerBond = lb, + UpperBond = ub, + NickName = varNickName, + Count = lb.Length + }; ObjNickName = objNickName; Settings = settings; EvalFunc = evalFunc; + } public void Solve() { - int variableCount = Lb.Length; int samplerType = Settings.Optimize.SelectSampler; int nTrials = Settings.Optimize.NumberOfTrials; double timeout = Settings.Optimize.Timeout <= 0 ? double.MaxValue : Settings.Optimize.Timeout; @@ -51,7 +54,7 @@ public void Solve() using (Py.GIL()) { dynamic optuna = Py.Import("optuna"); - dynamic sampler = SetSamplerSettings(variableCount, samplerType, ref nTrials, optuna); + dynamic sampler = SetSamplerSettings(Sliders, samplerType, ref nTrials, optuna); dynamic study = optuna.create_study( sampler: sampler, @@ -69,7 +72,7 @@ public void Solve() name.Remove(name.Length - 1, 1); SetStudyUserAttr(study, name); - double[] xTest = new double[variableCount]; + double[] xTest = new double[Sliders.Count]; var result = new EvaluatedGHResult(); int trialNum = 0; @@ -91,9 +94,9 @@ public void Solve() int nullCount = 0; while (nullCount < 10) { - for (int j = 0; j < variableCount; j++) + for (int j = 0; j < Sliders.Count; j++) { - xTest[j] = trial.suggest_uniform(VarNickName[j], Lb[j], Ub[j]); + xTest[j] = trial.suggest_uniform(Sliders.NickName[j], Sliders.LowerBond[j], Sliders.UpperBond[j]); } result = EvalFunc(xTest, progress); @@ -123,13 +126,13 @@ public void Solve() { double[] values = (double[])study.best_params.values(); string[] keys = (string[])study.best_params.keys(); - double[] opt = new double[VarNickName.Length]; + double[] opt = new double[Sliders.Count]; - for (int i = 0; i < VarNickName.Length; i++) + for (int i = 0; i < Sliders.Count; i++) { for (int j = 0; j < keys.Length; j++) { - if (keys[j] == VarNickName[i]) + if (keys[j] == Sliders.NickName[i]) { opt[i] = values[j]; } @@ -179,25 +182,25 @@ private static void SetTrialUserAttr(EvaluatedGHResult result, dynamic trial) } } - private dynamic SetSamplerSettings(int n, int samplerType, ref int nTrials, dynamic optuna) + private dynamic SetSamplerSettings(SliderValueParameters sliders, int samplerType, ref int nTrials, dynamic optuna) { dynamic sampler; switch (samplerType) { case 0: - sampler = SetTPESamplerSettings(optuna); + sampler = Sampler.TPE(optuna, Settings); break; case 1: - sampler = SetNSGAIISamplerSettings(optuna); + sampler = Sampler.NSGAII(optuna, Settings); break; case 2: - sampler = SetCmaEsSamplerSettings(optuna); + sampler = Sampler.CmaEs(optuna, Settings); break; case 3: - sampler = SetRandomSamplerSettings(optuna); + sampler = Sampler.Random(optuna, Settings); break; case 4: - sampler = SetGridSamplerSettings(n, ref nTrials, optuna); + sampler = Sampler.Grid(optuna, sliders, ref nTrials); break; default: throw new ArgumentException("Unknown sampler type"); @@ -205,74 +208,6 @@ private dynamic SetSamplerSettings(int n, int samplerType, ref int nTrials, dyna return sampler; } - private dynamic SetRandomSamplerSettings(dynamic optuna) - { - Settings.Random random = Settings.Optimize.Sampler.Random; - return optuna.samplers.RandomSampler( - seed: random.Seed - ); - } - - private dynamic SetCmaEsSamplerSettings(dynamic optuna) - { - CmaEs cmaEs = Settings.Optimize.Sampler.CmaEs; - return optuna.samplers.CmaEsSampler( - sigma0: cmaEs.Sigma0, - n_startup_trials: cmaEs.NStartupTrials, - warn_independent_sampling: cmaEs.WarnIndependentSampling, - seed: cmaEs.Seed, - consider_pruned_trials: cmaEs.ConsiderPrunedTrials, - restart_strategy: cmaEs.RestartStrategy, - inc_popsize: cmaEs.IncPopsize, - use_separable_cma: cmaEs.UseSeparableCma - ); - } - - private dynamic SetGridSamplerSettings(int n, ref int nTrials, dynamic optuna) - { - var searchSpace = new Dictionary>(); - for (int i = 0; i < n; i++) - { - var numSpace = new List(); - for (int j = 0; j < nTrials; j++) - { - numSpace.Add(Lb[i] + ((Ub[i] - Lb[i]) * j / (nTrials - 1))); - } - searchSpace.Add(VarNickName[i], numSpace); - } - nTrials = (int)Math.Pow(nTrials, n); - return optuna.samplers.GridSampler(searchSpace); - } - - private dynamic SetNSGAIISamplerSettings(dynamic optuna) - { - NSGAII nsga2 = Settings.Optimize.Sampler.NsgaII; - return optuna.samplers.NSGAIISampler( - population_size: nsga2.PopulationSize, - mutation_prob: nsga2.MutationProb, - crossover_prob: nsga2.CrossoverProb, - swapping_prob: nsga2.SwappingProb, - seed: nsga2.Seed - ); - } - - private dynamic SetTPESamplerSettings(dynamic optuna) - { - Tpe tpe = Settings.Optimize.Sampler.Tpe; - return optuna.samplers.TPESampler( - seed: tpe.Seed, - consider_prior: tpe.ConsiderPrior, - prior_weight: 1.0, - consider_magic_clip: tpe.ConsiderMagicClip, - consider_endpoints: tpe.ConsiderEndpoints, - n_startup_trials: tpe.NStartupTrials, - n_ei_candidates: tpe.NEICandidates, - multivariate: tpe.Multivariate, - group: tpe.Group, - warn_independent_sampling: tpe.WarnIndependentSampling, - constant_liar: tpe.ConstantLiar - ); - } public double[] GetXOptimum() { diff --git a/Tunny/Solver/Optuna/Sampler.cs b/Tunny/Solver/Optuna/Sampler.cs new file mode 100644 index 00000000..ab6d3722 --- /dev/null +++ b/Tunny/Solver/Optuna/Sampler.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; + +using Tunny.Settings; +using Tunny.Util; + +namespace Tunny.Solver.Optuna +{ + public class Sampler + { + internal static dynamic Random(dynamic optuna, TunnySettings settings) + { + Settings.Random random = settings.Optimize.Sampler.Random; + return optuna.samplers.RandomSampler( + seed: random.Seed + ); + } + + internal static dynamic CmaEs(dynamic optuna, TunnySettings settings) + { + CmaEs cmaEs = settings.Optimize.Sampler.CmaEs; + return optuna.samplers.CmaEsSampler( + sigma0: cmaEs.Sigma0, + n_startup_trials: cmaEs.NStartupTrials, + warn_independent_sampling: cmaEs.WarnIndependentSampling, + seed: cmaEs.Seed, + consider_pruned_trials: cmaEs.ConsiderPrunedTrials, + restart_strategy: cmaEs.RestartStrategy, + inc_popsize: cmaEs.IncPopsize, + use_separable_cma: cmaEs.UseSeparableCma + ); + } + + internal static dynamic Grid(dynamic optuna, SliderValueParameters sliders, ref int nTrials) + { + var searchSpace = new Dictionary>(); + for (int i = 0; i < sliders.LowerBond.Length; i++) + { + var numSpace = new List(); + for (int j = 0; j < nTrials; j++) + { + numSpace.Add(sliders.LowerBond[i] + (sliders.UpperBond[i] - sliders.LowerBond[i]) * j / (nTrials - 1)); + } + searchSpace.Add(sliders.NickName[i], numSpace); + } + nTrials = (int)Math.Pow(nTrials, sliders.Count); + return optuna.samplers.GridSampler(searchSpace); + } + + internal static dynamic NSGAII(dynamic optuna, TunnySettings settings) + { + NSGAII nsga2 = settings.Optimize.Sampler.NsgaII; + return optuna.samplers.NSGAIISampler( + population_size: nsga2.PopulationSize, + mutation_prob: nsga2.MutationProb, + crossover_prob: nsga2.CrossoverProb, + swapping_prob: nsga2.SwappingProb, + seed: nsga2.Seed + ); + } + + internal static dynamic TPE(dynamic optuna, TunnySettings settings) + { + Tpe tpe = settings.Optimize.Sampler.Tpe; + return optuna.samplers.TPESampler( + seed: tpe.Seed, + consider_prior: tpe.ConsiderPrior, + prior_weight: 1.0, + consider_magic_clip: tpe.ConsiderMagicClip, + consider_endpoints: tpe.ConsiderEndpoints, + n_startup_trials: tpe.NStartupTrials, + n_ei_candidates: tpe.NEICandidates, + multivariate: tpe.Multivariate, + group: tpe.Group, + warn_independent_sampling: tpe.WarnIndependentSampling, + constant_liar: tpe.ConstantLiar + ); + } + } +} diff --git a/Tunny/Solver/Optuna.cs b/Tunny/Solver/Optuna/optuna.cs similarity index 97% rename from Tunny/Solver/Optuna.cs rename to Tunny/Solver/Optuna/optuna.cs index cf475fbf..8fcb0e3b 100644 --- a/Tunny/Solver/Optuna.cs +++ b/Tunny/Solver/Optuna/optuna.cs @@ -12,16 +12,16 @@ using Tunny.UI; using Tunny.Util; -namespace Tunny.Solver +namespace Tunny.Solver.Optuna { - public class Optuna + public class optuna { public double[] XOpt { get; private set; } private double[] FxOpt { get; set; } private readonly string _componentFolder; - public Optuna(string componentFolder) + public optuna(string componentFolder) { _componentFolder = componentFolder; string envPath = PythonInstaller.GetEmbeddedPythonPath() + @"\python310.dll"; @@ -57,7 +57,7 @@ EvaluatedGHResult Eval(double[] x, int progress) try { - var tpe = new OptunaAlgorithm(lb, ub, varNickName, objNickName, settings, Eval); + var tpe = new Algorithm(lb, ub, varNickName, objNickName, settings, Eval); tpe.Solve(); XOpt = tpe.GetXOptimum(); FxOpt = tpe.GetFxOptimum(); diff --git a/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs b/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs index d11ecc43..c4becda5 100644 --- a/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs +++ b/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs @@ -2,7 +2,7 @@ using System.Diagnostics; using System.Windows.Forms; -using Tunny.Solver; +using Tunny.Solver.Optuna; using Tunny.Util; namespace Tunny.UI @@ -26,7 +26,7 @@ private void DashboardButton_Click(object sender, EventArgs e) private void SelectedTypePlotButton_Click(object sender, EventArgs e) { - var optuna = new Optuna(_component.GhInOut.ComponentFolder); + var optuna = new optuna(_component.GhInOut.ComponentFolder); optuna.ShowSelectedTypePlot(visualizeTypeComboBox.Text, studyNameTextBox.Text); } } diff --git a/Tunny/Util/SliderValueParameters.cs b/Tunny/Util/SliderValueParameters.cs new file mode 100644 index 00000000..0ced736a --- /dev/null +++ b/Tunny/Util/SliderValueParameters.cs @@ -0,0 +1,10 @@ +namespace Tunny.Util +{ + public class SliderValueParameters + { + public int Count { get; set; } + public double[] LowerBond { get; set; } + public double[] UpperBond { get; set; } + public string[] NickName { get; set; } + } +} From 27cb1a167add210bbf7ef4d42c58e1a817f8455b Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 7 Jul 2022 15:45:24 +0900 Subject: [PATCH 56/79] Clean sliderValueParam to variable --- Tunny/Solver/Optuna/Algorithm.cs | 30 ++++++++++++----------------- Tunny/Solver/Optuna/Sampler.cs | 10 +++++----- Tunny/Solver/Optuna/optuna.cs | 17 +++------------- Tunny/Util/GrasshopperInOut.cs | 4 ++-- Tunny/Util/SliderValueParameters.cs | 10 ---------- Tunny/Util/Variables.cs | 6 +++--- 6 files changed, 25 insertions(+), 52 deletions(-) delete mode 100644 Tunny/Util/SliderValueParameters.cs diff --git a/Tunny/Solver/Optuna/Algorithm.cs b/Tunny/Solver/Optuna/Algorithm.cs index 25f016b5..31cd9c56 100644 --- a/Tunny/Solver/Optuna/Algorithm.cs +++ b/Tunny/Solver/Optuna/Algorithm.cs @@ -13,7 +13,7 @@ namespace Tunny.Solver.Optuna { public class Algorithm { - private SliderValueParameters Sliders { get; set; } + private List Variables { get; set; } private string[] ObjNickName { get; set; } private TunnySettings Settings { get; set; } private Func EvalFunc { get; set; } @@ -21,17 +21,11 @@ public class Algorithm private double[] FxOpt { get; set; } public Algorithm( - double[] lb, double[] ub, string[] varNickName, string[] objNickName, + List variables, string[] objNickName, TunnySettings settings, Func evalFunc) { - Sliders = new SliderValueParameters - { - LowerBond = lb, - UpperBond = ub, - NickName = varNickName, - Count = lb.Length - }; + Variables = variables; ObjNickName = objNickName; Settings = settings; EvalFunc = evalFunc; @@ -54,7 +48,7 @@ public void Solve() using (Py.GIL()) { dynamic optuna = Py.Import("optuna"); - dynamic sampler = SetSamplerSettings(Sliders, samplerType, ref nTrials, optuna); + dynamic sampler = SetSamplerSettings(samplerType, ref nTrials, optuna); dynamic study = optuna.create_study( sampler: sampler, @@ -72,7 +66,7 @@ public void Solve() name.Remove(name.Length - 1, 1); SetStudyUserAttr(study, name); - double[] xTest = new double[Sliders.Count]; + double[] xTest = new double[Variables.Count]; var result = new EvaluatedGHResult(); int trialNum = 0; @@ -94,9 +88,9 @@ public void Solve() int nullCount = 0; while (nullCount < 10) { - for (int j = 0; j < Sliders.Count; j++) + for (int j = 0; j < Variables.Count; j++) { - xTest[j] = trial.suggest_uniform(Sliders.NickName[j], Sliders.LowerBond[j], Sliders.UpperBond[j]); + xTest[j] = trial.suggest_uniform(Variables[j].NickName, Variables[j].LowerBond, Variables[j].UpperBond); } result = EvalFunc(xTest, progress); @@ -126,13 +120,13 @@ public void Solve() { double[] values = (double[])study.best_params.values(); string[] keys = (string[])study.best_params.keys(); - double[] opt = new double[Sliders.Count]; + double[] opt = new double[Variables.Count]; - for (int i = 0; i < Sliders.Count; i++) + for (int i = 0; i < Variables.Count; i++) { for (int j = 0; j < keys.Length; j++) { - if (keys[j] == Sliders.NickName[i]) + if (keys[j] == Variables[i].NickName) { opt[i] = values[j]; } @@ -182,7 +176,7 @@ private static void SetTrialUserAttr(EvaluatedGHResult result, dynamic trial) } } - private dynamic SetSamplerSettings(SliderValueParameters sliders, int samplerType, ref int nTrials, dynamic optuna) + private dynamic SetSamplerSettings(int samplerType, ref int nTrials, dynamic optuna) { dynamic sampler; switch (samplerType) @@ -200,7 +194,7 @@ private dynamic SetSamplerSettings(SliderValueParameters sliders, int samplerTyp sampler = Sampler.Random(optuna, Settings); break; case 4: - sampler = Sampler.Grid(optuna, sliders, ref nTrials); + sampler = Sampler.Grid(optuna, Variables, ref nTrials); break; default: throw new ArgumentException("Unknown sampler type"); diff --git a/Tunny/Solver/Optuna/Sampler.cs b/Tunny/Solver/Optuna/Sampler.cs index ab6d3722..fb981f98 100644 --- a/Tunny/Solver/Optuna/Sampler.cs +++ b/Tunny/Solver/Optuna/Sampler.cs @@ -31,19 +31,19 @@ internal static dynamic CmaEs(dynamic optuna, TunnySettings settings) ); } - internal static dynamic Grid(dynamic optuna, SliderValueParameters sliders, ref int nTrials) + internal static dynamic Grid(dynamic optuna, List variables, ref int nTrials) { var searchSpace = new Dictionary>(); - for (int i = 0; i < sliders.LowerBond.Length; i++) + for (int i = 0; i < variables.Count; i++) { var numSpace = new List(); for (int j = 0; j < nTrials; j++) { - numSpace.Add(sliders.LowerBond[i] + (sliders.UpperBond[i] - sliders.LowerBond[i]) * j / (nTrials - 1)); + numSpace.Add(variables[i].LowerBond + (variables[i].UpperBond - variables[i].LowerBond) * j / (nTrials - 1)); } - searchSpace.Add(sliders.NickName[i], numSpace); + searchSpace.Add(variables[i].NickName, numSpace); } - nTrials = (int)Math.Pow(nTrials, sliders.Count); + nTrials = (int)Math.Pow(nTrials, variables.Count); return optuna.samplers.GridSampler(searchSpace); } diff --git a/Tunny/Solver/Optuna/optuna.cs b/Tunny/Solver/Optuna/optuna.cs index 8fcb0e3b..428704ae 100644 --- a/Tunny/Solver/Optuna/optuna.cs +++ b/Tunny/Solver/Optuna/optuna.cs @@ -34,21 +34,8 @@ public bool RunSolver( Func, int, EvaluatedGHResult> evaluate, TunnySettings settings) { - int dVar = variables.Count; - double[] lb = new double[dVar]; - double[] ub = new double[dVar]; - bool[] isInteger = new bool[dVar]; - string[] varNickName = new string[dVar]; string[] objNickName = objectives.Select(x => x.NickName).ToArray(); - for (int i = 0; i < dVar; i++) - { - lb[i] = Convert.ToDouble(variables[i].LowerBond); - ub[i] = Convert.ToDouble(variables[i].UpperBond); - isInteger[i] = variables[i].IsInteger; - varNickName[i] = variables[i].NickName; - } - EvaluatedGHResult Eval(double[] x, int progress) { var decimals = x.Select(Convert.ToDecimal).ToList(); @@ -57,7 +44,7 @@ EvaluatedGHResult Eval(double[] x, int progress) try { - var tpe = new Algorithm(lb, ub, varNickName, objNickName, settings, Eval); + var tpe = new Algorithm(variables, objNickName, settings, Eval); tpe.Solve(); XOpt = tpe.GetXOptimum(); FxOpt = tpe.GetFxOptimum(); @@ -79,6 +66,7 @@ EvaluatedGHResult Eval(double[] x, int progress) public void ShowSelectedTypePlot(string visualize, string studyName) { + //TODO: Use settings path to get the path of the database. string storage = "sqlite:///" + _componentFolder + "/Tunny_Opt_Result.db"; PythonEngine.Initialize(); using (Py.GIL()) @@ -148,6 +136,7 @@ public void ShowSelectedTypePlot(string visualize, string studyName) public ModelResult[] GetModelResult(int[] resultNum, string studyName) { + //TODO: Use settings path to get the path of the database. string storage = "sqlite:///" + _componentFolder + "/Tunny_Opt_Result.db"; var modelResult = new List(); PythonEngine.Initialize(); diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index a017fb4d..67e0dab5 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -127,7 +127,7 @@ private void SetInputSliderValues(ICollection variables) break; } - variables.Add(new Variable(lowerBond, upperBond, isInteger, nickName)); + variables.Add(new Variable(Convert.ToDouble(lowerBond), Convert.ToDouble(upperBond), isInteger, nickName)); } } @@ -144,7 +144,7 @@ private void SetInputGenePoolValues(ICollection variables) for (int j = 0; j < genePool.Count; j++) { string nickName = "genepool" + count++; - variables.Add(new Variable(lowerBond, upperBond, isInteger, nickName)); + variables.Add(new Variable(Convert.ToDouble(lowerBond), Convert.ToDouble(upperBond), isInteger, nickName)); } } } diff --git a/Tunny/Util/SliderValueParameters.cs b/Tunny/Util/SliderValueParameters.cs deleted file mode 100644 index 0ced736a..00000000 --- a/Tunny/Util/SliderValueParameters.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Tunny.Util -{ - public class SliderValueParameters - { - public int Count { get; set; } - public double[] LowerBond { get; set; } - public double[] UpperBond { get; set; } - public string[] NickName { get; set; } - } -} diff --git a/Tunny/Util/Variables.cs b/Tunny/Util/Variables.cs index 20dc5f6c..4407b955 100644 --- a/Tunny/Util/Variables.cs +++ b/Tunny/Util/Variables.cs @@ -2,12 +2,12 @@ namespace Tunny.Util { public struct Variable { - public decimal LowerBond { get; } - public decimal UpperBond { get; } + public double LowerBond { get; } + public double UpperBond { get; } public bool IsInteger { get; } public string NickName { get; } - public Variable(decimal lowerBond, decimal upperBond, bool isInteger, string nickName) + public Variable(double lowerBond, double upperBond, bool isInteger, string nickName) { LowerBond = lowerBond; UpperBond = upperBond; From 7c1e18db73a8e225221cce76f39292ad2e90d6c1 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 7 Jul 2022 15:58:49 +0900 Subject: [PATCH 57/79] Fix visalize do not use setting storage path --- CHANGELOG.md | 2 +- Tunny/Optimization/OptimizeLoop.cs | 5 ++--- Tunny/Optimization/OutputLoop.cs | 4 +++- Tunny/Solver/Optuna/optuna.cs | 17 ++++++++--------- Tunny/UI/OptimizeWindowTab/OutputTab.cs | 1 + Tunny/UI/OptimizeWindowTab/VisualizeTab.cs | 2 +- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d20e9ca..17f8947e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - Stopped sampling when there was no geometry input - Once optimize output error, the component won't run again - I've tried to do a proper Dispose to fix this problem, but it still doesn't work sometimes. -- Optuna-DashBoard and Delete Result Files functions do not work properly when a different Storage path is specified in Settings than the default. +- Optuna-DashBoard and storage relate functions do not work properly when a different Storage path is specified in Settings than the default. ## [0.3.0] -2022-05-03 diff --git a/Tunny/Optimization/OptimizeLoop.cs b/Tunny/Optimization/OptimizeLoop.cs index ab4e27c6..79815d69 100644 --- a/Tunny/Optimization/OptimizeLoop.cs +++ b/Tunny/Optimization/OptimizeLoop.cs @@ -54,10 +54,9 @@ private static double[] RunOptimizationLoop(BackgroundWorker worker) return new[] { double.NaN }; } - var optunaSolver = new optuna(s_component.GhInOut.ComponentFolder); + var optunaSolver = new Optuna(s_component.GhInOut.ComponentFolder, Settings); - bool solverStarted = optunaSolver.RunSolver( - variables, objectives, EvaluateFunction, Settings); + bool solverStarted = optunaSolver.RunSolver(variables, objectives, EvaluateFunction); return solverStarted ? optunaSolver.XOpt : new[] { double.NaN }; } diff --git a/Tunny/Optimization/OutputLoop.cs b/Tunny/Optimization/OutputLoop.cs index 640887ac..abfa4990 100644 --- a/Tunny/Optimization/OutputLoop.cs +++ b/Tunny/Optimization/OutputLoop.cs @@ -6,6 +6,7 @@ using Rhino.Geometry; using Tunny.Component; +using Tunny.Settings; using Tunny.Solver.Optuna; using Tunny.Type; using Tunny.UI; @@ -17,6 +18,7 @@ internal static class OutputLoop { private static BackgroundWorker s_worker; private static TunnyComponent s_component; + public static TunnySettings Settings; public static string StudyName; public static string[] NickNames; public static int[] Indices; @@ -29,7 +31,7 @@ internal static void Run(object sender, DoWorkEventArgs e) var fishes = new List(); - var optunaSolver = new optuna(s_component.GhInOut.ComponentFolder); + var optunaSolver = new Optuna(s_component.GhInOut.ComponentFolder, Settings); ModelResult[] modelResult = optunaSolver.GetModelResult(Indices, StudyName); if (modelResult.Length == 0) { diff --git a/Tunny/Solver/Optuna/optuna.cs b/Tunny/Solver/Optuna/optuna.cs index 428704ae..692d62fa 100644 --- a/Tunny/Solver/Optuna/optuna.cs +++ b/Tunny/Solver/Optuna/optuna.cs @@ -14,16 +14,18 @@ namespace Tunny.Solver.Optuna { - public class optuna + public class Optuna { public double[] XOpt { get; private set; } private double[] FxOpt { get; set; } private readonly string _componentFolder; + private readonly TunnySettings _settings; - public optuna(string componentFolder) + public Optuna(string componentFolder, TunnySettings settings) { _componentFolder = componentFolder; + _settings = settings; string envPath = PythonInstaller.GetEmbeddedPythonPath() + @"\python310.dll"; Environment.SetEnvironmentVariable("PYTHONNET_PYDLL", envPath, EnvironmentVariableTarget.Process); } @@ -31,8 +33,7 @@ public optuna(string componentFolder) public bool RunSolver( List variables, IEnumerable objectives, - Func, int, EvaluatedGHResult> evaluate, - TunnySettings settings) + Func, int, EvaluatedGHResult> evaluate) { string[] objNickName = objectives.Select(x => x.NickName).ToArray(); @@ -44,7 +45,7 @@ EvaluatedGHResult Eval(double[] x, int progress) try { - var tpe = new Algorithm(variables, objNickName, settings, Eval); + var tpe = new Algorithm(variables, objNickName, _settings, Eval); tpe.Solve(); XOpt = tpe.GetXOptimum(); FxOpt = tpe.GetFxOptimum(); @@ -66,8 +67,7 @@ EvaluatedGHResult Eval(double[] x, int progress) public void ShowSelectedTypePlot(string visualize, string studyName) { - //TODO: Use settings path to get the path of the database. - string storage = "sqlite:///" + _componentFolder + "/Tunny_Opt_Result.db"; + string storage = "sqlite:///" + _settings.Storage; PythonEngine.Initialize(); using (Py.GIL()) { @@ -136,8 +136,7 @@ public void ShowSelectedTypePlot(string visualize, string studyName) public ModelResult[] GetModelResult(int[] resultNum, string studyName) { - //TODO: Use settings path to get the path of the database. - string storage = "sqlite:///" + _componentFolder + "/Tunny_Opt_Result.db"; + string storage = "sqlite:///" + _settings.Storage; var modelResult = new List(); PythonEngine.Initialize(); using (Py.GIL()) diff --git a/Tunny/UI/OptimizeWindowTab/OutputTab.cs b/Tunny/UI/OptimizeWindowTab/OutputTab.cs index a451e20c..cae36f03 100644 --- a/Tunny/UI/OptimizeWindowTab/OutputTab.cs +++ b/Tunny/UI/OptimizeWindowTab/OutputTab.cs @@ -33,6 +33,7 @@ private void ReflectToSliderButton_Click(object sender, EventArgs e) private void RunOutputLoop(OutputMode mode) { OutputLoop.Mode = mode; + OutputLoop.Settings = _settings; OutputLoop.StudyName = studyNameTextBox.Text; OutputLoop.NickNames = _component.GhInOut.Variables.Select(x => x.NickName).ToArray(); int[] indices = outputModelNumTextBox.Text.Split(',').Select(int.Parse).ToArray(); diff --git a/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs b/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs index c4becda5..4ce1f948 100644 --- a/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs +++ b/Tunny/UI/OptimizeWindowTab/VisualizeTab.cs @@ -26,7 +26,7 @@ private void DashboardButton_Click(object sender, EventArgs e) private void SelectedTypePlotButton_Click(object sender, EventArgs e) { - var optuna = new optuna(_component.GhInOut.ComponentFolder); + var optuna = new Optuna(_component.GhInOut.ComponentFolder, _settings); optuna.ShowSelectedTypePlot(visualizeTypeComboBox.Text, studyNameTextBox.Text); } } From c7a7cce64adc39e7daa8d3b3e6e5eb9f63522639 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 7 Jul 2022 20:32:34 +0900 Subject: [PATCH 58/79] Fix CodeClimate issue --- Tunny/Solver/Optuna/Algorithm.cs | 118 +++++++++++----------- Tunny/Solver/Optuna/optuna.cs | 124 ++++++++++++------------ Tunny/UI/OptimizeWindowTab/OutputTab.cs | 7 +- 3 files changed, 131 insertions(+), 118 deletions(-) diff --git a/Tunny/Solver/Optuna/Algorithm.cs b/Tunny/Solver/Optuna/Algorithm.cs index 31cd9c56..6077d5d8 100644 --- a/Tunny/Solver/Optuna/Algorithm.cs +++ b/Tunny/Solver/Optuna/Algorithm.cs @@ -66,82 +66,87 @@ public void Solve() name.Remove(name.Length - 1, 1); SetStudyUserAttr(study, name); - double[] xTest = new double[Variables.Count]; - var result = new EvaluatedGHResult(); + RunOptimize(nTrials, timeout, study, out double[] xTest, out EvaluatedGHResult result); + SetResultValues(nObjective, study, xTest, result); + } + PythonEngine.Shutdown(); + } - int trialNum = 0; - DateTime startTime = DateTime.Now; - while (true) + private void SetResultValues(int nObjective, dynamic study, double[] xTest, EvaluatedGHResult result) + { + if (nObjective == 1) + { + double[] values = (double[])study.best_params.values(); + string[] keys = (string[])study.best_params.keys(); + double[] opt = new double[Variables.Count]; + + for (int i = 0; i < Variables.Count; i++) { - if (trialNum == nTrials || (DateTime.Now - startTime).TotalSeconds >= timeout) + for (int j = 0; j < keys.Length; j++) { - break; + if (keys[j] == Variables[i].NickName) + { + opt[i] = values[j]; + } } + } + XOpt = opt; + FxOpt = new[] { (double)study.best_value }; + } + else + { + XOpt = xTest; + FxOpt = result.ObjectiveValues.ToArray(); + } + } + + private void RunOptimize(int nTrials, double timeout, dynamic study, out double[] xTest, out EvaluatedGHResult result) + { + xTest = new double[Variables.Count]; + result = new EvaluatedGHResult(); + int trialNum = 0; + DateTime startTime = DateTime.Now; + while (true) + { + if (trialNum == nTrials || (DateTime.Now - startTime).TotalSeconds >= timeout) + { + break; + } - int progress = trialNum * 100 / nTrials; - dynamic trial = study.ask(); + int progress = trialNum * 100 / nTrials; + dynamic trial = study.ask(); - //TODO: Is this the correct way to handle the case of null? - //Other than TPE, the value is returned at random when retrying, so the value will be anything but null. - //TPEs, on the other hand, search for a specific location determined by GP, - //so the value tends to remain the same even after retries and there is no way to get out. - int nullCount = 0; - while (nullCount < 10) + //TODO: Is this the correct way to handle the case of null? + int nullCount = 0; + while (nullCount < 10) + { + for (int j = 0; j < Variables.Count; j++) { - for (int j = 0; j < Variables.Count; j++) - { - xTest[j] = trial.suggest_uniform(Variables[j].NickName, Variables[j].LowerBond, Variables[j].UpperBond); - } - result = EvalFunc(xTest, progress); - - if (result.ObjectiveValues.Contains(double.NaN)) - { - trial = study.ask(); - nullCount++; - } - else - { - break; - } + xTest[j] = trial.suggest_uniform(Variables[j].NickName, Variables[j].LowerBond, Variables[j].UpperBond); } + result = EvalFunc(xTest, progress); - SetTrialUserAttr(result, trial); - try + if (result.ObjectiveValues.Contains(double.NaN)) { - study.tell(trial, result.ObjectiveValues.ToArray()); + trial = study.ask(); + nullCount++; } - catch + else { + break; } - trialNum++; } - if (nObjective == 1) + SetTrialUserAttr(result, trial); + try { - double[] values = (double[])study.best_params.values(); - string[] keys = (string[])study.best_params.keys(); - double[] opt = new double[Variables.Count]; - - for (int i = 0; i < Variables.Count; i++) - { - for (int j = 0; j < keys.Length; j++) - { - if (keys[j] == Variables[i].NickName) - { - opt[i] = values[j]; - } - } - } - XOpt = opt; - FxOpt = new[] { (double)study.best_value }; + study.tell(trial, result.ObjectiveValues.ToArray()); } - else + catch { - XOpt = xTest; - FxOpt = result.ObjectiveValues.ToArray(); } + trialNum++; } - PythonEngine.Shutdown(); } private static void SetStudyUserAttr(dynamic study, StringBuilder name) @@ -202,7 +207,6 @@ private dynamic SetSamplerSettings(int samplerType, ref int nTrials, dynamic opt return sampler; } - public double[] GetXOptimum() { return XOpt; diff --git a/Tunny/Solver/Optuna/optuna.cs b/Tunny/Solver/Optuna/optuna.cs index 692d62fa..7704c2b7 100644 --- a/Tunny/Solver/Optuna/optuna.cs +++ b/Tunny/Solver/Optuna/optuna.cs @@ -17,8 +17,6 @@ namespace Tunny.Solver.Optuna public class Optuna { public double[] XOpt { get; private set; } - private double[] FxOpt { get; set; } - private readonly string _componentFolder; private readonly TunnySettings _settings; @@ -48,23 +46,26 @@ EvaluatedGHResult Eval(double[] x, int progress) var tpe = new Algorithm(variables, objNickName, _settings, Eval); tpe.Solve(); XOpt = tpe.GetXOptimum(); - FxOpt = tpe.GetFxOptimum(); TunnyMessageBox.Show("Solver completed successfully.", "Tunny"); - return true; } catch (Exception e) { - TunnyMessageBox.Show( - "Tunny runtime error:\n" + - "Please send below message (& gh file if possible) to Tunny support.\n\n" + - "\" " + e.Message + " \"", "Tunny", - MessageBoxButtons.OK, MessageBoxIcon.Error); + ShowErrorMessages(e); return false; } } + private static void ShowErrorMessages(Exception e) + { + TunnyMessageBox.Show( + "Tunny runtime error:\n" + + "Please send below message (& gh file if possible) to Tunny support.\n\n" + + "\" " + e.Message + " \"", "Tunny", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + public void ShowSelectedTypePlot(string visualize, string studyName) { string storage = "sqlite:///" + _settings.Storage; @@ -86,45 +87,7 @@ public void ShowSelectedTypePlot(string visualize, string studyName) string[] nickNames = ((string)study.user_attrs["objective_names"]).Split(','); try { - dynamic vis; - switch (visualize) - { - case "contour": - vis = optuna.visualization.plot_contour(study, target_name: nickNames[0]); - vis.show(); - break; - case "EDF": - vis = optuna.visualization.plot_edf(study, target_name: nickNames[0]); - vis.show(); - break; - case "intermediate values": - vis = optuna.visualization.plot_intermediate_values(study); - vis.show(); - break; - case "optimization history": - vis = optuna.visualization.plot_optimization_history(study, target_name: nickNames[0]); - vis.show(); - break; - case "parallel coordinate": - vis = optuna.visualization.plot_parallel_coordinate(study, target_name: nickNames[0]); - vis.show(); - break; - case "param importances": - vis = optuna.visualization.plot_param_importances(study, target_name: nickNames[0]); - vis.show(); - break; - case "pareto front": - vis = optuna.visualization.plot_pareto_front(study, target_names: nickNames); - vis.show(); - break; - case "slice": - vis = optuna.visualization.plot_slice(study, target_name: nickNames[0]); - vis.show(); - break; - default: - TunnyMessageBox.Show("This visualization type is not supported in this study case.", "Tunny"); - break; - } + ShowPlot(optuna, visualize, study, nickNames); } catch (Exception) { @@ -134,6 +97,42 @@ public void ShowSelectedTypePlot(string visualize, string studyName) PythonEngine.Shutdown(); } + private static void ShowPlot(dynamic optuna, string visualize, dynamic study, string[] nickNames) + { + dynamic vis; + switch (visualize) + { + case "contour": + vis = optuna.visualization.plot_contour(study, target_name: nickNames[0]); + break; + case "EDF": + vis = optuna.visualization.plot_edf(study, target_name: nickNames[0]); + break; + case "intermediate values": + vis = optuna.visualization.plot_intermediate_values(study); + break; + case "optimization history": + vis = optuna.visualization.plot_optimization_history(study, target_name: nickNames[0]); + break; + case "parallel coordinate": + vis = optuna.visualization.plot_parallel_coordinate(study, target_name: nickNames[0]); + break; + case "param importances": + vis = optuna.visualization.plot_param_importances(study, target_name: nickNames[0]); + break; + case "pareto front": + vis = optuna.visualization.plot_pareto_front(study, target_names: nickNames); + break; + case "slice": + vis = optuna.visualization.plot_slice(study, target_name: nickNames[0]); + break; + default: + TunnyMessageBox.Show("This visualization type is not supported in this study case.", "Tunny"); + return; + } + vis.show(); + } + public ModelResult[] GetModelResult(int[] resultNum, string studyName) { string storage = "sqlite:///" + _settings.Storage; @@ -154,34 +153,39 @@ public ModelResult[] GetModelResult(int[] resultNum, string studyName) return modelResult.ToArray(); } - if (resultNum[0] == -1) - { + SetTrialsToModelResult(resultNum, modelResult, study); + } + PythonEngine.Shutdown(); + + return modelResult.ToArray(); + } + + private void SetTrialsToModelResult(int[] resultNum, List modelResult, dynamic study) + { + switch (resultNum[0]) + { + case -1: var bestTrials = (dynamic[])study.best_trials; foreach (dynamic trial in bestTrials) { ParseTrial(modelResult, trial); } - } - else if (resultNum[0] == -10) - { + break; + case -10: var trials = (dynamic[])study.trials; foreach (dynamic trial in trials) { ParseTrial(modelResult, trial); } - } - else - { + break; + default: foreach (int res in resultNum) { dynamic trial = study.trials[res]; ParseTrial(modelResult, trial); } - } + break; } - PythonEngine.Shutdown(); - - return modelResult.ToArray(); } private static void ParseTrial(ICollection modelResult, dynamic trial) diff --git a/Tunny/UI/OptimizeWindowTab/OutputTab.cs b/Tunny/UI/OptimizeWindowTab/OutputTab.cs index cae36f03..64b51c57 100644 --- a/Tunny/UI/OptimizeWindowTab/OutputTab.cs +++ b/Tunny/UI/OptimizeWindowTab/OutputTab.cs @@ -37,6 +37,12 @@ private void RunOutputLoop(OutputMode mode) OutputLoop.StudyName = studyNameTextBox.Text; OutputLoop.NickNames = _component.GhInOut.Variables.Select(x => x.NickName).ToArray(); int[] indices = outputModelNumTextBox.Text.Split(',').Select(int.Parse).ToArray(); + SetOutputIndices(mode, indices); + outputResultBackgroundWorker.RunWorkerAsync(_component); + } + + private static void SetOutputIndices(OutputMode mode, int[] indices) + { switch (mode) { case OutputMode.ParatoSolutions: @@ -55,7 +61,6 @@ private void RunOutputLoop(OutputMode mode) default: throw new ArgumentException("Unsupported output mode."); } - outputResultBackgroundWorker.RunWorkerAsync(_component); } private static void CheckIndicesLength(int[] indices) From 9d44f51d9a2f57e8a7cd4f0ecd4c521051dd1809 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 7 Jul 2022 20:46:51 +0900 Subject: [PATCH 59/79] Clean Optuna.cs --- Tunny/Solver/Optuna/{optuna.cs => Optuna.cs} | 44 ++++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) rename Tunny/Solver/Optuna/{optuna.cs => Optuna.cs} (89%) diff --git a/Tunny/Solver/Optuna/optuna.cs b/Tunny/Solver/Optuna/Optuna.cs similarity index 89% rename from Tunny/Solver/Optuna/optuna.cs rename to Tunny/Solver/Optuna/Optuna.cs index 7704c2b7..c3d44cf6 100644 --- a/Tunny/Solver/Optuna/optuna.cs +++ b/Tunny/Solver/Optuna/Optuna.cs @@ -162,29 +162,29 @@ public ModelResult[] GetModelResult(int[] resultNum, string studyName) private void SetTrialsToModelResult(int[] resultNum, List modelResult, dynamic study) { - switch (resultNum[0]) + if (resultNum[0] == -1) { - case -1: - var bestTrials = (dynamic[])study.best_trials; - foreach (dynamic trial in bestTrials) - { - ParseTrial(modelResult, trial); - } - break; - case -10: - var trials = (dynamic[])study.trials; - foreach (dynamic trial in trials) - { - ParseTrial(modelResult, trial); - } - break; - default: - foreach (int res in resultNum) - { - dynamic trial = study.trials[res]; - ParseTrial(modelResult, trial); - } - break; + var bestTrials = (dynamic[])study.best_trials; + foreach (dynamic trial in bestTrials) + { + ParseTrial(modelResult, trial); + } + } + else if (resultNum[0] == -10) + { + var trials = (dynamic[])study.trials; + foreach (dynamic trial in trials) + { + ParseTrial(modelResult, trial); + } + } + else + { + foreach (int res in resultNum) + { + dynamic trial = study.trials[res]; + ParseTrial(modelResult, trial); + } } } From 7a4943585684c59cb8bb31a9aa8d00314273c581 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 7 Jul 2022 20:52:47 +0900 Subject: [PATCH 60/79] Fix Codacy issue --- Tunny/Optimization/OptimizeLoop.cs | 2 +- Tunny/Solver/Optuna/Sampler.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tunny/Optimization/OptimizeLoop.cs b/Tunny/Optimization/OptimizeLoop.cs index 79815d69..32969b97 100644 --- a/Tunny/Optimization/OptimizeLoop.cs +++ b/Tunny/Optimization/OptimizeLoop.cs @@ -17,7 +17,7 @@ internal static class OptimizeLoop { private static BackgroundWorker s_worker; private static TunnyComponent s_component; - public static TunnySettings Settings; + public static readonly TunnySettings Settings; internal static void RunMultiple(object sender, DoWorkEventArgs e) { diff --git a/Tunny/Solver/Optuna/Sampler.cs b/Tunny/Solver/Optuna/Sampler.cs index fb981f98..45b63e0e 100644 --- a/Tunny/Solver/Optuna/Sampler.cs +++ b/Tunny/Solver/Optuna/Sampler.cs @@ -6,7 +6,7 @@ namespace Tunny.Solver.Optuna { - public class Sampler + public static class Sampler { internal static dynamic Random(dynamic optuna, TunnySettings settings) { From eea4a20cfdfa8026788c020bbfa41103adffd8f3 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 7 Jul 2022 20:57:10 +0900 Subject: [PATCH 61/79] Fix build error --- Tunny/Optimization/OptimizeLoop.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tunny/Optimization/OptimizeLoop.cs b/Tunny/Optimization/OptimizeLoop.cs index 32969b97..d09e352a 100644 --- a/Tunny/Optimization/OptimizeLoop.cs +++ b/Tunny/Optimization/OptimizeLoop.cs @@ -17,13 +17,12 @@ internal static class OptimizeLoop { private static BackgroundWorker s_worker; private static TunnyComponent s_component; - public static readonly TunnySettings Settings; + public static TunnySettings Settings; internal static void RunMultiple(object sender, DoWorkEventArgs e) { s_worker = sender as BackgroundWorker; s_component = e.Argument as TunnyComponent; - s_component.GhInOutInstantiate(); double[] result = RunOptimizationLoop(s_worker); From 4462f2def69479cb9e960c1f21add8b71fb3536e Mon Sep 17 00:00:00 2001 From: hrntsm Date: Thu, 7 Jul 2022 21:30:47 +0900 Subject: [PATCH 62/79] Add Optimze end state --- Tunny/Solver/Optuna/Algorithm.cs | 17 ++++++++++++++++- Tunny/Solver/Optuna/Optuna.cs | 24 ++++++++++++++++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Tunny/Solver/Optuna/Algorithm.cs b/Tunny/Solver/Optuna/Algorithm.cs index 6077d5d8..c87365d3 100644 --- a/Tunny/Solver/Optuna/Algorithm.cs +++ b/Tunny/Solver/Optuna/Algorithm.cs @@ -19,6 +19,7 @@ public class Algorithm private Func EvalFunc { get; set; } private double[] XOpt { get; set; } private double[] FxOpt { get; set; } + public EndState EndState { get; set; } = EndState.Error; public Algorithm( List variables, string[] objNickName, @@ -108,8 +109,14 @@ private void RunOptimize(int nTrials, double timeout, dynamic study, out double[ DateTime startTime = DateTime.Now; while (true) { - if (trialNum == nTrials || (DateTime.Now - startTime).TotalSeconds >= timeout) + if (trialNum >= nTrials) { + EndState = EndState.AllTrialFinish; + break; + } + else if ((DateTime.Now - startTime).TotalSeconds >= timeout) + { + EndState = EndState.Timeout; break; } @@ -216,5 +223,13 @@ public double[] GetFxOptimum() { return FxOpt; } + + } + + public enum EndState + { + AllTrialFinish, + Timeout, + Error } } diff --git a/Tunny/Solver/Optuna/Optuna.cs b/Tunny/Solver/Optuna/Optuna.cs index c3d44cf6..dd849abd 100644 --- a/Tunny/Solver/Optuna/Optuna.cs +++ b/Tunny/Solver/Optuna/Optuna.cs @@ -43,11 +43,11 @@ EvaluatedGHResult Eval(double[] x, int progress) try { - var tpe = new Algorithm(variables, objNickName, _settings, Eval); - tpe.Solve(); - XOpt = tpe.GetXOptimum(); + var optimize = new Algorithm(variables, objNickName, _settings, Eval); + optimize.Solve(); + XOpt = optimize.GetXOptimum(); - TunnyMessageBox.Show("Solver completed successfully.", "Tunny"); + ShowEndMessages(optimize); return true; } catch (Exception e) @@ -57,6 +57,22 @@ EvaluatedGHResult Eval(double[] x, int progress) } } + private static void ShowEndMessages(Algorithm optimize) + { + switch (optimize.EndState) + { + case EndState.Timeout: + TunnyMessageBox.Show("Solver completed successfully.\nThe specified time has elapsed.", "Tunny"); + break; + case EndState.AllTrialFinish: + TunnyMessageBox.Show("Solver completed successfully.\nThe specified number of trials has been completed.", "Tunny"); + break; + default: + TunnyMessageBox.Show("Solver error.", "Tunny"); + break; + } + } + private static void ShowErrorMessages(Exception e) { TunnyMessageBox.Show( From 5113754903a8d7fb1e85323c80864c9c54f87f6d Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 11:25:50 +0900 Subject: [PATCH 63/79] Update opt stop button to enable --- Tunny/Optimization/OptimizeLoop.cs | 1 + Tunny/Solver/Optuna/Algorithm.cs | 12 +++-- Tunny/Solver/Optuna/Optuna.cs | 5 +- Tunny/UI/OptimizationWindow.Designer.cs | 60 +++++++++++------------ Tunny/UI/OptimizeWindowTab/OptimizeTab.cs | 1 + 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/Tunny/Optimization/OptimizeLoop.cs b/Tunny/Optimization/OptimizeLoop.cs index d09e352a..a8d49723 100644 --- a/Tunny/Optimization/OptimizeLoop.cs +++ b/Tunny/Optimization/OptimizeLoop.cs @@ -18,6 +18,7 @@ internal static class OptimizeLoop private static BackgroundWorker s_worker; private static TunnyComponent s_component; public static TunnySettings Settings; + public static bool IsForcedStopOptimize { get; set; } internal static void RunMultiple(object sender, DoWorkEventArgs e) { diff --git a/Tunny/Solver/Optuna/Algorithm.cs b/Tunny/Solver/Optuna/Algorithm.cs index c87365d3..8a6c07b1 100644 --- a/Tunny/Solver/Optuna/Algorithm.cs +++ b/Tunny/Solver/Optuna/Algorithm.cs @@ -30,7 +30,6 @@ public Algorithm( ObjNickName = objNickName; Settings = settings; EvalFunc = evalFunc; - } public void Solve() @@ -111,7 +110,7 @@ private void RunOptimize(int nTrials, double timeout, dynamic study, out double[ { if (trialNum >= nTrials) { - EndState = EndState.AllTrialFinish; + EndState = EndState.AllTrialCompleted; break; } else if ((DateTime.Now - startTime).TotalSeconds >= timeout) @@ -119,6 +118,12 @@ private void RunOptimize(int nTrials, double timeout, dynamic study, out double[ EndState = EndState.Timeout; break; } + else if (OptimizeLoop.IsForcedStopOptimize) + { + EndState = EndState.StoppedByUser; + OptimizeLoop.IsForcedStopOptimize = false; + break; + } int progress = trialNum * 100 / nTrials; dynamic trial = study.ask(); @@ -228,8 +233,9 @@ public double[] GetFxOptimum() public enum EndState { - AllTrialFinish, + AllTrialCompleted, Timeout, + StoppedByUser, Error } } diff --git a/Tunny/Solver/Optuna/Optuna.cs b/Tunny/Solver/Optuna/Optuna.cs index dd849abd..3337c78b 100644 --- a/Tunny/Solver/Optuna/Optuna.cs +++ b/Tunny/Solver/Optuna/Optuna.cs @@ -64,9 +64,12 @@ private static void ShowEndMessages(Algorithm optimize) case EndState.Timeout: TunnyMessageBox.Show("Solver completed successfully.\nThe specified time has elapsed.", "Tunny"); break; - case EndState.AllTrialFinish: + case EndState.AllTrialCompleted: TunnyMessageBox.Show("Solver completed successfully.\nThe specified number of trials has been completed.", "Tunny"); break; + case EndState.StoppedByUser: + TunnyMessageBox.Show("Solver completed successfully.\nThe user stopped the solver.", "Tunny"); + break; default: TunnyMessageBox.Show("Solver error.", "Tunny"); break; diff --git a/Tunny/UI/OptimizationWindow.Designer.cs b/Tunny/UI/OptimizationWindow.Designer.cs index 0611b898..a735e42d 100644 --- a/Tunny/UI/OptimizationWindow.Designer.cs +++ b/Tunny/UI/OptimizationWindow.Designer.cs @@ -44,6 +44,8 @@ private void InitializeComponent() this.studyNameTextBox = new System.Windows.Forms.TextBox(); this.optimizeTabControl = new System.Windows.Forms.TabControl(); this.optimizeTabPage = new System.Windows.Forms.TabPage(); + this.timeoutNumUpDown = new System.Windows.Forms.NumericUpDown(); + this.Timeout = new System.Windows.Forms.Label(); this.visualizeTabPage = new System.Windows.Forms.TabPage(); this.dashboardButton = new System.Windows.Forms.Button(); this.visualizeButton = new System.Windows.Forms.Button(); @@ -69,17 +71,15 @@ private void InitializeComponent() this.openResultFolderButton = new System.Windows.Forms.Button(); this.clearResultButton = new System.Windows.Forms.Button(); this.outputResultBackgroundWorker = new System.ComponentModel.BackgroundWorker(); - this.Timeout = new System.Windows.Forms.Label(); - this.timeoutNumUpDown = new System.Windows.Forms.NumericUpDown(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); ((System.ComponentModel.ISupportInitialize)(this.nTrialNumUpDown)).BeginInit(); this.optimizeTabControl.SuspendLayout(); this.optimizeTabPage.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.timeoutNumUpDown)).BeginInit(); this.visualizeTabPage.SuspendLayout(); this.outputTabPage.SuspendLayout(); this.settingsTabPage.SuspendLayout(); this.fileTabPage.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.timeoutNumUpDown)).BeginInit(); this.SuspendLayout(); // // optimizeRunButton @@ -237,6 +237,32 @@ private void InitializeComponent() this.optimizeTabPage.Text = "Optimize"; this.optimizeTabPage.UseVisualStyleBackColor = true; // + // timeoutNumUpDown + // + this.timeoutNumUpDown.Location = new System.Drawing.Point(197, 102); + this.timeoutNumUpDown.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); + this.timeoutNumUpDown.Maximum = new decimal(new int[] { + 10000000, + 0, + 0, + 0}); + this.timeoutNumUpDown.Name = "timeoutNumUpDown"; + this.timeoutNumUpDown.Size = new System.Drawing.Size(165, 30); + this.timeoutNumUpDown.TabIndex = 12; + this.timeoutNumUpDown.ThousandsSeparator = true; + this.toolTip1.SetToolTip(this.timeoutNumUpDown, "After this time has elapsed, optimization stops.\r\nIf 0 is entered, no stop by tim" + + "e is performed."); + // + // Timeout + // + this.Timeout.AutoSize = true; + this.Timeout.Location = new System.Drawing.Point(16, 102); + this.Timeout.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.Timeout.Name = "Timeout"; + this.Timeout.Size = new System.Drawing.Size(132, 23); + this.Timeout.TabIndex = 11; + this.Timeout.Text = "Timeout (sec)"; + // // visualizeTabPage // this.visualizeTabPage.Controls.Add(this.dashboardButton); @@ -520,32 +546,6 @@ private void InitializeComponent() this.clearResultButton.Text = "Clear result flie"; this.clearResultButton.UseVisualStyleBackColor = true; this.clearResultButton.Click += new System.EventHandler(this.ClearResultButton_Click); - // - // Timeout - // - this.Timeout.AutoSize = true; - this.Timeout.Location = new System.Drawing.Point(16, 102); - this.Timeout.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.Timeout.Name = "Timeout"; - this.Timeout.Size = new System.Drawing.Size(132, 23); - this.Timeout.TabIndex = 11; - this.Timeout.Text = "Timeout (sec)"; - // - // timeoutNumUpDown - // - this.timeoutNumUpDown.Location = new System.Drawing.Point(197, 102); - this.timeoutNumUpDown.Margin = new System.Windows.Forms.Padding(4, 6, 4, 6); - this.timeoutNumUpDown.Maximum = new decimal(new int[] { - 10000000, - 0, - 0, - 0}); - this.timeoutNumUpDown.Name = "timeoutNumUpDown"; - this.timeoutNumUpDown.Size = new System.Drawing.Size(165, 30); - this.timeoutNumUpDown.TabIndex = 12; - this.timeoutNumUpDown.ThousandsSeparator = true; - this.toolTip1.SetToolTip(this.timeoutNumUpDown, "After this time has elapsed from the start of optimization, optimization is stopp" + - "ed."); // // OptimizationWindow // @@ -564,13 +564,13 @@ private void InitializeComponent() this.optimizeTabControl.ResumeLayout(false); this.optimizeTabPage.ResumeLayout(false); this.optimizeTabPage.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.timeoutNumUpDown)).EndInit(); this.visualizeTabPage.ResumeLayout(false); this.visualizeTabPage.PerformLayout(); this.outputTabPage.ResumeLayout(false); this.outputTabPage.PerformLayout(); this.settingsTabPage.ResumeLayout(false); this.fileTabPage.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.timeoutNumUpDown)).EndInit(); this.ResumeLayout(false); } diff --git a/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs b/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs index 209b7ff9..0144ab5e 100644 --- a/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs +++ b/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs @@ -62,6 +62,7 @@ private void OptimizeStopButton_Click(object sender, EventArgs e) { optimizeRunButton.Enabled = true; optimizeStopButton.Enabled = false; + OptimizeLoop.IsForcedStopOptimize = true; if (optimizeBackgroundWorker != null) { From 6a729f08a51f0dd8ed05b1a2aa370879f7bf3cfa Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 15:29:08 +0900 Subject: [PATCH 64/79] Update output stop button to enable --- Tunny/Optimization/OutputLoop.cs | 10 ++-- Tunny/Solver/Optuna/Optuna.cs | 62 +++++++++++++++++++------ Tunny/UI/OptimizeWindowTab/OutputTab.cs | 5 ++ 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/Tunny/Optimization/OutputLoop.cs b/Tunny/Optimization/OutputLoop.cs index abfa4990..725b6cf9 100644 --- a/Tunny/Optimization/OutputLoop.cs +++ b/Tunny/Optimization/OutputLoop.cs @@ -23,6 +23,7 @@ internal static class OutputLoop public static string[] NickNames; public static int[] Indices; public static OutputMode Mode; + public static bool IsForcedStopOutput; internal static void Run(object sender, DoWorkEventArgs e) { @@ -32,17 +33,16 @@ internal static void Run(object sender, DoWorkEventArgs e) var fishes = new List(); var optunaSolver = new Optuna(s_component.GhInOut.ComponentFolder, Settings); - ModelResult[] modelResult = optunaSolver.GetModelResult(Indices, StudyName); + ModelResult[] modelResult = optunaSolver.GetModelResult(Indices, StudyName, s_worker); if (modelResult.Length == 0) { TunnyMessageBox.Show("There are no output models. Please check study name.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } - for (int i = 0; i < modelResult.Length; i++) + foreach (ModelResult result in modelResult) { - SetResultToFish(fishes, modelResult[i], NickNames); - s_worker.ReportProgress(i * 100 / modelResult.Length); + SetResultToFish(fishes, result, NickNames); } s_component.Fishes = fishes.ToArray(); @@ -50,7 +50,7 @@ internal static void Run(object sender, DoWorkEventArgs e) if (s_worker != null) { - s_worker.CancelAsync(); + s_worker.Dispose(); } TunnyMessageBox.Show("Output result to fish completed successfully.", "Tunny"); } diff --git a/Tunny/Solver/Optuna/Optuna.cs b/Tunny/Solver/Optuna/Optuna.cs index 3337c78b..c44e6714 100644 --- a/Tunny/Solver/Optuna/Optuna.cs +++ b/Tunny/Solver/Optuna/Optuna.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Windows.Forms; @@ -152,7 +153,7 @@ private static void ShowPlot(dynamic optuna, string visualize, dynamic study, st vis.show(); } - public ModelResult[] GetModelResult(int[] resultNum, string studyName) + public ModelResult[] GetModelResult(int[] resultNum, string studyName, BackgroundWorker worker) { string storage = "sqlite:///" + _settings.Storage; var modelResult = new List(); @@ -172,38 +173,71 @@ public ModelResult[] GetModelResult(int[] resultNum, string studyName) return modelResult.ToArray(); } - SetTrialsToModelResult(resultNum, modelResult, study); + SetTrialsToModelResult(resultNum, modelResult, study, worker); } PythonEngine.Shutdown(); return modelResult.ToArray(); } - private void SetTrialsToModelResult(int[] resultNum, List modelResult, dynamic study) + private void SetTrialsToModelResult(int[] resultNum, List modelResult, dynamic study, BackgroundWorker worker) { if (resultNum[0] == -1) { - var bestTrials = (dynamic[])study.best_trials; - foreach (dynamic trial in bestTrials) + ParatoSolutions(modelResult, study, worker); + } + else if (resultNum[0] == -10) + { + AllTrials(modelResult, study, worker); + } + else + { + UseModelNumber(resultNum, modelResult, study, worker); + } + } + + private void UseModelNumber(int[] resultNum, List modelResult, dynamic study, BackgroundWorker worker) + { + for (int i = 0; i < resultNum.Length; i++) + { + int res = resultNum[i]; + if (OutputLoop.IsForcedStopOutput) { - ParseTrial(modelResult, trial); + break; } + dynamic trial = study.trials[res]; + ParseTrial(modelResult, trial); + worker.ReportProgress(i * 100 / resultNum.Length); } - else if (resultNum[0] == -10) + } + + private void AllTrials(List modelResult, dynamic study, BackgroundWorker worker) + { + var trials = (dynamic[])study.trials; + for (int i = 0; i < trials.Length; i++) { - var trials = (dynamic[])study.trials; - foreach (dynamic trial in trials) + dynamic trial = trials[i]; + if (OutputLoop.IsForcedStopOutput) { - ParseTrial(modelResult, trial); + break; } + ParseTrial(modelResult, trial); + worker.ReportProgress(i * 100 / trials.Length); } - else + } + + private void ParatoSolutions(List modelResult, dynamic study, BackgroundWorker worker) + { + var bestTrials = (dynamic[])study.best_trials; + for (int i = 0; i < bestTrials.Length; i++) { - foreach (int res in resultNum) + dynamic trial = bestTrials[i]; + if (OutputLoop.IsForcedStopOutput) { - dynamic trial = study.trials[res]; - ParseTrial(modelResult, trial); + break; } + ParseTrial(modelResult, trial); + worker.ReportProgress(i * 100 / bestTrials.Length); } } diff --git a/Tunny/UI/OptimizeWindowTab/OutputTab.cs b/Tunny/UI/OptimizeWindowTab/OutputTab.cs index 64b51c57..e08ca07e 100644 --- a/Tunny/UI/OptimizeWindowTab/OutputTab.cs +++ b/Tunny/UI/OptimizeWindowTab/OutputTab.cs @@ -32,6 +32,7 @@ private void ReflectToSliderButton_Click(object sender, EventArgs e) private void RunOutputLoop(OutputMode mode) { + outputStopButton.Enabled = true; OutputLoop.Mode = mode; OutputLoop.Settings = _settings; OutputLoop.StudyName = studyNameTextBox.Text; @@ -80,6 +81,7 @@ private void OutputStopButton_Click(object sender, EventArgs e) { outputResultBackgroundWorker.Dispose(); } + OutputLoop.IsForcedStopOutput = true; switch (OutputLoop.Mode) { case OutputMode.ParatoSolutions: @@ -95,6 +97,9 @@ private void OutputStopButton_Click(object sender, EventArgs e) default: throw new ArgumentException("Unsupported output mode."); } + + outputStopButton.Enabled = false; + OutputLoop.IsForcedStopOutput = false; } private void OutputProgressChangedHandler(object sender, ProgressChangedEventArgs e) From e14edc8d8d1749e550fea0500e813082ebbbdbd9 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 16:39:40 +0900 Subject: [PATCH 65/79] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17f8947e..beef0b5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - When restoring the results from Tunny component as Fishes, those that the trial did not work and Objective could not get are not output. - Update UI - The UI of the Restore tab was confusing, so the UI was modified to make it easier to understand which button to press and how the results are output. +- The progress bar on the Output tab has been made to show progress in a more understandable way. ### Fix @@ -38,6 +39,7 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - Once optimize output error, the component won't run again - I've tried to do a proper Dispose to fix this problem, but it still doesn't work sometimes. - Optuna-DashBoard and storage relate functions do not work properly when a different Storage path is specified in Settings than the default. +- Pressing the stop button in output and the stop button in Optimize does not stop the operation. ## [0.3.0] -2022-05-03 From 1969f7d6d8aefea11d009105c9a1bd17f8e7223a Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 16:44:37 +0900 Subject: [PATCH 66/79] Update pr template --- .github/pull_request_template.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8e9f66fb..ff773d14 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,14 @@ -## Changes +## Added -- +## Changed + +## Deprecated + +## Removed + +## Fixed + +## Security ## Related issue number From d8205f2fe9ccd11a3e8188125b2e425bc07d342e Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 20:59:24 +0900 Subject: [PATCH 67/79] Add input conponent hilight --- Tunny/Component/TunnyAttributes.cs | 78 ++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/Tunny/Component/TunnyAttributes.cs b/Tunny/Component/TunnyAttributes.cs index 5a6cfdcf..10dd1059 100644 --- a/Tunny/Component/TunnyAttributes.cs +++ b/Tunny/Component/TunnyAttributes.cs @@ -1,6 +1,7 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; +using System.Linq; using Grasshopper.GUI; using Grasshopper.GUI.Canvas; @@ -25,26 +26,73 @@ public override GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel) { - if (channel == GH_CanvasChannel.Objects) + switch (channel) { - GH_PaletteStyle normalStyle = GH_Skin.palette_normal_standard; - GH_PaletteStyle warningStyle = GH_Skin.palette_warning_standard; - GH_Skin.palette_normal_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); - GH_Skin.palette_warning_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); - base.Render(canvas, graphics, channel); - GH_Skin.palette_normal_standard = normalStyle; - GH_Skin.palette_warning_standard = warningStyle; + case GH_CanvasChannel.First: + if (Selected) + { + RenderInputComponentBoxes(graphics); + } + break; + case GH_CanvasChannel.Objects: + GH_PaletteStyle normalStyle = GH_Skin.palette_normal_standard; + GH_PaletteStyle warningStyle = GH_Skin.palette_warning_standard; + GH_Skin.palette_normal_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); + GH_Skin.palette_warning_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); + base.Render(canvas, graphics, channel); + GH_Skin.palette_normal_standard = normalStyle; + GH_Skin.palette_warning_standard = warningStyle; + break; + case GH_CanvasChannel.Wires: + DrawVariableWire(canvas, graphics); + DrawObjectiveWire(canvas, graphics); + DrawAttributeWire(canvas, graphics); + break; + default: + base.Render(canvas, graphics, channel); + break; } - else if (channel == GH_CanvasChannel.Wires) + } + + private void RenderInputComponentBoxes(Graphics graphics) + { + Brush[] fill = new[] + { + new SolidBrush(Color.FromArgb(Convert.ToInt32("9900008B", 16))), + new SolidBrush(Color.FromArgb(Convert.ToInt32("99008000", 16))), + new SolidBrush(Color.FromArgb(Convert.ToInt32("998B008B", 16))), + }; + Pen[] edge = new[] { Pens.DarkBlue, Pens.Green, Pens.DarkMagenta }; + for (int i = 0; i < 3; i++) + { + foreach (Guid guid in Owner.Params.Input[i].Sources.Select(s => s.InstanceGuid)) + { + RenderBox(graphics, fill[i], edge[i], guid); + } + } + } + + private void RenderBox(Graphics graphics, Brush fill, Pen edge, Guid guid) + { + GH_Document doc = Owner.OnPingDocument(); + if (doc == null) + { + return; + } + IGH_DocumentObject obj = doc.FindObject(guid, false); + if (obj == null) { - DrawVariableWire(canvas, graphics); - DrawObjectiveWire(canvas, graphics); - DrawModelMeshWire(canvas, graphics); + return; } - else + if (!obj.Attributes.IsTopLevel) { - base.Render(canvas, graphics, channel); + Guid topLevelGuid = obj.Attributes.GetTopLevel.InstanceGuid; + obj = doc.FindObject(topLevelGuid, true); } + var rectangle = GH_Convert.ToRectangle(obj.Attributes.Bounds); + rectangle.Inflate(5, 5); + graphics.FillRectangle(fill, rectangle); + graphics.DrawRectangle(edge, rectangle); } private void DrawVariableWire(GH_Canvas canvas, Graphics graphics) @@ -116,7 +164,7 @@ private void DrawObjectiveWire(GH_Canvas canvas, Graphics graphics) } } - private void DrawModelMeshWire(GH_Canvas canvas, Graphics graphics) + private void DrawAttributeWire(GH_Canvas canvas, Graphics graphics) { IGH_Param param = Owner.Params.Input[2]; PointF p1 = param.Attributes.InputGrip; From 54de89b1003af0a212ff618240a8a5822847b4da Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 21:00:55 +0900 Subject: [PATCH 68/79] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index beef0b5e..e3654b95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - Python package licenses to clearly state the license of each package. - requirements.txt file to avoid conflict python packages versions. - Implemented Timeout to stop optimization over time. +- Input components are now highlighted in color, as are other optimization components. ### Changed From 0bd1ab7df86b9118fc96de48f9ab10ddd2f1af70 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 21:19:51 +0900 Subject: [PATCH 69/79] Fix code climate issue --- Tunny/Component/TunnyAttributes.cs | 115 +++++++++++++---------------- 1 file changed, 50 insertions(+), 65 deletions(-) diff --git a/Tunny/Component/TunnyAttributes.cs b/Tunny/Component/TunnyAttributes.cs index 10dd1059..cebc0775 100644 --- a/Tunny/Component/TunnyAttributes.cs +++ b/Tunny/Component/TunnyAttributes.cs @@ -35,18 +35,10 @@ protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasCha } break; case GH_CanvasChannel.Objects: - GH_PaletteStyle normalStyle = GH_Skin.palette_normal_standard; - GH_PaletteStyle warningStyle = GH_Skin.palette_warning_standard; - GH_Skin.palette_normal_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); - GH_Skin.palette_warning_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); - base.Render(canvas, graphics, channel); - GH_Skin.palette_normal_standard = normalStyle; - GH_Skin.palette_warning_standard = warningStyle; + DrawObjects(canvas, graphics, channel); break; case GH_CanvasChannel.Wires: - DrawVariableWire(canvas, graphics); - DrawObjectiveWire(canvas, graphics); - DrawAttributeWire(canvas, graphics); + DrawWires(canvas, graphics); break; default: base.Render(canvas, graphics, channel); @@ -54,6 +46,24 @@ protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasCha } } + private void DrawObjects(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel) + { + GH_PaletteStyle normalStyle = GH_Skin.palette_normal_standard; + GH_PaletteStyle warningStyle = GH_Skin.palette_warning_standard; + GH_Skin.palette_normal_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); + GH_Skin.palette_warning_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); + base.Render(canvas, graphics, channel); + GH_Skin.palette_normal_standard = normalStyle; + GH_Skin.palette_warning_standard = warningStyle; + } + + private void DrawWires(GH_Canvas canvas, Graphics graphics) + { + DrawVariableWire(canvas, graphics); + DrawObjectiveWire(canvas, graphics); + DrawAttributeWire(canvas, graphics); + } + private void RenderInputComponentBoxes(Graphics graphics) { Brush[] fill = new[] @@ -98,85 +108,48 @@ private void RenderBox(Graphics graphics, Brush fill, Pen edge, Guid guid) private void DrawVariableWire(GH_Canvas canvas, Graphics graphics) { IGH_Param param = Owner.Params.Input[0]; - PointF p1 = param.Attributes.InputGrip; - int wireWidth = 1; - var wireColor = Color.FromArgb(Convert.ToInt32("3300008B", 16)); + var wire = new Wire(2, Color.FromArgb(Convert.ToInt32("3300008B", 16))); if (Owner.Attributes.Selected) { - wireWidth = 3; - wireColor = Color.DarkBlue; - + wire.Width = 3; + wire.Color = Color.DarkBlue; } - foreach (IGH_Param source in param.Sources) - { - PointF p0 = source.Attributes.OutputGrip; - if (!canvas.Painter.ConnectionVisible(p0, p1)) - { - continue; - } - - GraphicsPath wirePath = GH_Painter.ConnectionPath(p0, p1, GH_WireDirection.right, GH_WireDirection.left); - if (wirePath == null) - { - continue; - } - - var wirePen = new Pen(wireColor, wireWidth); - graphics.DrawPath(wirePen, wirePath); - wirePen.Dispose(); - wirePath.Dispose(); - } + DrawPath(canvas, graphics, param, wire); } private void DrawObjectiveWire(GH_Canvas canvas, Graphics graphics) { IGH_Param param = Owner.Params.Input[1]; - PointF p1 = param.Attributes.InputGrip; - int wireWidth = 2; - var wireColor = Color.FromArgb(Convert.ToInt32("33008000", 16)); + var wire = new Wire(2, Color.FromArgb(Convert.ToInt32("33008000", 16))); if (Owner.Attributes.Selected) { - wireWidth = 3; - wireColor = Color.Green; + wire.Width = 3; + wire.Color = Color.Green; } - foreach (IGH_Param source in param.Sources) - { - PointF p0 = source.Attributes.OutputGrip; - if (!canvas.Painter.ConnectionVisible(p0, p1)) - { - continue; - } - - GraphicsPath wirePath = GH_Painter.ConnectionPath(p0, p1, GH_WireDirection.right, GH_WireDirection.left); - if (wirePath == null) - { - continue; - } - - var wirePen = new Pen(wireColor, wireWidth); - graphics.DrawPath(wirePen, wirePath); - wirePen.Dispose(); - wirePath.Dispose(); - } + DrawPath(canvas, graphics, param, wire); } private void DrawAttributeWire(GH_Canvas canvas, Graphics graphics) { IGH_Param param = Owner.Params.Input[2]; - PointF p1 = param.Attributes.InputGrip; - int wireWidth = 2; - var wireColor = Color.FromArgb(Convert.ToInt32("338B008B", 16)); + var wire = new Wire(2, Color.FromArgb(Convert.ToInt32("338B008B", 16))); if (Owner.Attributes.Selected) { - wireWidth = 3; - wireColor = Color.DarkMagenta; + wire.Width = 3; + wire.Color = Color.DarkMagenta; } + DrawPath(canvas, graphics, param, wire); + } + + private static void DrawPath(GH_Canvas canvas, Graphics graphics, IGH_Param param, Wire wire) + { + PointF p1 = param.Attributes.InputGrip; foreach (IGH_Param source in param.Sources) { PointF p0 = source.Attributes.OutputGrip; @@ -191,12 +164,24 @@ private void DrawAttributeWire(GH_Canvas canvas, Graphics graphics) continue; } - var wirePen = new Pen(wireColor, wireWidth); + var wirePen = new Pen(wire.Color, wire.Width); graphics.DrawPath(wirePen, wirePath); wirePen.Dispose(); wirePath.Dispose(); } } + + private class Wire + { + public int Width; + public Color Color; + + public Wire(int wireWidth, Color wireColor) + { + Width = wireWidth; + Color = wireColor; + } + } } } } From 7cbdccb88a0dcb59f7e97f267d44e9eec5ba44a2 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 21:34:55 +0900 Subject: [PATCH 70/79] Clean draw wire --- Tunny/Component/TunnyAttributes.cs | 62 ++++++++---------------------- 1 file changed, 17 insertions(+), 45 deletions(-) diff --git a/Tunny/Component/TunnyAttributes.cs b/Tunny/Component/TunnyAttributes.cs index cebc0775..17eea919 100644 --- a/Tunny/Component/TunnyAttributes.cs +++ b/Tunny/Component/TunnyAttributes.cs @@ -59,9 +59,23 @@ private void DrawObjects(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel c private void DrawWires(GH_Canvas canvas, Graphics graphics) { - DrawVariableWire(canvas, graphics); - DrawObjectiveWire(canvas, graphics); - DrawAttributeWire(canvas, graphics); + Wire[] wires = Owner.Attributes.Selected + ? (new[] + { + new Wire(3, Color.DarkBlue), + new Wire(3, Color.Green), + new Wire(3, Color.DarkMagenta), + }) + : (new[] + { + new Wire(2, Color.FromArgb(Convert.ToInt32("3300008B", 16))), + new Wire(2, Color.FromArgb(Convert.ToInt32("33008000", 16))), + new Wire(2, Color.FromArgb(Convert.ToInt32("338B008B", 16))), + }); + for (int i = 0; i < 2; i++) + { + DrawPath(canvas, graphics, Owner.Params.Input[i], wires[i]); + } } private void RenderInputComponentBoxes(Graphics graphics) @@ -105,48 +119,6 @@ private void RenderBox(Graphics graphics, Brush fill, Pen edge, Guid guid) graphics.DrawRectangle(edge, rectangle); } - private void DrawVariableWire(GH_Canvas canvas, Graphics graphics) - { - IGH_Param param = Owner.Params.Input[0]; - - var wire = new Wire(2, Color.FromArgb(Convert.ToInt32("3300008B", 16))); - if (Owner.Attributes.Selected) - { - wire.Width = 3; - wire.Color = Color.DarkBlue; - } - - DrawPath(canvas, graphics, param, wire); - } - - private void DrawObjectiveWire(GH_Canvas canvas, Graphics graphics) - { - IGH_Param param = Owner.Params.Input[1]; - - var wire = new Wire(2, Color.FromArgb(Convert.ToInt32("33008000", 16))); - if (Owner.Attributes.Selected) - { - wire.Width = 3; - wire.Color = Color.Green; - } - - DrawPath(canvas, graphics, param, wire); - } - - private void DrawAttributeWire(GH_Canvas canvas, Graphics graphics) - { - IGH_Param param = Owner.Params.Input[2]; - - var wire = new Wire(2, Color.FromArgb(Convert.ToInt32("338B008B", 16))); - if (Owner.Attributes.Selected) - { - wire.Width = 3; - wire.Color = Color.DarkMagenta; - } - - DrawPath(canvas, graphics, param, wire); - } - private static void DrawPath(GH_Canvas canvas, Graphics graphics, IGH_Param param, Wire wire) { PointF p1 = param.Attributes.InputGrip; From 5c6ffaa2e76dc2966b9c6fc7b23e84c09ab38b26 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 21:46:21 +0900 Subject: [PATCH 71/79] Fix component color inactive --- Tunny/Component/TunnyAttributes.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Tunny/Component/TunnyAttributes.cs b/Tunny/Component/TunnyAttributes.cs index 17eea919..e1b234f6 100644 --- a/Tunny/Component/TunnyAttributes.cs +++ b/Tunny/Component/TunnyAttributes.cs @@ -50,11 +50,14 @@ private void DrawObjects(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel c { GH_PaletteStyle normalStyle = GH_Skin.palette_normal_standard; GH_PaletteStyle warningStyle = GH_Skin.palette_warning_standard; + GH_PaletteStyle hiddenStyle = GH_Skin.palette_hidden_standard; GH_Skin.palette_normal_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); GH_Skin.palette_warning_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); + GH_Skin.palette_hidden_standard = new GH_PaletteStyle(Color.CornflowerBlue, Color.Blue, Color.Black); base.Render(canvas, graphics, channel); GH_Skin.palette_normal_standard = normalStyle; GH_Skin.palette_warning_standard = warningStyle; + GH_Skin.palette_hidden_standard = hiddenStyle; } private void DrawWires(GH_Canvas canvas, Graphics graphics) @@ -72,7 +75,7 @@ private void DrawWires(GH_Canvas canvas, Graphics graphics) new Wire(2, Color.FromArgb(Convert.ToInt32("33008000", 16))), new Wire(2, Color.FromArgb(Convert.ToInt32("338B008B", 16))), }); - for (int i = 0; i < 2; i++) + for (int i = 0; i < 3; i++) { DrawPath(canvas, graphics, Owner.Params.Input[i], wires[i]); } From 2200acd2c9a2890fba3cda522bf08bbc757b4603 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 21:47:21 +0900 Subject: [PATCH 72/79] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3654b95..ee5b5894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - I've tried to do a proper Dispose to fix this problem, but it still doesn't work sometimes. - Optuna-DashBoard and storage relate functions do not work properly when a different Storage path is specified in Settings than the default. - Pressing the stop button in output and the stop button in Optimize does not stop the operation. +- Components were in their normal color instead of blue. ## [0.3.0] -2022-05-03 From 385b4c75bad1ef48f378876df2f2c006070e3ccf Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 23:05:41 +0900 Subject: [PATCH 73/79] Added error message when not proper input --- Tunny/Util/GrasshopperInOut.cs | 75 +++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index 67e0dab5..499ee596 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -55,20 +55,26 @@ private void SetVariables() Sliders = new List(); _genePool = new List(); - foreach (IGH_Param source in _component.Params.Input[0].Sources) - { - _inputGuids.Add(source.InstanceGuid); - } - + _inputGuids.AddRange(_component.Params.Input[0].Sources.Select(source => source.InstanceGuid)); if (_inputGuids.Count == 0) { - TunnyMessageBox.Show("No input variables found. Please connect a number slider to the input of the component.", "Tunny"); + TunnyMessageBox.Show("No input variables found. Please connect a number slider to the input of the component.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } - foreach (IGH_DocumentObject input in _inputGuids.Select(guid => _document.FindObject(guid, true))) + var variables = new List(); + SortInputs(); + SetInputSliderValues(variables); + SetInputGenePoolValues(variables); + Variables = variables; + } + + private void SortInputs() + { + var errorGuids = new List(); + foreach ((IGH_DocumentObject docObject, int i) in _inputGuids.Select((guid, i) => (_document.FindObject(guid, true), i))) { - switch (input) + switch (docObject) { case GH_NumberSlider slider: Sliders.Add(slider); @@ -76,13 +82,21 @@ private void SetVariables() case GalapagosGeneListObject genePool: _genePool.Add(genePool); break; + default: + errorGuids.Add(docObject.InstanceGuid); + break; } } - - var variables = new List(); - SetInputSliderValues(variables); - SetInputGenePoolValues(variables); - Variables = variables; + if (errorGuids.Count > 0) + { + TunnyMessageBox.Show("Input variables must be either a number slider or a gene pool.\nError input will automatically remove.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); + foreach (Guid guid in errorGuids) + { + _component.Params.Input[0].RemoveSource(guid); + } + _component.ExpireSolution(true); + } + return; } private void SetInputSliderValues(ICollection variables) @@ -149,13 +163,33 @@ private void SetInputGenePoolValues(ICollection variables) } } - private void SetObjectives() { if (_component.Params.Input[1].SourceCount == 0) { - TunnyMessageBox.Show("No objective found. Please connect a number to the objective of the component.", "Tunny"); + TunnyMessageBox.Show("No objective found. Please connect a number to the objective of the component.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + var noNumberObjectives = new List(); + foreach (IGH_Param ghParam in _component.Params.Input[1].Sources) + { + if (ghParam.ToString() != "Grasshopper.Kernel.Parameters.Param_Number") + { + noNumberObjectives.Add(ghParam); + } + } + + if (noNumberObjectives.Count > 0) + { + TunnyMessageBox.Show("Objective supports only the Number input.\nError input will automatically remove.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); + foreach (IGH_Param noNumberSource in noNumberObjectives) + { + _component.Params.Input[1].RemoveSource(noNumberSource); + } + _component.ExpireSolution(true); return; + } Objectives = _component.Params.Input[1].Sources.ToList(); @@ -168,6 +202,17 @@ private void SetAttributes() _attributes = new GH_FishAttribute(); return; } + if (_component.Params.Input[2].SourceCount >= 2) + { + TunnyMessageBox.Show("Inputs to Attribute should be grouped together into a single input.\nError input will automatically remove.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); + while (_component.Params.Input[2].SourceCount > 1) + { + _component.Params.Input[2].RemoveSource(_component.Params.Input[2].Sources[1]); + } + _component.ExpireSolution(true); + _attributes = new GH_FishAttribute(); + return; + } IGH_StructureEnumerator enumerator = _component.Params.Input[2].Sources[0].VolatileData.AllData(true); foreach (IGH_Goo goo in enumerator) From ddc8f3e13f652d0b0e200af29f921e1bf91a61f9 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 23:07:37 +0900 Subject: [PATCH 74/79] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee5b5894..a4f5f52a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - Update UI - The UI of the Restore tab was confusing, so the UI was modified to make it easier to understand which button to press and how the results are output. - The progress bar on the Output tab has been made to show progress in a more understandable way. +- Error messages are displayed when the input to Tunny component is not appropriate, and inappropriate input wires are automatically removed. ### Fix From e1501d770ea3ae4867ee25c58ff378aadee637c4 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 23:14:17 +0900 Subject: [PATCH 75/79] Fix code climate issue --- Tunny/Util/GrasshopperInOut.cs | 55 +++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index 499ee596..1d868a3d 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -89,14 +89,18 @@ private void SortInputs() } if (errorGuids.Count > 0) { - TunnyMessageBox.Show("Input variables must be either a number slider or a gene pool.\nError input will automatically remove.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); - foreach (Guid guid in errorGuids) - { - _component.Params.Input[0].RemoveSource(guid); - } - _component.ExpireSolution(true); + ShowIncorrectVariableInputMessage(errorGuids); + } + } + + private void ShowIncorrectVariableInputMessage(List errorGuids) + { + TunnyMessageBox.Show("Input variables must be either a number slider or a gene pool.\nError input will automatically remove.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); + foreach (Guid guid in errorGuids) + { + _component.Params.Input[0].RemoveSource(guid); } - return; + _component.ExpireSolution(true); } private void SetInputSliderValues(ICollection variables) @@ -182,19 +186,23 @@ private void SetObjectives() if (noNumberObjectives.Count > 0) { - TunnyMessageBox.Show("Objective supports only the Number input.\nError input will automatically remove.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); - foreach (IGH_Param noNumberSource in noNumberObjectives) - { - _component.Params.Input[1].RemoveSource(noNumberSource); - } - _component.ExpireSolution(true); + ShowIncorrectObjectiveInputMessage(noNumberObjectives); return; - } Objectives = _component.Params.Input[1].Sources.ToList(); } + private void ShowIncorrectObjectiveInputMessage(List noNumberObjectives) + { + TunnyMessageBox.Show("Objective supports only the Number input.\nError input will automatically remove.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); + foreach (IGH_Param noNumberSource in noNumberObjectives) + { + _component.Params.Input[1].RemoveSource(noNumberSource); + } + _component.ExpireSolution(true); + } + private void SetAttributes() { if (_component.Params.Input[2].SourceCount == 0) @@ -204,13 +212,7 @@ private void SetAttributes() } if (_component.Params.Input[2].SourceCount >= 2) { - TunnyMessageBox.Show("Inputs to Attribute should be grouped together into a single input.\nError input will automatically remove.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); - while (_component.Params.Input[2].SourceCount > 1) - { - _component.Params.Input[2].RemoveSource(_component.Params.Input[2].Sources[1]); - } - _component.ExpireSolution(true); - _attributes = new GH_FishAttribute(); + ShowIncorrectAttributeInputMessage(); return; } @@ -225,6 +227,17 @@ private void SetAttributes() } } + private void ShowIncorrectAttributeInputMessage() + { + TunnyMessageBox.Show("Inputs to Attribute should be grouped together into a single input.\nError input will automatically remove.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); + while (_component.Params.Input[2].SourceCount > 1) + { + _component.Params.Input[2].RemoveSource(_component.Params.Input[2].Sources[1]); + } + _component.ExpireSolution(true); + _attributes = new GH_FishAttribute(); + } + private bool SetSliderValues(IList parameters) { int i = 0; From c9fb560835e8fddb8a1106f20a62dbf0a943a8e2 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Fri, 8 Jul 2022 23:19:20 +0900 Subject: [PATCH 76/79] Clean code --- Tunny/Util/GrasshopperInOut.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Tunny/Util/GrasshopperInOut.cs b/Tunny/Util/GrasshopperInOut.cs index 1d868a3d..88ade169 100644 --- a/Tunny/Util/GrasshopperInOut.cs +++ b/Tunny/Util/GrasshopperInOut.cs @@ -205,12 +205,12 @@ private void ShowIncorrectObjectiveInputMessage(List noNumberObjectiv private void SetAttributes() { + _attributes = new GH_FishAttribute(); if (_component.Params.Input[2].SourceCount == 0) { - _attributes = new GH_FishAttribute(); return; } - if (_component.Params.Input[2].SourceCount >= 2) + else if (_component.Params.Input[2].SourceCount >= 2) { ShowIncorrectAttributeInputMessage(); return; @@ -235,7 +235,6 @@ private void ShowIncorrectAttributeInputMessage() _component.Params.Input[2].RemoveSource(_component.Params.Input[2].Sources[1]); } _component.ExpireSolution(true); - _attributes = new GH_FishAttribute(); } private bool SetSliderValues(IList parameters) From 3fa98f70722fcb89f22dab8a7718a5d6dbbda803 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sat, 9 Jul 2022 16:22:37 +0900 Subject: [PATCH 77/79] Update python error message more helpful --- Tunny/Optimization/OptimizeLoop.cs | 2 +- Tunny/Solver/Optuna/Algorithm.cs | 101 +++++++++++++++++----- Tunny/Solver/Optuna/Optuna.cs | 12 ++- Tunny/UI/OptimizeWindowTab/OptimizeTab.cs | 5 ++ 4 files changed, 93 insertions(+), 27 deletions(-) diff --git a/Tunny/Optimization/OptimizeLoop.cs b/Tunny/Optimization/OptimizeLoop.cs index a8d49723..150368b8 100644 --- a/Tunny/Optimization/OptimizeLoop.cs +++ b/Tunny/Optimization/OptimizeLoop.cs @@ -27,7 +27,7 @@ internal static void RunMultiple(object sender, DoWorkEventArgs e) s_component.GhInOutInstantiate(); double[] result = RunOptimizationLoop(s_worker); - if (double.IsNaN(result[0])) + if (result == null || double.IsNaN(result[0])) { return; } diff --git a/Tunny/Solver/Optuna/Algorithm.cs b/Tunny/Solver/Optuna/Algorithm.cs index 8a6c07b1..609e6233 100644 --- a/Tunny/Solver/Optuna/Algorithm.cs +++ b/Tunny/Solver/Optuna/Algorithm.cs @@ -19,7 +19,7 @@ public class Algorithm private Func EvalFunc { get; set; } private double[] XOpt { get; set; } private double[] FxOpt { get; set; } - public EndState EndState { get; set; } = EndState.Error; + public EndState EndState { get; set; } public Algorithm( List variables, string[] objNickName, @@ -34,15 +34,13 @@ public Algorithm( public void Solve() { + EndState = EndState.Error; + OptimizeLoop.IsForcedStopOptimize = false; int samplerType = Settings.Optimize.SelectSampler; int nTrials = Settings.Optimize.NumberOfTrials; double timeout = Settings.Optimize.Timeout <= 0 ? double.MaxValue : Settings.Optimize.Timeout; int nObjective = ObjNickName.Length; - string[] directions = new string[nObjective]; - for (int i = 0; i < nObjective; i++) - { - directions[i] = "minimize"; - } + string[] directions = SetDirectionValues(nObjective); PythonEngine.Initialize(); using (Py.GIL()) @@ -50,28 +48,83 @@ public void Solve() dynamic optuna = Py.Import("optuna"); dynamic sampler = SetSamplerSettings(samplerType, ref nTrials, optuna); - dynamic study = optuna.create_study( - sampler: sampler, - directions: directions, - storage: "sqlite:///" + Settings.Storage, - study_name: Settings.StudyName, - load_if_exists: Settings.Optimize.LoadExistStudy - ); - - var name = new StringBuilder(); - foreach (string objName in ObjNickName) + if (CheckExistStudyParameter(nObjective, optuna)) { - name.Append(objName + ","); + dynamic study = CreateStudy(directions, optuna, sampler); + SetStudyUserAttr(study, ObjectNicknameToAttr()); + RunOptimize(nTrials, timeout, study, out double[] xTest, out EvaluatedGHResult result); + SetResultValues(nObjective, study, xTest, result); } - name.Remove(name.Length - 1, 1); - SetStudyUserAttr(study, name); - - RunOptimize(nTrials, timeout, study, out double[] xTest, out EvaluatedGHResult result); - SetResultValues(nObjective, study, xTest, result); } PythonEngine.Shutdown(); } + private StringBuilder ObjectNicknameToAttr() + { + var name = new StringBuilder(); + foreach (string objName in ObjNickName) + { + name.Append(objName + ","); + } + name.Remove(name.Length - 1, 1); + return name; + } + + private dynamic CreateStudy(string[] directions, dynamic optuna, dynamic sampler) + { + return optuna.create_study( + sampler: sampler, + directions: directions, + storage: "sqlite:///" + Settings.Storage, + study_name: Settings.StudyName, + load_if_exists: Settings.Optimize.LoadExistStudy + ); + } + + private static string[] SetDirectionValues(int nObjective) + { + string[] directions = new string[nObjective]; + for (int i = 0; i < nObjective; i++) + { + directions[i] = "minimize"; + } + + return directions; + } + + private bool CheckExistStudyParameter(int nObjective, dynamic optuna) + { + PyList studySummaries = optuna.get_all_study_summaries("sqlite:///" + Settings.Storage); + var directions = new Dictionary(); + + foreach (dynamic pyObj in studySummaries) + { + directions.Add((string)pyObj.study_name, (int)pyObj.directions.__len__()); + } + + return directions.ContainsKey(Settings.StudyName) + ? CheckDirections(nObjective, directions) + : directions.Count == 0; + } + + private bool CheckDirections(int nObjective, Dictionary directions) + { + if (!Settings.Optimize.LoadExistStudy) + { + EndState = EndState.UseExitStudyWithoutLoading; + return false; + } + else if (directions[Settings.StudyName] == nObjective) + { + return true; + } + else + { + EndState = EndState.DirectionNumNotMatch; + return false; + } + } + private void SetResultValues(int nObjective, dynamic study, double[] xTest, EvaluatedGHResult result) { if (nObjective == 1) @@ -96,7 +149,7 @@ private void SetResultValues(int nObjective, dynamic study, double[] xTest, Eval else { XOpt = xTest; - FxOpt = result.ObjectiveValues.ToArray(); + FxOpt = result.ObjectiveValues?.ToArray(); } } @@ -236,6 +289,8 @@ public enum EndState AllTrialCompleted, Timeout, StoppedByUser, + DirectionNumNotMatch, + UseExitStudyWithoutLoading, Error } } diff --git a/Tunny/Solver/Optuna/Optuna.cs b/Tunny/Solver/Optuna/Optuna.cs index c44e6714..f65b8fa8 100644 --- a/Tunny/Solver/Optuna/Optuna.cs +++ b/Tunny/Solver/Optuna/Optuna.cs @@ -63,13 +63,19 @@ private static void ShowEndMessages(Algorithm optimize) switch (optimize.EndState) { case EndState.Timeout: - TunnyMessageBox.Show("Solver completed successfully.\nThe specified time has elapsed.", "Tunny"); + TunnyMessageBox.Show("Solver completed successfully.\n\nThe specified time has elapsed.", "Tunny"); break; case EndState.AllTrialCompleted: - TunnyMessageBox.Show("Solver completed successfully.\nThe specified number of trials has been completed.", "Tunny"); + TunnyMessageBox.Show("Solver completed successfully.\n\nThe specified number of trials has been completed.", "Tunny"); break; case EndState.StoppedByUser: - TunnyMessageBox.Show("Solver completed successfully.\nThe user stopped the solver.", "Tunny"); + TunnyMessageBox.Show("Solver completed successfully.\n\nThe user stopped the solver.", "Tunny"); + break; + case EndState.DirectionNumNotMatch: + TunnyMessageBox.Show("Solver error.\n\nThe number of Objective in the existing Study does not match the one that you tried to run; Match the number of objective, or change the \"Study Name\".", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); + break; + case EndState.UseExitStudyWithoutLoading: + TunnyMessageBox.Show("Solver error.\n\n\"Load if study file exists\" was false even though the same \"Study Name\" exists. Please change the name or set it to true.", "Tunny", MessageBoxButtons.OK, MessageBoxIcon.Error); break; default: TunnyMessageBox.Show("Solver error.", "Tunny"); diff --git a/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs b/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs index 0144ab5e..b8f44a84 100644 --- a/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs +++ b/Tunny/UI/OptimizeWindowTab/OptimizeTab.cs @@ -21,6 +21,7 @@ private void OptimizeRunButton_Click(object sender, EventArgs e) _settings.Optimize.SelectSampler = samplerComboBox.SelectedIndex; _settings.StudyName = studyNameTextBox.Text; _settings.Optimize.Timeout = (double)timeoutNumUpDown.Value; + _settings.Optimize.LoadExistStudy = loadIfExistsCheckBox.Checked; OptimizeLoop.Settings = _settings; if (!CheckInputValue(ghCanvas)) @@ -28,6 +29,10 @@ private void OptimizeRunButton_Click(object sender, EventArgs e) return; } + if (optimizeBackgroundWorker.IsBusy) + { + optimizeBackgroundWorker.Dispose(); + } optimizeBackgroundWorker.RunWorkerAsync(_component); optimizeStopButton.Enabled = true; } From 1f35b5073e7ab729c75f354c84d8bb466a616691 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sat, 9 Jul 2022 16:27:10 +0900 Subject: [PATCH 78/79] Update CHANGELOG.md --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4f5f52a..cbbf6664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,8 +34,11 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - The UI of the Restore tab was confusing, so the UI was modified to make it easier to understand which button to press and how the results are output. - The progress bar on the Output tab has been made to show progress in a more understandable way. - Error messages are displayed when the input to Tunny component is not appropriate, and inappropriate input wires are automatically removed. +- Error massage in python runtime is more clear. + - Added error message when the number of objective does not match the existing Study. + - Added error message when a Study with the same name exists but is used without Loading. -### Fix +### Fixed - Stopped sampling when there was no geometry input - Once optimize output error, the component won't run again @@ -43,6 +46,7 @@ Please see [here](https://github.com/hrntsm/Tunny/releases) for the data release - Optuna-DashBoard and storage relate functions do not work properly when a different Storage path is specified in Settings than the default. - Pressing the stop button in output and the stop button in Optimize does not stop the operation. - Components were in their normal color instead of blue. +- Once an error occurs in PythonRuntime, the optimization will not work after that. ## [0.3.0] -2022-05-03 From 6aad55fff233e70fd19bf1a2c332bc415e9bcf4a Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sat, 9 Jul 2022 21:20:49 +0900 Subject: [PATCH 79/79] Update README.md --- CHANGELOG.md | 2 +- README.md | 152 ++++++++++++++++++++++++++++++++++----------------- 2 files changed, 103 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbbf6664..4d75e5f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p Please see [here](https://github.com/hrntsm/Tunny/releases) for the data released for each version. -## [UNRELEASED] +## [0.4.0] -2022-07-09 ### Added diff --git a/README.md b/README.md index 93892471..94994106 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Release](https://img.shields.io/github/v/release/hrntsm/Tunny)](https://github.com/hrntsm/Tunny/releases) [![download](https://img.shields.io/github/downloads/hrntsm/Tunny/total)](https://github.com/hrntsm/Tunny/releases) -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/c7947be6770545e88153125060b41284)](https://www.codacy.com/gh/hrntsm/Tunny/dashboard?utm_source=github.com&utm_medium=referral&utm_content=hrntsm/Tunny&utm_campaign=Badge_Grade) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/c7947be6770545e88153125060b41284)](https://www.codacy.com/gh/hrntsm/Tunny/dashboard?utm_source=github.com&utm_medium=referral&utm_content=hrntsm/Tunny&utm_campaign=Badge_Grade) [![Maintainability](https://api.codeclimate.com/v1/badges/63a5b0a923062d25ad23/maintainability)](https://codeclimate.com/github/hrntsm/Tunny/maintainability) ![Code Climate technical debt](https://img.shields.io/codeclimate/tech-debt/hrntsm/Tunny) @@ -32,71 +32,83 @@ First, Tunny runs on Windows only. 1. Right-click the file > Properties > make sure there is no "blocked" text 1. In Grasshopper, choose File > Special Folders > Components folder. Move Tunny folder you downloaded there. 1. Restart Rhino and Grasshopper +1. In Grasshopper, Place the Tunny component and double-click the icon to start downloading the necessary libraries. 1. Enjoy! -Tunny also support yak. So you can find Tunny in Rhinoceros package manager. - ## Usage ### Quick usage -![tunny](https://user-images.githubusercontent.com/23289252/166186417-7541ccb9-efa0-4569-a068-373ebde1c0ed.gif) +https://user-images.githubusercontent.com/23289252/178105107-5e9dd9f7-5680-40d4-97b0-840a4f1f329c.mp4 ### Component location -Tunny can be found in the Params tab if it has been installed. +Tunny can be found in the Tunny tab if it has been installed. -![image](https://user-images.githubusercontent.com/23289252/172011237-583835a5-3ecc-4ca9-a77d-29c1f5a1687a.png) +image ### Inputs #### Variables -This component support Number slider & GenePool. +Tunny support Number slider & GenePool. Optimization is performed when this value is changed by Tunny. It is recommended that components be given nicknames, as this makes it easier to understand the resulting process. Here it is named x1, x2, x3. +The genepool values are nicknamed from the top as genepool1, genepool2, and so on. -![image](https://user-images.githubusercontent.com/23289252/172011316-b8e812bd-0362-443f-8841-a2157032b7d1.png) + #### Objectives -Optimization is performed to minimize the value input here. Multi-objective optimization is also supported. +Optimization is performed to minimize the value input here. Multi-objective optimization is also supported. Each objective value have to be separated to a number component. It is recommended to set nickname like input variables. -![image](https://user-images.githubusercontent.com/23289252/172011407-e1c8a0ea-f5bc-466a-9670-d005f35af89c.png) + -#### Geometries +#### Attributes This input is optional. -GeometryBase input is supported as a function to save the model during optimization. -Input of large date size geometries are deprecated because it makes the analysis heavier. +The ConstructFishAttribute component allows you to set an Attribute for each trial of optimization. +ZUI is supported, so you can increase the number of inputs to any number and set Attribute. + +The nickname of the ConstructFishAttribute component input is stored paired with the value entered as the name of that Attribute. + +The Geometry input has a special meaning; what is entered here will be displayed as a Geometry when the results are sorted in the FishMarket component described below. -![image](https://user-images.githubusercontent.com/23289252/172012910-d49e526b-db3f-43cd-af45-1f24a9f0e221.png) + ### Other components -![image](https://user-images.githubusercontent.com/23289252/172013084-a74e08d7-9b8d-47a5-8eef-f6840a369ee1.png) +image -- The Fish component, a Param component dedicated to Tunny, was created to facilitate data handling. -- You can internalize and save the results to an gh file. -- The FishMarket comport allows the results obtained from optimization to be viewed side by side just like a fish market. +- The **Fish** component, a Param component dedicated to Tunny, was created to facilitate data handling. + - You can internalize and save the results to a .gh file. +- The **FishMarket** component allows the results obtained from optimization to be viewed side by side just like a fish market. +- The **Deconstruct Fish** and **Deconstruct Fish Attribute** components are used to retrieve each value. + + - The DeconstructFishAttribute component, like the ExplodeTree component, creates Outputs for the number of Attributes by selecting Match outputs from the context menu. + + image ### Optimization Window Double-click on the component icon to open the form for performing optimization. -The component differs from other optimization components in that it does not graph the learning status during optimization. +The component differs from other optimization components in that it does not graph the learning status during optimization in GrasshopperUI. On the other hand, it is possible to save the learning status, and even after the optimization has been completed once, the study content can be used to perform ongoing optimization. +On the other hand, Running the Dashboard function, which charts the results, starts a server that handles the results and allows you to see the optimization results in real time in your browser. +This feature is useful for analyzing post-optimization results, as it allows the user to not only see the results in real time, but also to view several figures at once. + It is recommended that optimization be performed a small number of times, and after completion, the results should be reviewed to determine if continued optimization should be performed. #### Optimize Tab -![image](https://user-images.githubusercontent.com/23289252/172011516-a0d170f9-f118-4f62-a1aa-b76f4cbb9218.png) +image Values that can be set and their meanings are as follows. @@ -112,20 +124,80 @@ Values that can be set and their meanings are as follows. - This number of trials will be performed. - If the grid sampler is selected, the calculation is performed by dividing each entered Variable by this number. - **Note** that the number of calculations is (Number of trial) to the power of (Number of Variable). +- Timeout(sec) + - After the time set here elapses, optimization stops. + - If 0 is input, no stop by time is performed. - Load if study file exists - If the checkbox is checked and a file of optimization results is available, the results of the training will be used to perform ongoing optimization. - Study Name - Name of the training result to be saved in the optimization result file - RunOptimize - Push the button to perform the optimization. +- Stop + - Forces optimization to stop. + - Even when stopped, the system automatically saves the results up to the most recent evaluation. + +#### Visualize Tab + +image + +Values that can be set and their meanings are as follows. + +- Open Optuna-Dashboard + - Run Real-time Web Dashboard for handling optimization results. + - https://github.com/optuna/optuna-dashboard#features + - You can now more easily check results or even see results in real time. +- Visualize type + - The following types of graphing are supported. See the [Optuna.visualization](https://optuna.readthedocs.io/en/stable/reference/visualization/index.html) page below for more information. + 1. contour + 2. EDF + 3. intermediate values + 4. optimization history + 5. parallel coordinate + 6. param importance + 7. pareto front + 8. slice +- Show selected type of plots + - Show the plot selected in Visualize type above. + +#### Output Tab + +image + +Values that can be set and their meanings are as follows. + +- First, the model number matches the tree structure of the output. + - If the model number is 42, the output is {0; 42}. +- Parato solutions + - Output Parato solutions. +- All trials + - Output all trials. +- Use model number + + - Multiple values can be entered separated by commas, such as "1,3,42". + +- Output + + - The model with the number entered here is restored from the optimization results file and is the output of the component. + - The model number can be found on each plot. + - In the example below, model number 424 with a value of -11.49 for the first objective function is selected. + + image + +- Reflect the result on the sliders + - The result of the input model number is reflected in Slider and Genepool. #### Settings Tab -![image](https://user-images.githubusercontent.com/23289252/172011625-3b5476f8-c143-40a1-809b-e6e066379d1e.png) +image -Tunny stores the settings in this window in json. +Tunny stores the optimize settings in json. Detailed settings in optimization can now also be configured in Json. +- **IMPORTANT**: The default hyperparameter for each optimization contain Optuna defaults. + - These are not necessarily the best values for your optimization. + - Pay particular attention to the initial population or trial. + - the recommended initial trial for TPE is "the number of Variable" x 11 - 1. - Open API page - Open Optuna's API page with the meaning of each value in the advanced optimization settings. - Save settings to json @@ -137,37 +209,16 @@ Detailed settings in optimization can now also be configured in Json. - Open the folder where the settings files are stored. - The file Settings.json is Tunny's settings file. Edit it with any text editor. -#### Result Tab +#### File Tab -![image](https://user-images.githubusercontent.com/23289252/172011663-e60c9e10-f8fc-48bd-b909-2c15b0b23ca9.png) +image -Values that can be set and their meanings are as follows. - -- Dashboard - - Run Real-time Web Dashboard for Optuna. - - https://github.com/optuna/optuna-dashboard#features - - You can now more easily check results or even see results in real time. -- Visualize type - - The following types of graphing are supported. See the [Optuna.visualization](https://optuna.readthedocs.io/en/stable/reference/visualization/index.html) page below for more information. - 1. contour - 2. EDF - 3. intermediate values - 4. optimization history - 5. parallel coordinate - 6. param importance - 7. pareto front - 8. slice - Open result file folder - - Open the folder where the file containing the optimization results is located. The results are stored under the name "Tunny_Opt_Result.db". + - Open the folder where the file containing the optimization results is located. + - Results are saved as "Tunny_Opt_Result.db" by default. + - This can be made to be anything you want by rewriting the Setting.json file. - Clear result file - Deletes the optimization result file. - - If the value of the input changes, delete it if necessary, since optimization using a file containing the same name study will cause content conflicts and optimization will not be performed. -- Set restore model number - - The model with the number entered here is restored from the optimization results file and is the output of the component. - - The model number matches the tree structure of the output. - - -1 is input, the results of all models on the Pareto front will output. - - -10 is input, the results of all models will output - - Clicking the Reflect button will cause Grasshopper to reflect the results of the model number inputted. ## Contact information @@ -176,6 +227,7 @@ Values that can be set and their meanings are as follows. - HP : [https://hiron.dev/](https://hiron.dev/) - Mail : support(at)hrntsm.com - change (at) to @ +- Postings to [the discussion page](https://github.com/hrntsm/Tunny/discussions) are also welcome. ## Donation @@ -191,6 +243,6 @@ Or [pixivFANBOX](https://hiron.fanbox.cc/) Tunny is licensed under the [MIT](https://github.com/hrntsm/Tunny/blob/main/LICENSE) license. Copyright© 2022, hrntsm -Release package is embedded Python runtime & optuna libraries. +Tunny use Python runtime & some python packages. These depend on their own licenses. -Please see PYTHON_PACKAGE_LICENSE for more license informations. +Please see PYTHON_PACKAGE_LICENSE for more license information.