Writing a solver - how to connect & respond to downstream value?

OK, so I really need to write my own simple solver.

But how do I connect to and read from a downstream component value (like from a Num) so I can respond to it in a for-loop, for example.

bild

Say I have the following for-loop;

{
    // Main Simple solver    
    var current_fitness = GetDownstreamNumValue();  //* How to?
    if (current_fitness >= GoodEnough)
    {
         return; // dunwithit
    }

    // Cause some changes downstreams
    m_laststeppervalue++;
    OutputA = m_laststeppervalue * SomeSpookyFormula;

    // ScheduleSolution with callback etc...
}

private double m_laststeppervalue;

private double GetDownstreamNumValue()
{
     // Read downstreams Num component value. Um... How?
}

I understand that I cannot connect the “DownstreamNumValue” to an inport since that would be an illegal circular reference. So there must be another, probaly simple, way to read the downstream component. Access it via a registered guid?

// Rolf

That would be safest. Guid lookup is pretty fast and there are updating issues with using instance references.

You can only get the value inside the parameter after the solution completes, but presumably your code is running inside a solution so that’s fine.

You have to find the object with the correct ID, cast it to a Param_Number and access its VolatileData. This gives you access to the GH_Structure<GH_Number> which stores all calculated values from the most recent solution.

I could call a callback method which saves the value in an instance variable (“m_last_downstream_fitnessvalue”), and ExpireSolution inside the callback, and so check this stored value in the next round in the main method?

// Rolf

Incidentally if you’re using GUIDs to remember specific objects, you’ll want to implement the IGH_InstanceGuidDependent interface. Sometimes all the object ids in a document are mutated (most often when two documents are merged, such as during paste), and you’ll need to know the new id of your target parameter.

By implementing IGH_InstanceGuidDependent you will be informed of these guid changes and you can look up the new guid of an object if you know the old guid.

Where exactly is your code running from? SolveInstance()? A menu or other UI event like button press? A timer?

Or, if I look up the Num component by name (say “Fitness A”) and makes sure it’s unique.

I’m running it from a C# ScriptComponent. See attached def with RIL Solver.ghx (66.8 KB)
the initial stub I have.

// Rolf

This won’t work as you cannot get the value out of C(Num) from within the solution of the script component.

You have to delay getting the value out (probably by using the events associated with completed solutions) and then decide whether to start a new solution.

I’m playing with your file, but I think dinner is getting close. To be continued.

Same here.

For clarity, rename “C(Num)” to “Fitness A”.

// Rolf

What’s the logic behind the slider updates btw.? Should the slider value get bigger if the current value is less than the target value?

No, the sliders are only “settings”.

The only thing is to know when the “Threshold” is reached by the Fitness value, then just terminate as “Converged”

m_converged = GetDownstreamFitnessValue() >= m_threshold;

Kindof

// Rolf

I think I might be very confused then. What is changing over time to make the target number more correct, and who’s in charge of those changes?

That can be any components downstreams, represented by the placeholder named “Whatever Downstream Logic” in my example (pictured below).

As the solver’s output values (A and B) changes, then the “Whatever Downstream Logic's” output chnages as well ( = “Fitness A”). And so the solver can terminate if the (input value) Threshold is reached (or some other goal i come up with).

I just need to know what results this stepper’s output causes downstream so it can terminate when a value is “good enough” ( >= Threshold)

bild

// Rolf

Ah, now I see better what you mean. You are onto Galapagos philosophy. But in my case the target number (Threshold) doesn’t change.

So, this is only about reaching a predefined target goal (which is calculated and reached only by downstreams components though). So this is meant to be a “stepper solver” which terminates at that predefined goal.

The term “fitness” associates to finding an optimum by messing with the input, but that is not really the case here, instead the optimum is known beforehand ( = the Threshold).

// Rolf

So the script outputs zeroes for A and B the first time it runs, then keeps on outputting bigger and bigger values until the number inside Fitness A is within tolerance of some constant?

In this case yes, and yes.

The solver/stepper outputs whatever data (dummy stepper numbers in this case), causing downstreams components to reevaluate (expire), and so at each step I want to check the result value (“Fitness A”) of the downsstream components and then act on that value in the “RILSolver” stepper (terminate the loop, for example).

The idea is that I can then use the final increment (I) of the Solver/Stepper, knowing that I reached some predefined goal (Fitness = reached the Threshold) at just that increment.

Or, if the predefined Threshold wasn’t reached (quitting at MaxIterations) it means that the downstream logic never reached the predefined goal with any of the data emitted from the Solver/Stepper. Which in some cases also can be a meaningful result.

// Rolf

Still a bit confused, but maybe the attached has sufficient tips to get you going. It’s a script which starts at zero and keeps incrementing an output value until the number inside some parameter exceeds a provided limit.

The basic two tricks are:

  1. Handle the SolutionEnd event because that’s when you know whether the current iteration yielded a good enough number.
  2. Schedule a new solution with a callback.

RIL Solver C#.ghx (82.5 KB)

2 Likes

… but now on a higher level? :grinning:

The “private double GetResult()” bit was the bit I just couldn’t figure out. I had “debug printed” almost every property of the component (I picked the “Result” num component by name just like you did), but couldn’t get any value out of it. Perhaps it was because I tried to read it in the main method?

Anyway, thank you very much!

// Rolf

Hi David,
this code has been extremely helpful for a little project I am working on - thank you!

one question - I have noticed that closing rhino while the script is running (i.e. while it’s auto scheduling new solutions) leads to a breakpoint pop-up: “an unhandled document exception was trapped”.

I believe this is the result of closing rhino while some internal timer is still running, so, what would you recommend as the best way to cancel any scheduled solutions if the user closes rhino?

thank you for your help!
UB

There’s a static event called RhinoApp.Closing which may be handled by code which then shuts down the timer. Or maybe handle events on the GH_Document when it goes inactive. I’m not sure what would be soon enough.