Optimization Plug-In for Grasshopper: How to use Galapagos' interface and gene pool?

Hallo,

I have built a prototype of a new optimization tool for Grasshopper, using surrogate modelling (https://en.wikipedia.org/wiki/Surrogate_model). Now I want to develop it into something more user-friendly, so I have two questions:

  1. 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?

  2. 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.)

Thanks,
Thomas

Hi Thomas,

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#).

You probably can just add a reference to the GHA file in Visual Studio, because it is a DLL with its extension changed.

Thank you both.

I’m gonna try Menno’s suggestion first, and then probably get back to you.

Cheers,
Thomas

Just to let you know, I got the genepool to work! :smile:
Example code is attached (with sliders, too).

genepool.gh (10.9 KB)

Cheers,
Thomas

PS: One needs to include Microsoft.CSharp.dll in order to use the dynamic keyword.

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.

1 Like

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?

Yup, you’ll have to draw them entirely yourself.

@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?

Thanks,
Christoph

Hi Christoph,

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.)

1 Like

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) { }

Thanks @ParamDesSing !

sorry, but how do you declare “doc”?
Is this wrong?: GH_Document doc = new GH_Document();

Because trying this will pop up the msgbox forever, even though nothing is happening in the document:

    protected override void SolveInstance(IGH_DataAccess DA)
    {
        doc.NewSolution(true, GH_SolutionMode.CommandLine);
        while (doc.SolutionState != GH_ProcessStep.PostProcess)
        {
             MessageBox.Show ("hi");

        }
    }

Hi @Christoph1,

You can use:

GH_Document doc = Component.OnPingDocument();

Cheers,
Thomas

OptimiTest.gh (3.5 KB)
cs files.zip (2.3 KB)

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;
    }
}

Just from looking at this code, I think that the problem is that you’re not updating fx.

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?

Ah, I think you’re right about the component expiration.
That’s why you should run your code in a separate window, as discussed in this thread:

http://discourse.mcneel.com/t/gh-document-newsolution-crashing-rhino/14744

1 Like

Thanks Thomas!
I tried to follow David’s example code from the thread you mentioned, however in Visual Studio and not in the c# script component.

Unfortunately, now I’m getting an “object has expired” error, exactly when I try to change the slider value.

I found this forum topic, discussing exactly what I am trying to do and mentioning the problems I now encounter:
http://discourse.mcneel.com/t/how-can-i-get-a-number-slider-object-and-set-a-value/12407/6

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?

Thanks a lot! Christoph

Hi Christoph,

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;
}