Parameter Variation with Anylogic

459 views
Skip to first unread message

Lukas Preusser

unread,
Feb 5, 2021, 10:52:40 AM2/5/21
to HeuristicLab
Hello everybody,

I'm quite to new to HeuristicLab, so I have a question regarding the parameter variation experiment with Anylogic.

In Anylogic I have a few parameters which are restricted by certain conditions. These conditions can be found in a Database table in Anylogic. For example if condition x is true, parameter y cannot be 1. Is it possible with HeuristicLab to first check those conditions and then set the parameters in the experiment accordingly? If yes, can someone give me a hint how to do it?

I hope my problem description is clear, if not I can add some additional information :)

Have a great day!!
Lukas

YOUNESS EL HAMZAOUI

unread,
Feb 6, 2021, 7:59:42 AM2/6/21
to heuris...@googlegroups.com
Hi, Lukas, I don't understand your problem

--
You received this message because you are subscribed to the Google Groups "HeuristicLab" group.
To unsubscribe from this group and stop receiving emails from it, send an email to heuristiclab...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/heuristiclab/92c2c3ff-cac5-4e71-9963-8f97ea884b2dn%40googlegroups.com.

Lukas Preusser

unread,
Feb 6, 2021, 8:38:11 AM2/6/21
to HeuristicLab
Hi Yelham,
thanks for your answer, I'll try to explain a little better.

I have multiple parameters (let's say p1, p2, p3)  with which I want to perfom a variation experiment. Those parameters are declared in Anylogic. They can obtain the values 0 or 1. When I now run a genetic algorithm with the parameters, every parameter can be either 0 or 1, depending on the genetic algorithm. That's declared in the Interger.Vector.Bounds. 
Now I want to implement a restriction of these parameters. So for example if p1=1 then p2 ≠ 1. Where and how can I set those restrictions in HeuristicLab?

HeuristicLab

unread,
Feb 7, 2021, 4:49:58 PM2/7/21
to HeuristicLab
Hi,

These conditions are called constraints. A solution typically is called infeasible when such constraints are violated and called feasible when all constraints are satisfied. Constraint handling is a rather huge topic and some general approaches have emerged in the domain of evolutionary algorithms:
1) You may describe your own solution encoding, thereby defining operators that ensure all constraints are satisfied
2) You provide a repair function that transforms any solution of the space of infeasible solutions into a certain solution of the feasible space, this can also be implicit
3) You may detect a violation of the constraint and add it as a penalty to grealy reduce fitness the more the constraint is violated
4) You transform your problem into a multi-objective problem where you include constraint violation as an additional objective
5) Some hybrid form of the above.

So how would you do those in HeuristicLab:
Ad 1) You'll have to write your own encoding plugin and program all the operators
Ad 2) and 3) You can do this as part of the evaluation function (e.g. in a programmable problem)
Ad 4) You'll have to define a multi-objective problem instead of single-objective
Ad 5) Some mixture of the above

I suppose in your case you have an external evaluation problem and I suppose you can't just call AnyLogic with an infeasible solution. Thus, you'll have to do some kind of repairing.
For this case, I would recommend to follow this post here:
and redefine your external evaluation problem as part of a programmable problem. Thereby you can gain access to writing some code as part of the Evaluate method that will perform the repair.
In that code you will have to check the feasibility of your solution and repair it in case of it being invalid. You can either change the solution itself or leave the solution untouched and just provide the repaired solution to AnyLogic.
The performance of both approaches may be quite different and it's hard to say what works better a priori.

You can also write your own encoding plugin, although that requires more time and effort on your side.

For more information on constraint-handling in evolutionary algorithms I refer to the excellent tutorial by Carlos A. Coello freely available on this website:

Sincerely,
Andreas

Lukas Preusser

unread,
Feb 10, 2021, 9:55:00 AM2/10/21
to HeuristicLab
Hi Andreas,
thanks a lot for your detailed answer and the constraint-handling tutorial! That's some really good input for me :)

All the best
Lukas

Lukas Preusser

unread,
Feb 16, 2021, 9:50:50 AM2/16/21
to HeuristicLab
Hi Andreas,

after thinking about your answer and my problem, another question appeared. I am able to call Anylogic with an infeasible solution. In that case I would use a function in Anylogic to detect the infeasible solution and repair it also within a function of Anylogic. Is it then possible to send the repaired parameters back to HeuristicLab, so it connects those repaired parameters instead of the infesible parameters with the value of the fitness function?

In Anylogic after the simulation this code is executed:

try {
  commDriver.sendQuality(currentSolution, quality / replications);
} catch (IOException e) { }
quality = 0;
replications = 0;
getMessage();

So I would test the feasibility of the solution before each simulation run and repair it if needed, run the sim with the repaired parameters and then I would try to modify the currentSolution at the beginning of the code from above. I would replace the infeasible solution from Heuristic Lab with the repaired solution from Anylogic and send it back to HeuristicLab.

Something like:
currentSolution = ParametersRepaired; //Still need to figure out how to do this, but that should be know problem right?

try {
  commDriver.sendQuality(currentSolution, quality / replications);
} catch (IOException e) { }
quality = 0;
replications = 0;
getMessage();


All the best,
Lukas

HeuristicLab

unread,
Feb 16, 2021, 10:12:13 AM2/16/21
to HeuristicLab
Hi,

There is an older sample plugin on how to realize this using extensions. It's a bit dated, but it should give you the idea on how to achieve this:
In this extension, additional fields are defined to the quality message that would return information such as the number of repetitions, variance and so forth.
Note however, that the main proto is before the distinction among single- and multi-objective quality messages. You would have to build this with the current proto and I give no guarantee that this still compiles (it also uses pre-3.3.16 persistence, e.g. StorableClass attribute instead of StorableType).

Sincerely,
Andreas

Lukas Preusser

unread,
Feb 18, 2021, 6:50:36 AM2/18/21
to HeuristicLab
Hi Andreas, 
thanks for your quick reply! That seems quite complex and uncertain, but I will have a look into it! I might come back with some more questions in the future :)

All the best,
Lukas

HeuristicLab

unread,
Feb 18, 2021, 7:12:02 AM2/18/21
to HeuristicLab
Yes, it's not that simple unfortunately. I had not anticipated that we'd need so much flexibility in this interface, respectively I didn't want to make it very complicated with the technology and state of the art I used at the time I implemented this. In addition Google deprecated all the Extensions mechanism in ProtoBuf v3, so this approach kind of put its bet on the wrong horse.

If I'd redo this nowadays, I'd probably be using ZeroMQ, not define a fixed message format and instead leave the protocol for the user to implement. This is much more flexible and would support a wider range of scenarios more easily. ZeroMQ is available for all relevant programming languages.
Basically, you'd need to add a plugin that adds the NetMQ library to HeuristicLab and then you can use the programmable problem to just define the relevant socket and send the appropriate messages. Things like caching wouldn't work as generic as with having a unified message buffer, but... not that important. We could probably realize caching generically in a different form in future versions. You can always implement your own specific cache without that much effort.
Since you can write any code that you like in the Evaluate(..) method you can realize any kind of protocol. Of course, you'll have to think about how you'd encode the solution to transmit it over the wire for yourself (but you can use a string-based encoding for simplicity). On the Java side, you'd replace the HLExternalEvaluation library with a zeromq implementation for java. Instead of getSolution() you'd wait for a message in e.g. a reply socket that the simulation creates and binds to some port. It shouldn't be that difficult, but I don't have a use case right now and also no time to do it.

Lukas Preusser

unread,
Feb 20, 2021, 8:50:40 AM2/20/21
to HeuristicLab
Hi Andreas,
I'm trying to set up a new programmable external program for now and I'm running into a few troubles. My initial problem is similar to the AnyLogic Supply Chain Problem (from https://dev.heuristiclab.com/trac.fcgi/wiki/Documentation/Howto/OptimizeAnyLogicModels). This tutorial by the way was a great help!  
In the screenshot you can see the boundaries for my parameter dimensions and the amount of parameters (IntegerLength). These variables are set up in the GUI of the programmable program.
I'm now havin problems on how to access the boundaries and the amount of parameters. In my code below you can see that I'm trying to encode with new IntegerVectorEncoding. I tried to put in some min, max and step values, but I can't get it to work with the boundaries. Could you give me a hint on how I can get it to work in this particular case? Or do you have any other similar examples of the programmable problem, that I could study? Is it possible to get access to the compiled code of a "regularly" created External Evaluation Problem? I could maybe use that code to transfer it to the programmable program...

In the Evaluate method I'm trying to check the feasibility of the generated solution and want to repair it in case it is infeasible. What would be the best way to access the generated values of the single parameters? I'm trying to write every generated parametervalue into an 2D array and then loop trough the array to check for feasibility. I have Parameters like P11, P12, P13, P21, P22, P23. The array would look like this: [P11, P12, P13] [P21, P22, P23]. If infeasible some code repairs the generated value of Pxy and gives Pxy its new value, with which the simulation in Anylogic is carried out. Quite a few questions for a Saturday, but I went through all the HeuristicLab training slides and right now I'm pretty stuck. It would be great if you could help me once more!

All the best
Lukas

Variables.PNG

public
override void Initialize() {
     
      var IntegerBounds = (IntMatrix)vars.IntegerBounds;
      var IntegerLength = ((IntValue)vars.IntegerLength).Value;
      
      Encoding = new IntegerVectorEncoding("i", length: IntegerLength);  //
      vars.cache = new EvaluationCache();
      var clients = new CheckedItemCollection<IEvaluationServiceClient>();
      var client = new EvaluationServiceClient();
      client.ChannelParameter.Value = new EvaluationTCPChannel("127.0.0.1", 2112);         // was ist mit Retry Anzahl?
      clients.Add(client);
      vars.clients = clients;
     
                    
      vars.messageBuilder = new SolutionMessageBuilder();

      // Define the solution encoding which can also consist of multiple vectors, examples below
 
      //Encoding = new IntegerVectorEncoding("i", length: 5, min: 2, max: 14, step: 2);
    }



    public double Evaluate(Individual individual, IRandom random) {
      
      var solutionMessage = BuildSolutionMessage(individual);
      
      var cache = (EvaluationCache)vars.cache;
      return (cache == null
        ? EvaluateOnNextAvailableClient(solutionMessage)
        : cache.GetValue(solutionMessage, EvaluateOnNextAvailableClient,
            GetQualityMessageExtensions()))
        .GetExtension(SingleObjectiveQualityMessage.QualityMessage_).Quality;    

      // Use vars.yourVariable to access variables in the variable store i.e. yourVariable
      // aus Musterprogramm var quality = 0.0;
      //quality = individual.RealVector("r").Sum(x => x * x);
      // aus Musterprogramm return quality;
    }   

HeuristicLab

unread,
Feb 20, 2021, 3:43:02 PM2/20/21
to HeuristicLab
I am not sure what you are asking me exactly.
The code that you posted shows that you set the problem's encoding to a new IntegerVectorEncoding given a length that you define in a variable "integerLength" in the variable store of the programmable problem. You also define a bounds matrix, but you don't use that anywhwere. That initialize doesn't seem right to me. Also, initialize is called everytime you compile or load the problem from a file.

The code to create an integervector with 5 values each ranging from 0 to 4 for each dimension would be:
// this is just a sample matrix created in code
var bounds = new IntMatrix(1, 2);
bounds[0, 0] = 0;
bounds[0, 1] = 5;
// here you define a new encoding with length 5 and set the bounds
var enc = new IntegerVectorEncoding("i", length: 5);
 enc.Bounds = bounds;
Encoding = enc;

If you want specific bounds for each dimension, you have to make as many rows as dimensions. The matrix has 2 columns per dimension: min and max or 3 columns if you also want to define step size.
I think you're missing this enc.Bounds = bounds;
You can't call Encoding.Bounds = bounds, because "Encoding" is of type "IEncoding" and that doesn't know about bounds.

Lukas Preusser

unread,
Feb 24, 2021, 10:27:16 AM2/24/21
to HeuristicLab
Hi Andreas,
thanks for the clarification and your patience! Sorry, that my question was unclear.
Yes, I was missing enc.Bounds = bounds; now it is working!

In the Evaluate method I now want to check for feasibility and perfom the repair of infeasible solutions. To check the feasibility of the solutions I need to acces the generated values of the single parameters, which are generated by the used algorithm (e.g. genetic algorithm). That's the way I think it works, but maybe I'm wrong. Is this the right approach? For this to work the Evaluate method must be called everytime before we send solutions to Anylogic. Is my understanding of the use of the Evaluate method correct?
Below in the Evaluate method I wrote down some steps to do the feasibility check and the repair regarding your example of an integervector with 5 values each ranging from 0 to 4 for each dimension. Each value represents a parameter.
For steps 1), 2)  and 5) I can't find the specific code to perform these tasks. Any help regarding the right code would be great! I hope that my problem is clear.

All the best 
Lukas

public double Evaluate(Individual individual, IRandom random) {
      
      var solutionMessage = BuildSolutionMessage(individual);
      
      var cache = (EvaluationCache)vars.cache;
      return (cache == null
        ? EvaluateOnNextAvailableClient(solutionMessage)
        : cache.GetValue(solutionMessage, EvaluateOnNextAvailableClient,
            GetQualityMessageExtensions()))
        .GetExtension(SingleObjectiveQualityMessage.QualityMessage_).Quality;   
 
       //      1) get solution message for integervector with 5 values each ranging from 0 to 4 created by algorithm
                     // maybe like in Anylogic: Parameter11 = vector.getData(1); ?          --> example result of this step would be: For iteration 3 Parameter1 = 2; Parameter2 = 1; ....
                     // or do I have to access the cache?

       //      2) write individual solutions for individual values into array var SolutionCheck = new IntArray(5);     --> example:  SolutionCheck = [2,1,...];

       //      3) access array, go through values and check for feasibility and repair in case of infeasibility           --> example: SolutionCheck[2] = 1 infesible --> repair 

       //      4) write repaired values into array      --> example: SolutionCheck[2] = 3

       //      5) update solution with repaired values and continue optimization run with repaired values      --> example: use SolutionCheck[2] = 3 and update solution message for Anylogic

HeuristicLab

unread,
Feb 24, 2021, 4:26:46 PM2/24/21
to HeuristicLab
Hi Lukas,

Ah, so you want to know how you can access the solution vector that the algorithm has produced. You're right that Evaluate is called on every solution that the algorithm wants to know the fitness of. Typically this is not called for solutions where the fitness is already known (e.g. from the previous generation). But there should be a hidden option to force reevaluation in such cases too.
Repairing should be done before "BuildSolutionMessage":

var solutionVector = individual.IntegerVector();

Step 2) seems strange, I am not quite sure I understand the point of SolutionCheck. Is that a datastructure you need to identifiy the feasibility?

Anyway, yu can do the repair, as indicated in Step 3 on the solutionVector for instance like so:
if (solutionVector[0] >= 1) solutionVector[2] = 0;
which would force the value in position 2 to be 0 when the value at position 0 would be greater or equal to one.

The solution is then repaired and the optimization would work with the updated solution.

Then comes the part that you have now like BuildSolutionMessage(individual) which will now take the updated vector within individual and send it to AnyLogic.

I hope that you can realize what you want this way.

Sincerely,
Andreas

Lukas Preusser

unread,
Feb 26, 2021, 11:12:00 AM2/26/21
to HeuristicLab
Hi Andres,
awesome, thanks for your quick reply! var solutionVector = individual.IntegerVector(); is what I was searching for!

Yes you're right regarding step 2). I used your example for simpler understanding, but in my case I have a 2D array in which I check datastructures to identify the feasibility. That's why I can't use the var solutionVector for that and first have to transform the var solutionVector into a 2D array. After that I can check for feasibility and do the repairing.

I'm now at the point that I have the repaired var solutionVector. But Anylogic does not receive repaired values and still runs on infeasible solutions. I guess HeuristicLab does not send the var solutionVector, but the initial individual.IntegerVector() to Anylogic. So I think I have to update the individual.IntegerVector() with the values from var solutionVector, before BuildSolutionMessage(individual) to use the repaired individuals. As var solutionVector and individual.IntegerVector() have the same length I could use something like individual.IntegerVector = solutionVector; But I'm not sure how to access the individual.IntegerVector.
Probably it's again some short code, but that's where I'm unfortunately lacking experience and knowledge...
Any tips on this issue?

Thanks a lot for your time and have a nice weekend!
Lukas

HeuristicLab

unread,
Feb 26, 2021, 11:38:51 AM2/26/21
to HeuristicLab
Hi Lukas,

var solutionVector = individual.IntegerVector() does not perform a copy of the solution. So if you modify that original solutionVector variable, then BuildSolutionMessage() should read the updated solution. Of course, BuildSolutionMessage() must come afterwards, because that does copy the data I think. Maybe you reassign the variable or you modify a copy of it? Check that first.

Otherwise, you can also bypass BuildSolutionMessage() entirely and create the message on your own using the protobuf API.
It should work like this (SolutionMessage is part of the HeuristicLab.Problems.ExternalEvaluation namespace):

var solMsgBuilder = SolutionMessage.CreateBuilder();
var intVecBuilder = SolutionMessage.Types.IntegerArrayVariable.CreateBuilder();
intVecBuilder.SetName(Encoding.Name).AddRangeData(solutionVector).SetLength(solutionVector.Length);
solMsgBuilder.AddIntegerArrayVars(intVecBuilder.Build());
var solutionMessage = solMsgBuilder.Build();

And then you've also got your solution message with the IntegerArrayVars sub-message set. But again, the message contains whatever "solutionVector" contains.

Best,
Andreas

Lukas Preusser

unread,
Feb 28, 2021, 3:00:56 PM2/28/21
to HeuristicLab
Hi Andreas, 
thanks for your reply! I did some testing and the error seems to be somewhere in my repair code. I have to do some investigating tomorrow.

I have another issue regarding the programmable problem. 
So I created two problems. One is the programmable problem (single objective) with external evaluation (the one we talked about in the upper posts) and the other one is a "regular" External Evaluation Problem (single-objective). I assume both, with the same Integer bounds and Integer length and without any feasibility checks or repair functions, should now work the same using a genetic algorithm with the same algorithm parameters. In Anylogic I set the number of iterations for the experiment and with the ""regular" External Evaluation Problem (single-objective) everything works just fine. But the programmable problem with external evaluation shows some weird behavior. Somehow HeuristicLab shows a lot more evaluated solutions than Anylogic actually evaluated.
I noticed two cases: 
1) HeuristicLab shows thoughout the optimization run more evaluated solutions than Anylogic, until the the predefined amount of iterations (from Anylogic) is reached (e.g. Anylogic shows 300, HL shows 1360 iterations).
EvaluatedSolutions1.PNG

2) HeuristicLab shows thoughout the optimization run more evaluated solutions than Anylogic, but the predefined amount of iterations in Anylogic is never reached, Anylogic stops at some random time and HL skyrockets though iterations, until the set maximum is reached (e.g Anylogic shows 122, HL shows 90100).

EvaluatedSolutions2.PNG

I tried to play around with the parameters of the algorithm, especially population size and max generations, to adjust that to the predefined iterations from Anylogic, but nothing is helping.  Using the "regular" External Evaluation Problem (single-objective), the algorithm just stops at the number predefined iterations from Anylogic and works fine. So I guess it's some problem in the code of the programmable program.

Below you will find the code of the programmable program with external evaluation. It's close to the code of https://dev.heuristiclab.com/trac.fcgi/blog/programmable-external-evaluation with some adjustments to the bounds and the integer length as discussed prior. 
Any help would be great!
All the best,
Lukas

using System;
using System.Linq;
using System.Collections.Generic;
using HeuristicLab.Collections;
using System.Threading;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.BinaryVectorEncoding;
using HeuristicLab.Encodings.IntegerVectorEncoding;
using HeuristicLab.Encodings.RealVectorEncoding;
using HeuristicLab.Encodings.PermutationEncoding;
using HeuristicLab.Encodings.LinearLinkageEncoding;
using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
using HeuristicLab.Optimization;
using Google.ProtocolBuffers;
using HeuristicLab.Problems.Programmable;
using HeuristicLab.Problems.ExternalEvaluation;


namespace HeuristicLab.Problems.Programmable {
  public class ExternalModulareMontageZweiterVersuch : CompiledProblemDefinition, ISingleObjectiveProblemDefinition {
    public bool Maximization { get { return true; } }


    public override void Initialize() {

      
      var IntegerBounds = (IntMatrix)vars.IntegerBounds;
      var IntegerLength = ((IntValue)vars.IntegerLength).Value;
      
     
      
        var enc = new IntegerVectorEncoding("i", length: IntegerLength);
        enc.Bounds = IntegerBounds;
        Encoding = enc;

     
      vars.cache = new EvaluationCache();
      var clients = new CheckedItemCollection<IEvaluationServiceClient>();
      var client = new EvaluationServiceClient();
      client.ChannelParameter.Value = new EvaluationTCPChannel("127.0.0.1", 2112);
      clients.Add(client);
      vars.clients = clients;
      
      vars.messageBuilder = new SolutionMessageBuilder();
     
      // Add additional initialization code e.g. private variables that you need for evaluating

    }

    public double Evaluate(Individual individual, IRandom random) {
     
      var solutionMessage = BuildSolutionMessage(individual);
      
      var cache = (EvaluationCache)vars.cache;
      return (cache == null
        ? EvaluateOnNextAvailableClient(solutionMessage)
        : cache.GetValue(solutionMessage, EvaluateOnNextAvailableClient,
            GetQualityMessageExtensions()))
        .GetExtension(SingleObjectiveQualityMessage.QualityMessage_).Quality;


      // Use vars.yourVariable to access variables in the variable store i.e. yourVariable
      // var quality = 0.0;

      //quality = individual.RealVector("r").Sum(x => x * x);
      // return quality;
    }

     private HashSet<IEvaluationServiceClient> activeClients =
     new HashSet<IEvaluationServiceClient>();
     private object clientLock = new object();
    
     public virtual ExtensionRegistry GetQualityMessageExtensions() {
     var extensions = ExtensionRegistry.CreateInstance();
     extensions.Add(SingleObjectiveQualityMessage.QualityMessage_);
     return extensions;
     
     }
     
     private QualityMessage EvaluateOnNextAvailableClient(SolutionMessage message) {
      var clients = (CheckedItemCollection<IEvaluationServiceClient>)vars.clients;
      IEvaluationServiceClient client = null;
      lock (clientLock) {
        client = clients.CheckedItems.FirstOrDefault(c => !activeClients.Contains(c));
        while (client == null && clients.CheckedItems.Any()) {
          Monitor.Wait(clientLock);
          client = clients.CheckedItems.FirstOrDefault(c => !activeClients.Contains(c));
        }
        if (client != null)
          activeClients.Add(client);
      }
      try {
        return client.Evaluate(message, GetQualityMessageExtensions());
      } finally {
        lock (clientLock) {
          activeClients.Remove(client);
          Monitor.PulseAll(clientLock);
        }
      }
    }
     
     
      private SolutionMessage BuildSolutionMessage(Individual individual,
        int solutionId = 0) {
      var messageBuilder = (SolutionMessageBuilder)vars.messageBuilder;
      lock (clientLock) {
        SolutionMessage.Builder protobufBuilder = SolutionMessage.CreateBuilder();
        protobufBuilder.SolutionId = solutionId;
        foreach (var variable in individual.Values) {
          try {
            messageBuilder.AddToMessage(variable.Value, variable.Key, protobufBuilder);
          }
          catch (ArgumentException ex) {
            throw new InvalidOperationException(
string.Format(@"ERROR while building solution message:
Parameter {0} cannot be added to the message", variable.Key), ex);
          }
        }
        return protobufBuilder.Build();
      }
    }
     
    
    
    
    public void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) {

      // Use vars.yourVariable to access variables in the variable store i.e. yourVariable
      // Write or update results given the range of vectors and resulting qualities
      // Uncomment the following lines if you want to retrieve the best individual

      //var orderedIndividuals = individuals.Zip(qualities, (i, q) => new { Individual = i, Quality = q }).OrderBy(z => z.Quality);
      //var best = Maximization ? orderedIndividuals.Last().Individual : orderedIndividuals.First().Individual;

      //if (!results.ContainsKey("Best Solution")) {
      //  results.Add(new Result("Best Solution", typeof(RealVector)));
      //}
      //results["Best Solution"].Value = (IItem)best.RealVector("r").Clone();
    }

    public IEnumerable<Individual> GetNeighbors(Individual individual, IRandom random) {

      // Use vars.yourVariable to access variables in the variable store i.e. yourVariable
      // Create new vectors, based on the given one that represent small changes
      // This method is only called from move-based algorithms (Local Search, Simulated Annealing, etc.)
    //  while (true) {
        // Algorithm will draw only a finite amount of samples
        // Change to a for-loop to return a concrete amount of neighbors
    //    var neighbor = individual.Copy();
        // For instance, perform a single bit-flip in a binary parameter
        //var bIndex = random.Next(neighbor.BinaryVector("b").Length);
        //neighbor.BinaryVector("b")[bIndex] = !neighbor.BinaryVector("b")[bIndex];
    //    yield return neighbor;
    
     yield break;
    
      }
    }

    // Implement further classes and methods
  }

HeuristicLab

unread,
Feb 28, 2021, 7:14:47 PM2/28/21
to HeuristicLab
I suppose the effect that you are seeing is due to the use of a cache. Potentially the algorithm converges prematurely and does not produce a lot of different solutions after some time. Any solution found already will hit the cache and not be transmitted to AnyLogic. That's why you see different numbers. If you comment the line "vars.cache = new EvaluationCache();" in Initialize() and remove the corresponding cache variable in the Variable Store then you'll have all solutions being sent to AnyLogic.

Best,
Andreas

Lukas Preusser

unread,
Mar 1, 2021, 4:22:53 AM3/1/21
to HeuristicLab
When I comment the line "vars.cache = new EvaluationCache();" and remove the cache variable, the cache variable is also missing for the Evaluate method, so consequently I get an error message that a defintion for cache is missing. Then I tried to comment var cache = (EvaluationCache)vars.cache; and the following lines as well, but then Evaluate doesn't return any values. Tried using "var quality = 0.0;  return quality;" in Evaluate but that doesn't work.
When I just comment  "vars.cache = new EvaluationCache();" without removing the cache variable, Anylogic performs the intended number of runs, but HL still shows a lot more evaluated solutions.

All the best,
Lukas

public double Evaluate(Individual individual, IRandom random) {
     
      var solutionMessage = BuildSolutionMessage(individual);

      var cache = (EvaluationCache)vars.cache;
      return (cache == null
        ? EvaluateOnNextAvailableClient(solutionMessage)
        : cache.GetValue(solutionMessage, EvaluateOnNextAvailableClient,
            GetQualityMessageExtensions()))
        .GetExtension(SingleObjectiveQualityMessage.QualityMessage_).Quality;
    

HeuristicLab

unread,
Mar 1, 2021, 4:45:55 AM3/1/21
to HeuristicLab
Like this: Then all solutions should be sent to AnyLogic.

public double Evaluate(Individual individual, IRandom random) {
     
      var solutionMessage = BuildSolutionMessage(individual);

      //var cache = (EvaluationCache)vars.cache;
      EvaluationCache cache = null;

Lukas Preusser

unread,
Mar 1, 2021, 8:44:45 AM3/1/21
to HeuristicLab
Makes sense and it's working!
Thanks a lot Andreas!

Lukas Preusser

unread,
Mar 6, 2021, 9:57:01 AM3/6/21
to HeuristicLab
Hi Andreas, 
I'm trying to convert the programmable single objective external evaluation problem into a programmable multi objective problem. I hoped that if I change the "Single" statement into "Multi" inside the code in the Evaluate method (see my code examples below) it would work, but apparently something is missing as I get an error message, that there is no definition for 'Quality' in MultiObjectiveQualityMessage.QualityMessage and no extension method 'Quality'. I know that the datatype of quality is DoubleArray, do I have to add this somewhere in the code? Any tips would be helpful :)

All the best,
Lukas

return (cache == null
        ? EvaluateOnNextAvailableClient(solutionMessage)
        : cache.GetValue(solutionMessage, EvaluateOnNextAvailableClient,
            GetQualityMessageExtensions()))
        .GetExtension(MultiObjectiveQualityMessage.QualityMessage_).Quality;

//.......

public virtual ExtensionRegistry GetQualityMessageExtensions() {
     var extensions = ExtensionRegistry.CreateInstance();
     extensions.Add(MultiObjectiveQualityMessage.QualityMessage_);
     return extensions;   

Andreas Beham

unread,
Mar 6, 2021, 9:59:38 PM3/6/21
to HeuristicLab
You can check out trunk\HeuristicLab.Problems.ExternalEvaluation\3.4\Protos to see that it's called qualities instead of quality for multi-objective.
Btw, you also need to take all that code to a multi-objective programmable problem. Just changing the code inside a single-objective programmable problem to multi-objective won't give you a multi-objective problem.

Lukas Preusser

unread,
Mar 8, 2021, 9:13:33 AM3/8/21
to HeuristicLab
Thanks for the tip with trunk\HeuristicLab.Problems.ExternalEvaluation\3.4\Protos !!

I'm aware of using a multi-objective programmable problem. I've been trying to get the statement ".GetExtension(MultiObjectiveQualityMessage.QualityMessage_)....;" to compile, but with ".qualities;" or ".QualitiesList;" I cannot get it to work. I'm getting the following error using ".QualitiesList;".

Error QualitiesList.PNG

Do I have to convert the list to a double [] or do I have to use a different statement in ".GetExtension(MultiObjectiveQualityMessage.QualityMessage_)....;"?

Andreas Beham

unread,
Mar 8, 2021, 9:18:24 AM3/8/21
to HeuristicLab
Ah okay, didn't notice that the protobuf compiler renames this property. Doesn't matter, you already found it!
The solution is simple: just add a .ToArray() call after QualitiesList:
.GetExtension(...).QualitiesList.ToArray();

That will turn the IList<double> into a double[] as is demanded in the error message.

Andreas

Lukas Preusser

unread,
Mar 9, 2021, 4:11:06 PM3/9/21
to HeuristicLab
Thanks Andreas!
Is there any smart way to debug the code from the programmable problem? E.g. I declared an int value in the Evaluate method "int id = 0;" and want to print it for debugging "Console.WriteLine(id);". I tried using the Debug Engine, but that only shows the algorithm paramters, not the console output of the programmable problem.

Lukas

HeuristicLab

unread,
Mar 9, 2021, 4:46:28 PM3/9/21
to HeuristicLab
I think you should be able to add "System.Diagnostics.Debugger.Break()" call to your programmable problem where you want to have the breakpoint. Then compile and copy all code to the clipboard (ctrl+a, ctrl+c) - you'll need it later. Then execute the algorithm and upon reaching the code a dialog should pop up how you want to debug it. You have to select a visual studio (VS) instance (you've got visual studio installed?) and then comes the tricky part, VS asks you for the location of the source file - but of course there is none. So take a text editor, paste the code that you copied to the clipboard before and save it under the filename that visual studio asked you... something like sdkx24292tx.cs or so. Navigate in VS to that file you just created and voilà you should be able to step through your code in the debugger.
It's not really a smart way, but it should work and I had done this myself successfully before.

HeuristicLab also writes a HeuristicLab.log. There's a HeuristicLab.Tracing.Logger class that you should be able to achieve Console.WriteLine-like debugging with.

Otherwise, it's a bit difficult. The other UI-equivalent to Console.WriteLine is something like MessageBox.Show, but then you should definitely also add a System.Threading.Thread.Sleep(1000) - or higher - call next to it, otherwise you'll have a hard time stopping the algorithm, because constantly a message box blocks the UI.


Andreas

Lukas Preusser

unread,
Mar 9, 2021, 4:48:03 PM3/9/21
to HeuristicLab
As a workaround I could write id into my solution vector and observe its value in Anylogic or in the Debug Engine, but I guess there's probably a more sophisticated way to tackle this...

Lukas Preusser

unread,
Mar 9, 2021, 4:50:37 PM3/9/21
to HeuristicLab
I haven't seen your answer before I wrote about my workaround. I will try your suggestions, thanks a lot!

Lukas Preusser

unread,
Mar 10, 2021, 8:42:36 AM3/10/21
to HeuristicLab
Adding System.Diagnostics.Debugger.Break() and compiling works. But when I execute the algorithm no dialog window is popping up and the algorithm doesn't stop. But I noticed that something in the backround is working, as the windows loading cursor appears. That does not happen when I execute the algorithm without System.Diagnostics.Debugger.Break(). But nothing happens. I downloaded and installed VS, do I need some specific workload to get the debugging to work? I also tried  System.Diagnostics.Debugger.Launch(), but this doesn't help either.

Lukas Preusser

unread,
Mar 10, 2021, 9:07:42 AM3/10/21
to HeuristicLab
 I made also sure to put "System.Diagnostics.Debugger.Break()" at a place, where it is definately called.

All the best,
Lukas

HeuristicLab

unread,
Mar 11, 2021, 4:09:19 AM3/11/21
to HeuristicLab
I have talked to my colleagues who have tried some ways of debugging. One reported that using Debugger.Launch() did bring up the option to attach a debugger for him, so I am not sure why this didn't work for you. It should work according to the docs. The other reported that you could try attaching the Visual Studio debugger to HeuristicLab before you execute the algorithm (Debug > Attach to process) and then Debugger.Break() should work.
I posted this video before on working with debugger in visual studio: https://www.youtube.com/watch?v=juPCqN-Rb1E&t=123s
It may be helpful to you as well.

Otherwise, you could still try the "poor-man's solution" using the HeuristicLab.Tracing.Logger class.

Lukas Preusser

unread,
Mar 15, 2021, 11:45:52 AM3/15/21
to HeuristicLab
Hi Andreas,
thanks to you and your colleagues! The problem was my installation of VS, the .NET development workload was missing. Now Debugger.Launch() is working like a charm and the debugging is really helpful!

All the best!

Andreas Beham

unread,
Mar 15, 2021, 11:56:09 AM3/15/21
to HeuristicLab
Good to hear that it works. This thread is quite long already, let's close it at this point and I suggest you'll open a new one should further issues arise.

Andreas
Reply all
Reply to author
Forward
0 new messages