measures

Measures

A novelty in jMetal 5.0 is the inclusion of measures, which allows to obtain algorithm-specific information during its execution. The current implementation supports two types of measures: a PullMeasure provides the value of the measure on demand (synchronous), while a PushMeasure allows to register listeners (or observers) to receive the value of the measure when it is produced (asynchronous).
jMetal 5.0 的一个新颖之处在于包含了一些措施,它允许在其执行期间获得特定于算法的信息。
当前的实现支持两种类型的度量:“Pull Measure”按需提供度量值(同步),而“Push Measure”允许注册侦听器(或观察者)以接收度量值。
产生(异步)。

What are measures?

Measures are designed to access specific properties of a running algorithm. For instance, one could know the size of the current population in a genetic algorithm, the current velocity of the particles in a particle swarm optimization, the current iteration, etc. In order to deal properly with the many properties that an algorithm could have, two types of measures are provided.

Usually, properties are accessed by getters, like getPopulationSize() or getCurrentIteration(). These properties are accessed in a synchronous way, which means that we obtain (and process) them on the user’s demand. Each property of this kind can be accessed through a PullMeasure, as shows the PullMeasure.get() method:
措施旨在访问正在运行的算法的特定属性。
例如,可以知道遗传算法中当前种群的大小、粒子群优化中粒子的当前速度、当前迭代等。为了正确处理算法可能具有的许多属性,
提供了两种措施。
通常,属性由 getter 访问,例如 get Population Size()get Current Iteration()
这些属性以同步方式访问,这意味着我们根据用户的需求获取(和处理)它们。
这种类型的每个属性都可以通过“Pull Measure”访问,如“Pull Measure.get()”方法所示:

public interface PullMeasure<Value> extends Measure<Value> {
	public Value get();
}

At the opposite, a PushMeasure allows to obtain the value of a property in an asynchronous way, which means that the value is provided upon generation, the user having no control on when such a value will be provided to him. This is achieved by using an observer design pattern, such that the user registers one or several MeasureListener instances to receive and process the value when it is produced:
相反,“Push Measure”允许以异步方式获取属性的值,这意味着该值是在生成时提供的,用户无法控制何时将此类值提供给他。
这是通过使用 观察者设计模式 来实现的,这样用户注册一个或多个“测量监听器”实例以在生成值时接收和处理值

public interface PushMeasure<Value> extends Measure<Value> {
	public void register(MeasureListener<Value> listener);
	public void unregister(MeasureListener<Value> listener);
}

public interface MeasureListener<Value> {
	public void measureGenerated(Value value);
}

Some people could wonder why these interfaces only require reading methods: the PullMeasure does not provide a set(value) method to assign its value, and the PushMeasure does not provide some kind of push(value) method neither. This is because these interfaces are designed from a user perspective, not an algorithm designer perspective, and the user can only read these values, not write them. One could say that it makes the thing harder for the designer, but actually the designer needs to know exactly which implementation to use, because he is the one who know how the values will be provided. So if he uses an existing implementation, he should of course know the specific methods of this implementation, which can include methods to set/push his values. Only the user does not need to know about the specific implementation used by the measure, so he is the one who will have to deal with generic PullMeasure and PushMeasure. Moreover, while it is natural to think about these set and push methods, it is actually not the only way to implement these measures. For instance, the algorithm designer could use a field to store the value of a property, and the update of this property (and the related PullMeasure) will be done through this variable rather than through the call of some methods. More detailed explanations are provided in the section How to create measures?.
有些人可能想知道为什么这些接口只需要读取方法:“Pull-Measure”没有提供“set(value)”方法来分配其值,“Push-Measure”也没有提供某种“Push(value)”方法。这是因为这些界面是从用户角度设计的,而不是从算法设计器角度设计的,用户只能读取而不能写入这些值。有人可能会说这让设计人员的工作更加困难,但实际上,设计人员需要确切地知道要使用哪个实现,因为他知道如何提供值。因此,如果他使用现有的实现,他当然应该知道该实现的具体方法,其中可以包括设置/推送其值的方法。只有用户不需要知道度量所使用的具体实现,因此他必须处理通用的“Pull-measure”和“Push-measure”。此外,虽然考虑这些set和push方法是很自然的,但实际上这并不是实现这些措施的唯一途径。例如,算法设计者可以使用字段来存储属性的值,并且该属性(以及相关的“Pull-Measure”)的更新将通过该变量而不是通过调用某些方法来完成。更详细的解释见[如何创建度量?](#如何创建度量)一节。

One could notice that both the measure interfaces extend Measure. This one is an empty interface which is used as a root interface to both PullMeasure and PushMeasure. By having it, we give the possibility for programers to manage measures in a generic way, without having to consider both types of measures separately nor to use Object to have them together. Thus, it is only here to simplify the use of measures for some highly generic cases in jMetal. No algorithm designer nor user should normally need it.
可以注意到,这两个度量接口都扩展了“Measure”。
这是一个空接口,用作“Pull Measure”和“Push Measure”的根接口。
通过拥有它,我们为程序员提供了以通用方式管理度量的可能性,而不必分别考虑两种类型的度量,也无需使用“对象”将它们组合在一起。
因此,这里只是为了简化 jMetal 中一些高度通用的情况下的度量使用。
算法设计者和用户通常都不应该需要它。
Additionally to the measures themselves, jMetal 5 also provides the notion of MeasureManager, which deals with measures management features:除了度量本身,jMetal 5 还提供了“度量管理器”的概念,它处理度量管理功能:

public interface MeasureManager {
	public Collection<Object> getMeasureKeys();
	public <T> PullMeasure<T> getPullMeasure(Object key);
	public <T> PushMeasure<T> getPushMeasure(Object key);
}

You can notice that we provide a method for each type rather than a generic method returning a Measure. This is because, as mentionned before, the Measure interface is empty, thus has no practical value. Moreover, when the user want to exploit a measure, he should know which type of measure is implemented to know how to interact with it. Rather than providing a Measure and arbitrarily cast it, we prefered to provide methods which directly provide the right type of measure. Notice that a measure instance can implement both interfaces (or two instances can be made available for the same property), so both methods can return an instance (same or not) for the same key, giving to the user the choice of using one way or another. In the case where only one is provided (the other being null), it is possible to complete it, as described in the section dedicated to conversions. The key identifies the specific property to measure and is algorithm-dependent, justifying the presence of an additional getMeasureKeys() for the user to retrieve them.

Finally, in order for an algorithm to explicit that it provides measures, it should implement the Measurable interface, which simply requires to provide a MeasureManager:
您可以注意到,我们为每个类型提供了一个方法,而不是返回“Measure”的泛型方法。这是因为,如前所述,“Measure”接口是空的,因此没有实际价值。此外,当用户想要利用度量值时,他应该知道实现了哪种类型的度量值,以了解如何与之交互。我们宁愿提供直接提供正确类型的度量的方法,而不是提供“度量”并任意转换它。请注意,一个度量实例可以实现两个接口(或者可以为同一属性提供两个实例),因此两个方法都可以为同一个键返回一个实例(相同或不相同),使用户可以选择使用一种方式或另一种方式。在只提供一个(另一个为“null”)的情况下,可以完成它,如[专用于转换的部分](#conversions pullmeasure–pushmeasure)中所述。该键标识要测量的特定属性,并且依赖于算法,证明存在额外的“get measure Keys()”供用户检索。

最后,为了使算法明确其提供了度量,它应该实现“可测量”接口,该接口只需要提供一个“度量管理器”:

public interface Measurable {
	public MeasureManager getMeasureManager();
}

So far, an Algorithm does not automatically implements Measurable, but it may change in future releases if we assess the relevance of the interface for a generic purpose.
到目前为止,“算法”并不会自动实现“可测量的”,但如果我们出于通用目的评估接口的相关性,它可能会在未来的版本中发生变化。
One could wonder why we introduced the intermediary concept of MeasureManager rather than putting directly its methods into the Measurable interface. Indeed, by construction, the designer could simply decide to implement both the interfaces on the algorithm and implement getMeasureManager() by simply returning this, showing that we can artificially ignore the intermediary concept. Also, in the case where we actually reduce to the Measurable interface, the designer could add an intermediary objects which implements Measurable (so the methods of MeasureManager), and implement it also for the algorithm, but using his custom object in background. So technically, we see that introducing an intermediary notion of MeasureManager does not impose any constraint, justifying that we use the most simple design (no intermediary concept). Anyway, we made this design choice for the following reason: while the MeasureManager should focus on the measure management strategy, a Measurable should focus on which measures to provide. For instance, a MeasureManager could implement an advanced management strategy which, for measures implementing only one of the measure interfaces, would automatically instantiate a PullMeasure/PushMeasure in order to have all the features for each measure, with some smart inspection management for PushMeasures corresponding to PullMeasures. On the other hand, a Measurable instance, typically an algorithm, would focus on choosing which measures to provide and feeding them, delegating any measure management process to a dedicated MeasureManager implementation.
人们可能想知道,为什么我们引入了“度量管理器”的中介概念,而不是将其方法直接放入“可衡量”界面。实际上,通过构造,设计者可以简单地决定在算法上实现两个接口,并通过简单地返回“this”来实现“get Measure Manager()”,这表明我们可以人为地忽略中介概念。此外,在我们实际简化为“可测量”接口的情况下,设计者可以添加一个实现“可测量”的中间对象(也就是“测量管理器”的方法),并为算法实现它,但在后台使用他的自定义对象。因此,从技术上讲,我们看到引入“度量管理器”的中间概念并没有施加任何约束,这证明我们使用了最简单的设计(没有中间概念)。无论如何,我们之所以选择这种设计,是因为以下原因:“度量管理器”应该关注度量管理策略,而“可衡量的”应该关注提供哪些度量。例如,“度量管理器”可以实施高级管理策略,对于仅实现其中一个度量接口的度量,该策略将自动实例化“拉动度量”/“推动度量”,以便具有每个度量的所有功能,并对与“拉动度量”相对应的“推动度量”进行一些智能检查管理。另一方面,“可衡量的”实例,通常是一种算法,将重点放在选择提供哪些措施并为其提供信息,将任何措施管理过程委托给专门的“措施管理器”实施。

Why using measures?

Usually, properties are accessed by getters, like getPopulationSize() or getCurrentIteration(). The advantage of this design is that it gives a simple way to access these properties. However, we quickly face the limitations of this design when we want to read properties which evolve in time, like the current iteration. Indeed, such a design imposes to read these properties on a regular basis, sometime on a frequent basis to see all the updates (also called polling, spinning, or busy-waiting). Such a design becomes particularly cumbersome when we want to access short-term values, like which individuals have been generated at a given iteration of a genetic algorithm. These individuals are usually forgotten at the next iteration (only the good ones are kept), thus imposing a frequent check. Accessing such properties at runtime would require whether to consume a lot of computation time to continuously inspect some getGeneratedIndividuals() method, or to consume a lot of space to store all the data generated and access it through some getGeneratedIndividuals(int iteration) method.
通常,属性由getter访问,如“get Population Size()”或“get Current Iteration()。这种设计的优点是,它提供了一种访问这些属性的简单方法。然而,当我们想要读取随时间变化的属性(如当前迭代)时,我们很快就会面临这种设计的局限性。事实上,这样的设计要求定期读取这些属性,有时需要频繁读取以查看所有更新(也称为*轮询*、*旋转*、或*繁忙等待*)。当我们想要获取短期值时,这种设计变得特别麻烦,例如在遗传算法的给定迭代中生成的个体。这些个体通常在下一次迭代中被遗忘(只保留好的个体),因此需要经常检查。在运行时访问这些属性需要消耗大量计算时间来连续检查某些“get-Generated Indications()”方法,还是消耗大量空间来存储生成的所有数据并通过一些“get-Generated Indications(int迭代)”方法访问。 The design we choose for jMetal 5 is based on the notion of measure, which further split into two categories: PullMeasureandPushMeasure. The simplest one, the PullMeasure, is designed for the first kind of property, which can be easily accessed (pulled) from a getter. While one could simply use a getter, the PullMeasureprovides a more generic access to the property, allowing to apply generic evaluations on the algorithm (like generic experiments, which are not yet available in jMetal 5.0). The other type of measure, thePushMeasure`, is designed for the other kind of properties, which are not easily managed through getters and need to be provided (pushed) in real-time by the algorithm. By using such a measure, the values can be received and processed without risking to loose any update, and without the need to continuously inspect the property.

Another advantage of having both these measures is that it can be easily integrated to an algorithm without requiring significant additional resources: indeed, for cases where a value have to be stored into a variable for the algorithm to use it, like an iteration counter to stop after N iterations, such a value can be covered (or replaced) by a PullMeasure. At the opposite, when a value is generated but not stored during the running of the algorithm, a PushMeasure can be used to push the value to any potential listener and forget about it, letting the listeners decide whether or not this value should be stored or further processed. The additional resources consumed are only:

  • the stored measures, which are often lightweight and less numerous than the solutions to store
  • the calls to the listeners of the PushMeasures, which are negligible if no listener is registered, otherwise fully controlled by the user (not by a priori decisions from the algorithm designer)
    拥有这两种度量的另一个优点是,它可以轻松地集成到算法中,而不需要大量额外资源:事实上,对于必须将值存储到变量中以供算法使用的情况,例如迭代计数器在N次迭代后停止,这样的值可以被“拉度量”覆盖(或替换)。相反,当一个值在算法运行期间生成但未存储时,可以使用“推送度量”将该值推送到任何潜在的侦听器,并将其忘掉,让侦听器决定是否应存储或进一步处理该值。消耗的额外资源仅为:

-存储的度量,通常比存储的解决方案更轻,数量更少

-对“Push Measures”侦听器的调用,如果没有注册侦听器,则可以忽略不计,否则完全由用户控制(而不是由算法设计者的先验决定)

How to use measures?

Measures have been defined to be particularly easy to use. On one hand, a PullMeasure can be used basically like a getter, where instead of calling getXxx() we call xxxMeasure.get(). This results in using a PullMeasure directly when required by the user. On the other hand, because a PushMeasure only allows a user to register and unregister listeners (the notification generation is the responsibility of the algorithm), the user have no control on when the processing will occur: the process have to be put in the listener to exploit the data when it arrives, or the listener should store the value while letting another thread dealing with the processing. It is generally recommended to reduce the time spent in a listener to the minimum in order to let the source of the notification (the algorithm here) continues its job.

In other words, a process using PullMeasures should generally have this form, where the measures are used once the algorithm is running:
措施已被定义为特别易于使用。
一方面,Pull Measure 基本上可以像 getter 一样使用,我们调用 xxx Measure.get() 代替调用 get Xxx()
这导致在用户需要时直接使用“Pull Measure”。
另一方面,因为 Push Measure 只允许用户注册和取消注册监听器(通知生成是算法的责任),用户无法控制处理何时发生:必须放置进程
在侦听器中利用数据到达时,或者侦听器应该存储值,同时让另一个线程处理处理。
通常建议将在侦听器中花费的时间减少到最低限度,以便让通知源(此处的算法)继续其工作。
换句话说,使用“拉取措施”的流程通常应该具有这种形式,一旦算法运行,就会使用这些措施:

Algorithm<?> algorithm = new MyAlgorithm();

/* retrieval of the measures */
/* (only if the algorithm implements Measurable) */
MeasureManager measures = algorithm.getMeasureManager();
PullMeasure<Object> pullMeasure = measures.getPullMeasure(key);
...

/* preparation of the run */
...
/* run the algorithm in parallel */
Thread thread = new Thread(algorithm);
thread.start();

/* user process */
while (thread.isAlive()) {
	...
	/* use the value */
	Object value = pullMeasure.get();
	...
}

At the opposite, a process using PushMeasures should generally have this form, where the process is setup before to run the algorithm:
相反,使用“推送措施”的流程通常应该具有这种形式,其中流程在运行算法之前设置:

Algorithm<?> algorithm = new MyAlgorithm();

/* retrieval of the measures */
/* (only if the algorithm implements Measurable) */
MeasureManager measures = algorithm.getMeasureManager();
PushMeasure<Object> pushMeasure = measures.getPushMeasure(key);
pushMeasure.register(new MeasureListener<Object>() {
	
	@Override
	public void measureGenerated(Object value) {
		/* use the value */
		...
	}
});
...

/* preparation of the run */
...
/* run the algorithm in parallel */
Thread thread = new Thread(algorithm);
thread.start();

/* other processes */
while (thread.isAlive()) {
	...
}

In the case where only PushMeasures are used, one could also remove all the Thread management and simply call algorithm.run() and wait for it to finish. All the processes setup via PushMeasures will occur automatically.
在只使用 Pushmeasure 的情况下,也可以移除所有的 Thread 管理,只需调用 algorithm.run() 并等待它完成。
通过“推送措施”设置的所有流程都将自动发生。

How to create measures?

Create a PullMeasure

Usually, one implements an algorithm by storing some values in dedicated public fields, so a user can retrieve them on the fly. A really common example is a population of solutions, that we can find in many algorithms managing several solutions at the same time, like a genetic algorithm. Some others prefer to use getters, like getPopulation(), to provide a read-only access (while the field can be changed by the external user). These fields and getters are the best candidates for PullMeasures, because they can be wrapped in a straightforward manner. For our population example:
通常,通过将一些值存储在专用的公共字段中来实现一种算法,以便用户可以即时检索它们。
一个非常常见的例子是一组解决方案,我们可以在许多同时管理多个解决方案的算法中找到它,比如遗传算法。
其他一些人更喜欢使用 getter,例如 get Population(),来提供只读访问权限(而该字段可以由外部用户更改)。
这些字段和 getter 是“拉度量”的最佳候选者,因为它们可以以直接的方式包装。
对于我们的人口示例:

PullMeasure<Collection<Path>> populationMeasure = new PullMeasure<Collection<Path>>() {
	
	@Override
	public String getName() {
		return "population";
	}
	
	@Override
	public String getDescription() {
		return "The set of paths used so far.";
	}
	
	@Override
	public Collection<Path> get() {
		return getPopulation();
	}
};

The name and description are additional data to describe the measure itself, to know what it provides, and any measure should implement it, which makes the implementation of the interface quite heavy. However, this code can be reduced by using SimplePullMeasure, which takes the name and description in argu名称和描述是描述度量本身的附加数据,知道它提供了什么,任何度量都应该实现它,这使得接口的实现相当繁重。
但是,可以通过使用“Simple Pull Measure”来减少此代码,它采用参数中的名称和描述来关注要检索的值:ment to focus on the value to retrieve:

PullMeasure<Collection<Path>> populationMeasure
= new SimplePullMeasure<Collection<Path>>("population",
                                          "The set of paths used so far.") {
	@Override
	public Collection<Path> get() {
		return getPopulation();
	}
};

If no getter is available, a field can also be used exactly the same way. These cases are the most basic uses of a PullMeasure. They are also the most common if you adapt an existing implementation to use the jMetal formalism. Indeed, using fields and getters is the simple way to provide an access to the internal data of an algorithm, which gives a lot of occasions to use PullMeasures. But these cases are not the only ones. Indeed, any computation can be defined in the get() method, which allows to add new measures even for inexistent fields and getters. For instance, assuming that the algorithm manages the time spent in some of its steps, it could store an internal startTime value which indicates when the algorithm have been started. From this variable, one could measure how long the algorithm has run:
如果没有可用的 getter,也可以以完全相同的方式使用字段。
这些情况是 Pull Measure 的最基本用途。
如果您调整现有实现以使用 jMetal 形式,它们也是最常见的。
实际上,使用字段和 getter 是提供对算法内部数据的访问的简单方法,这为使用“拉度量”提供了很多场合。
但这些案例并不是唯一的。
事实上,任何计算都可以在 get() 方法中定义,即使是不存在的字段和 getter,也可以添加新的度量。
例如,假设算法管理其某些步骤所花费的时间,它可以存储一个内部的“开始时间”值,该值指示算法何时开始。
从这个变量中,可以测量算法运行了多长时间:

PullMeasure<Long> runningTimeMeasure
= new SimplePullMeasure<Long>("running time",
                              "The time spent so far in running the algorithm.") {
	@Override
	public Long get() {
		return System.currentTimeMillis() - startTime;
	}
};

The advantage of putting the computation directly into the get() method is that the value is computed only when requested. Thus, it is the responsibility of the external user to decide when it is worth to compute it.

So far, we saw that a PullMeasure is basically used in the same way than a getter (we can also use a getter to make some computation). This is actually its principal purpose, but it does it in a standardised way: indeed, given that you have an algorithm which provides you some getters or equivalent, you can wrap all of them into PullMeasures, which gives you a unified way to access these values in a generic context (where you do not know which method to use nor how to use them, unless reflexion is used). Moreover, if you have an algorithm which provides a set of PullMeasures, you can extend them by creating new ones (while you cannot add getters nor fields to an instance):
将计算直接放入 get() 方法的优点是仅在请求时才计算值。
因此,外部用户有责任决定何时值得计算。
到目前为止,我们看到 Pull Measure 的使用方式基本上与 getter 相同(我们也可以使用 getter 进行一些计算)。
这实际上是它的主要目的,但它以一种标准化的方式来做:事实上,如果你有一个算法可以为你提供一些 getter 或等价物,你可以将它们全部包装到 Pull measure 中,这为你提供了一个统一的方式
在通用上下文中访问这些值(除非使用反射,否则您不知道使用哪种方法或如何使用它们)。
此外,如果您有一个提供一组“拉度量”的算法,您可以通过创建新的来扩展它们(虽然您不能向实例添加 getter 或字段):

PullMeasure<Integer> populationSizeMeasure
= new SimplePullMeasure<Integer>("population size",
                                 "The number of solutions used so far.") {
	@Override
	public Integer get() {
		// reuse a measure to compute another property
		return populationMeasure.get().size();
	}
};

With the ability to extend a set of measures, one can create the relevant measures for a specific experiments for instance (not yet implemented in jMetal 5.0). In other words, the algorithm designer can focus on providing a minimal set of measures and let the external user define new ones when required.
借助扩展一组度量的能力,可以为特定实验创建相关度量(例如,尚未在 jMetal 5.0 中实现)。
换句话说,算法设计者可以专注于提供最小的度量集,并让外部用户在需要时定义新的度量。
Finally, additional facilities are provided by jMetal to simplify the creation of PullMeasures. We already mentionned the SimplePullMeasure which focuses on defining the get() method, but in the case where the measure wraps a field, one could prefer to use directly the measure instead of the field itself by using a BasicMeasure, which directly stores the value by defining an additional set(value) method. More specific measures are also defined, like the CountingMeasure which allows to count occurrences, typically like a number of iterations or a number of solutions generated, or the DurationMeasure to evaluate the time spent in some activities. Several implementations of PullMeasure also implement PushMeasure, thus allowing a high flexibility on their use. In the case where one want to add PullMeasures to an existing algorithm, it is also possible to use the MeasureFactory to facilitate the instantiation of several PullMeasures at once:

  • createPullsFromFields(object) instantiates a PullMeasure for each field of an object and returns a Map associating the name of each field to the corresponding measure,
  • createPullsFromGetters(object) instantiates a PullMeasure for each getter in a similar way.
    最后,jMetal还提供了其他设施,以简化“拉动措施”的创建。我们已经提到了“Simple Pull Measure”,它专注于定义“get()”方法,但在度量包装字段的情况下,人们可能更愿意通过使用“基本度量”直接使用度量,而不是字段本身,基本度量通过定义额外的“set(value)”方法直接存储值。还定义了更具体的度量,如允许计算发生次数的“计数度量”,通常如迭代次数或生成的解决方案数量,或用于评估在某些活动中花费的时间的“持续时间度量”。“Pull-Measure”的几个实现也实现了“Push-Measure”,因此在使用上具有很高的灵活性。在希望将“拉度量”添加到现有算法的情况下,也可以使用“度量工厂”来方便同时实例化几个“拉度量”:

-“create Pull From Fields(object)`为对象的每个字段实例化一个“Pull Measure”,并返回一个“Map”,将每个字段的名称与相应的度量值相关联,

-'create Pull From getter(object)‘以类似的方式为每个getter实例化一个’Pull Measure’。

Create a PushMeasure

A PushMeasure standardises the observer design pattern for algorithms. As an observer is supposed to be notified when the value it observes is updated, a PushMeasure notifies a listener (a broadly used term for observers in Java, especially in Swing) when a property of the algorithm changes. Noticeably, the interface PushMeasure is really reduced: it only requires the methods to add and remove listeners. Because of that, it is the responsibility of the measure implementer to decide how the listeners are notified. Several implementations are already provided in jMetal to simplify this work. In particular, probably most of the cases will find their way in using SimplePushMeasure, for instance if we want to notify when a new solution is generated:
Push Measure 使算法的 观察者设计模式 标准化。
由于观察者应该在其观察到的值更新时得到通知,因此当算法的属性发生更改时,“推送测量”会通知侦听器(Java 中广泛使用的观察者术语,尤其是在 Swing 中)。
值得注意的是,Push Measure 接口确实减少了:它只需要添加和删除侦听器的方法。
因此,措施实施者有责任决定如何通知侦听器。
jMetal 中已经提供了几个实现来简化这项工作。
特别是,可能大多数情况下都会使用“Simple Push Measure”,例如,如果我们想在生成新解决方案时发出通知:

SimplePushMeasure<MySolution> lastGeneratedSolution = new SimplePushMeasure<>(
		"last solution",
		"The last solution generated during the running of the algorithm.");

At the opposite of a passive PullMeasure (this is the user who decides when to call it), a PushMeasure is an active measure, in the sense that it is a particular event occuring during the run of the algorithm which triggers the notification process. For our example of last generated solution, we could have a basic hill-climbing method which starts from a random solution and then creates mutants to iteratively improve it. These random and mutant generations are the two relevant events in the algorithm for using our measure:
与被动“Pull Measure”(这是用户决定何时调用它)相反,“Push Measure”是一种主动措施,因为它是在算法运行期间发生的特定事件,
触发通知过程。
对于我们最后生成的解决方案的示例,我们可以有一个基本的爬山方法,它从随机解决方案开始,然后创建突变体以迭代改进它。
这些随机和突变的世代是使用我们的度量的算法中的两个相关事件:

MySolution best = createRandomSolution();
lastGeneratedSolution.push(best);

while (running) {
	MySolution mutant = createMutantSolutionFrom(best);
	lastGeneratedSolution.push(mutant);
	
	// ...
	// remaining of the loop
	// ...
}

This example is illustrative in two ways: first, there can have several events to consider for a single measure, and second, the notification process should be done as soon as the event has occurred. This implies that a PushMeasure is generally deeply involved in the code of the algorithm itself, at the opposite of a PullMeasure which can wrap a field or a getter method, letting the algorithm itself untouched. Moreover, it also means that some extra computation time is consumed each time such a relevant event occurs. Because of that, a PushMeasure is well suited for notifying about a result already computed (because the algorithm need it) rather than to compute extra data for the sole purpose of measuring some properties. For an extra computation, it is wiser to exploit both a PushMeasure, to notify about the already computed stuff which serves as a basis for the extra computation (if any), with an additional PullMeasure, which will make the extra computation only if requested by the user.
该示例从两个方面进行了说明:第一,对于单个度量,可以考虑多个事件;第二,一旦事件发生,就应该完成通知过程。这意味着“推测量”通常深入到算法本身的代码中,与“拉测量”相反,拉测量可以包装字段或getter方法,使算法本身不受影响。此外,这还意味着每次发生此类相关事件时都会消耗一些额外的计算时间。因此,“推式测量”非常适合通知已计算的结果*(因为算法需要它),而不是仅为了测量某些属性而计算额外数据。对于额外计算,更明智的做法是利用“推”度量,通知作为额外计算(如果有)基础的已经计算的内容,以及额外的“拉”度量,只有在用户请求时才会进行额外计算。
Several implementations provided by jMetal already implement the PushMeasure interface. We already saw the SimplePushMeasure, which should be sufficient for many cases, but one can also notice the CountingMeasure, which allows to count the events (the value notified is an integer incremented at each notification). This implementation is for instance well suited for notifying the start/end of an iteration or how many solutions have been generated so far. It also implements the PullMeasure interface, allowing to retrieve the last value notified on demand.
jMetal提供的几个实现已经实现了“Push-Measure”接口。我们已经看到了“简单推送度量”,它对于许多情况应该已经足够了,但我们也可以注意到“计数度量”,它允许对事件进行计数(通知的值是在每次通知时递增的整数)。例如,这种实现非常适合于通知迭代的开始/结束或到目前为止已经生成了多少个解决方案。它还实现了“Pull-Measure”接口,允许检索按需通知的最后一个值。

Conversions PullMeasure <-> PushMeasure

Some measures implement both PullMeasure and PushMeasure, but in the case where a single one is implemented, it is still possible to create another measure to complement it. This can be achieved by using the MeasureFactory, which provides conversions in both directions:

  • createPullFromPush(push, initialValue) creates a PullMeasure from a PushMeasure. Every time the initial PushMeasure notifies its listeners, the PullMeasure is updated and stores the value for future calls of its get(). The initialValue provided to the method tells which value to use before the next notification occurs (typically null or the actual value if it is known).
  • createPushFromPull(pull, period) creates a PushMeasure from a PullMeasure. Because there is no particular event produced by a PullMeasure, we artificially poll it (frequently check its value) at the given period to see when it changes. Identifying a change will result in generating a notification from the created PushMeasure. A short period offers a better reactivity but increases the computation cost, explaining why this method should not be used unless it is necessary. This is also why it is generally preferable, when it is possible to choose, to setup a PushMeasure rather than a PullMeasure: the conversion costs way less in that direction.
    一些措施同时实现了“拉动措施”和“推动措施”,但在实现单个措施的情况下,仍然可以创建另一个措施来补充它。这可以通过使用“测量工厂”实现,该工厂提供两个方向的转换:

-“create Pull From Push(Push,initial Value)”从“Push Measure”创建“Pull Measure”。每次初始的“Push-Measure”通知其侦听器时,“Pull-Measure”都会被更新,并存储值以供将来调用其“get()”。提供给方法的“初始值”告诉在下一个通知发生之前使用哪个值(通常为“null”或实际值,如果已知)。

-'create Push From Pull(Pull,period)‘从’Pull Measure’创建一个’Push Measure’。由于“拉动度量”不会产生特定事件,因此我们在给定的“周期”对其进行人工轮询(经常检查其值),以查看其何时发生变化。识别更改将导致从创建的“推送度量”生成通知。短“周期”提供了更好的反应性,但增加了计算成本,这解释了为什么除非必要,否则不应使用这种方法。这也是为什么在可能的情况下,通常最好设置“推测量”而不是“拉测量”:在这个方向上,转换成本要低得多。

Add measures to an algorithm

In order for an algorithm to provide measures, it should implement the Measurable interface, which asks to provide a MeasureManager. This manager is the one storing and giving access to all the measures of the algorithm. While one can implement his own manager, jMetal already provide the SimpleMeasureManager implementation which provides the following methods:
为了让算法提供度量,它应该实现“Measurable”接口,该接口要求提供一个“度量管理器”。
该管理器是存储和访问算法所有度量的人。
虽然可以实现自己的管理器,但 jMetal 已经提供了 Simple Measure Manager 实现,它提供了以下方法:

public class SimpleMeasureManager implements MeasureManager {

	// Methods to configure the PullMeasures
	public void setPullMeasure(Object key, PullMeasure<?> measure) {...}
	public <T> PullMeasure<T> getPullMeasure(Object key) {...}
	public void removePullMeasure(Object key) {...}
	
	// Methods to configure the PushMeasures
	public void setPushMeasure(Object key, PushMeasure<?> measure) {...}
	public <T> PushMeasure<T> getPushMeasure(Object key) {...}
	public void removePushMeasure(Object key) {...}

	// Methods to configure any measure (auto-recognition of the type)
	public void setMeasure(Object key, Measure<?> measure) {...}
	public void removeMeasure(Object key) {...}
	public void setAllMeasures(Map<? extends Object, ? extends Measure<?>> measures) {...}
	public void removeAllMeasures(Iterable<? extends Object> keys) {...}
	
	// Provide the keys of the configured measures
	public Collection<Object> getMeasureKeys() {...}
}

While there is the basic, type-specific methods, there is also generic methods which automatically recognize the type of the measure provided, and apply the corresponding type-specific methods. Measures which implement both PullMeasure and PushMeasure are also recognized and added both as a PullMeasure and a PushMeasure, so calling getPullMeasure(key) and getPushMeasure(key) with the same key will return the same measure. The generic methods also provide a massive configuration feature by managing several keys and measures at once.
虽然有基本的、特定于类型的方法,但也有通用方法可以自动识别所提供度量的类型,并应用相应的特定于类型的方法。
同时实现“Pull Measure”和“Push Measure”的措施也被识别并添加为“Pull Measure”和“Push Measure”,因此调用“get Pull Measure(key)”和“get Push Measure(key)”
使用相同的key将返回相同的度量。 通用方法还通过一次管理多个键和度量来提供大量配置功能。 It is worth noting that, if an instance of a *non-jMetal* algorithm is provided, one can easily try to retrieve some measures from it by usingMeasureFactory.createPullsFromFields(object)andMeasureFactory.createPullsFromGetters(object). The maps returned by these two methods can then be provided to SimpleMeasureManager.setAllMeasures(measures)to have a fully featured, ready to useMeasureManager, without knowing anything about the actual algorithm. Given that we know how to run it, it is then possible to run it in a dedicated thread, as described in the section [*How to use measures?*](#how-to-use-measures), and to exploit the available measures to obtain some information about the algorithm. However, as underlined in the section [*Conversions PullMeasure<->PushMeasure*](#conversions-pullmeasure---pushmeasure), it is costly to make PushMeasuresfromPullMeasures, while the reverse is quite cheap. Thus, although it can be tempting to simply implement an algorithm independently of jMetal and use this procedure to obtain a set of PullMeasures, someone designing an algorithm with the possibility to use the formalism of jMetal should focus on PushMeasures (or a combination of both) to retrieve more information in a more optimized way. 值得注意的是,如果提供了一个*非jMetal*算法的实例,则可以使用“度量工厂”轻松地从中检索一些度量。从字段(对象)和“测量工厂”创建拉力。从getter(object)。然后,可以将这两种方法返回的映射提供给“Simple Measure Manager”。将所有度量值(Measures)`设置为一个功能齐全、随时可用的“度量值管理器”,而不需要了解任何实际算法。假设我们知道如何运行它,那么就可以在专用线程中运行它,如[如何使用度量?](#如何使用度量)一节所述,并利用可用的度量来获取有关算法的一些信息。然而,正如[转换“Pull-Measure”<->“Push-Measure”](#Conversions-pullmeasure–pushmeasure)一节所强调的那样,从“Pull-Measures”生成“Push-Measures”的成本很高,反之则相当便宜。因此,尽管可以简单地独立于jMetal实现一个算法,并使用此过程来获得一组“拉度量”,但设计可能使用jMetal形式主义的算法的人应该专注于“推度量”(或两者的组合),以更优化的方式检索更多信息。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值