I have built a prototype of a new optimization tool for Grasshopper, using surrogate modelling (Surrogate model - Wikipedia). Now I want to develop it into something more user-friendly, so I have two questions:
How can I make my component look like Galapagos? Specifically, how do I get the special wires that highlight the number slider they connect to?
Is there any way I can access the gene pool component to use it with with my custom component?
(I’d need to be able get the variable range and set values, which is what I do with number sliders already.)
Galapagos is entirely custom drawn, it takes care of all the display, including the cursors for when you drag wires. You’ll have to provide custom attributes and override all channels in the drawing methods. This is quite a lot of rather boring work, but there’s no way to somehow re-use the code in Galapagos.
I forgot to respond to your gene-pool question. The gene pool is defined inside the GalapagosLibrary.gha and a such is difficult to access from other assemblies. You can probably use it though via reflection or the dynamic keyword (assuming you’re working in C#).
Yeah that’s probably the most useful missing standard reference in the C# component. I may add it to the GH for Rhino6 version and hope it doesn’t break anything.
Just a follow-up question to David regarding the display of the wires in Galapagos:
There’s no setting for the special wires in the Grasshopper API, correct?
Instead, I would need to recreate their look and feel from scratch, right?
@DavidRutten , @ParamDesSing
I have a related question regarding Galapagos and other optimization components:
How do you generally link evaluation models into the optimizers? Is there a way to treat them as anonymous functions / delegates? Or are you working with sth. like “wait until objectiveFunctionValue has changed”?
How was it realized in Galapagos?
In terms of waiting, I’m using a WaitHandle in C#, to check if the definition has been recomputed.
The solver runs as a separate process, and I interact with it via a hidden command line.
(Because the library I’m using requires NumPy and SciPy, as well as other plug-ins not available in IronPython.)
FYI, I’ve ported my code from a C# component to a plug-in, which created some issues with the WaitHandle.
The below solution is probably better anyway.
doc.NewSolution(true, GH_SolutionMode.CommandLine);
while (doc.SolutionState != GH_ProcessStep.PostProcess) { }
I don’t know, I just can’t figure it out… I couldn’t manage it with (doc.SolutionState != GH_ProcessStep.PostProcess) { }.
However, I managed to make something functioning at least a bit, but it never breaks out of the while loop. For some reason, properties of my optimizer class instance “opti” are not maintained, but purged.
I appreciate any help a lot!
namespace GHOptimizationTest
{
public class GHOptimizationTestComponent : GH_Component
{
GH_Document doc;
IGH_Component Component;
public GHOptimizationTestComponent()
: base("GHOptimizationTest", "Nickname",
"Description",
"Category", "Subcategory")
{
}
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddIntegerParameter ("f(x*)", "f(x*)", "optimum", GH_ParamAccess.item);
pManager.AddIntegerParameter("x", "x", "decision variable", GH_ParamAccess.item);
pManager.AddIntegerParameter("f(x)", "f(x)", "objective function value", GH_ParamAccess.item);
}
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
}
protected override void SolveInstance(IGH_DataAccess DA)
{
Component = this;
doc = Component.OnPingDocument();
//component input
int fopt = new int(); //expected optimum
int x = new int(); //decision variable
int fx = new int(); //objective function value
if (!DA.GetData(0, ref fopt)) { return; }
if (!DA.GetData(1, ref x)) { return; }
if (!DA.GetData(2, ref fx)) { return; }
//getting number slider for decision variable, which also updates objective function value once changed
List<Grasshopper.Kernel.Special.GH_NumberSlider> sliders = new List<Grasshopper.Kernel.Special.GH_NumberSlider>();
foreach (IGH_Param param in Component.Params.Input)
{
Grasshopper.Kernel.Special.GH_NumberSlider slider = param.Sources[0] as Grasshopper.Kernel.Special.GH_NumberSlider;
if (slider != null)
sliders.Add(slider);
}
Optimizer opti = new Optimizer(fopt); //initializing optimizer
while(opti.terminate != true) //run until termination criterion fullfilled
{
doc.NewSolution(false);
opti.maximize(x, fx); //optimize
sliders[0].SetSliderValue(opti.xNew); //update decision variable slider
}
}
}
}
and this one is the optimizer class (as a test only simply incrementing a variable)
class Optimizer
{
private int fopti;
public bool terminate;
public int xNew;
public Optimizer(int _fopti)
{
this.fopti = _fopti; //expected optimum value
this.terminate = false;
}
public void maximize(int x, int fx)
{
int _xNew = x;
if (fx < fopti ) //optimum not yet found
{
_xNew = x + 1; //changing decision variable x
}
else if (fx >= fopti) //optimum found
{
this.terminate = true;
MessageBox.Show("optimum found: " + fx);
}
this.xNew = _xNew;
}
}
fx is updated indirectly by sliders[0] which is the variable. Please see picture below.
Actually, everything works and the slider goes up to 10, calling the MessageBox “optimum found” from the optimizer class. But then it doesn’t break out of the while-loop in SolveInstance(), because, as I saw in the debugger, properties of my instance “opti” of the optimizer class, such as “opti.xNew” and “opti.terminate” are not stored, but purged after every iteration.
I suspect it has something to do with how Grasshopper recomputes and expires components?
Have you managed to do it the “inert”-way, David was describing? If so, how do you access and work with the fitness-evaluation components, without changing the number-sliders?
Would there be a chance that you upload a super simple example about this?
It sounds to me like you’re running some code in the component instead of in a separate window.
Below is a simplified excerpt from the code I’m using with a C# component. (All of which runs in the window, and not in the component). Also have a look at the Grasshopper definition I shared earlier in this thread (15 December).
//Get Sliders
private List<Grasshopper.Kernel.Special.GH_NumberSlider> getSliders(IGH_Component component)
{
// Collect all sliders that are plugged into the first input parameter of the script component.
List<Grasshopper.Kernel.Special.GH_NumberSlider> sliders = new List<Grasshopper.Kernel.Special.GH_NumberSlider>();
foreach (IGH_Param param in component.Params.Input[0].Sources)
{
Grasshopper.Kernel.Special.GH_NumberSlider slider = param as Grasshopper.Kernel.Special.GH_NumberSlider;
if (slider != null) sliders.Add(slider);
}
return sliders;
}
//Set Sliders
private void setSliders(IGH_Component component, decimal[] parameters, List<Grasshopper.Kernel.Special.GH_NumberSlider> sliders)
{
for(int i = 0; i < sliders.Count; i++) sliders[i].SetSliderValue(parameters[i]);
}
//Get Objective Value
private double getObjectiveValue(IGH_Component component)
{
GH_Structure<IGH_Goo> objectiveTree = (GH_Structure<IGH_Goo>) component.Params.Input[1].VolatileData;
IGH_Goo objective = objectiveTree.First();
double objectiveValue;
objective.CastTo<double>(out objectiveValue);
return objectiveValue;
}