main.io.github.moonlightsuite.moonlight.online.algorithms.Signals Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of moonlight-engine Show documentation
Show all versions of moonlight-engine Show documentation
MoonLight is a light-weight Java-tool for monitoring temporal, spatial and spatio-temporal properties of distributed complex systems, such as Cyber-Physical Systems and Collective Adaptive Systems.
The newest version!
/*
* MoonLight: a light-weight framework for runtime monitoring
* Copyright (C) 2018-2021
*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.moonlightsuite.moonlight.online.algorithms;
import io.github.moonlightsuite.moonlight.core.signal.Sample;
import io.github.moonlightsuite.moonlight.online.signal.*;
import java.util.function.BiPredicate;
/**
* Algorithms for basic signal primitives, precisely
*
* - {@link #refine} for refining a signal given an update
* - {@link #refineChain} for refining a signal
* given a sequence of updates
*
* - {@link #select} for selecting a fragment of a signal
* given some bounds
*
*
*/
public class Signals {
private Signals() {} // hidden constructor
public static boolean refineChain(TimeChain s,
TimeChain updates,
BiPredicate refinable)
{
ChainIterator> utr = updates.chainIterator();
ChainIterator> itr = s.chainIterator();
Sample current = itr.next();
V prevV = current.getValue(); // Default when no previous value exists
if(utr.hasNext()) {
Update u = nextUpdate(utr, updates.getEnd());
while (true) {
if (stillRefining(itr, current, u, refinable, prevV, s.getEnd()))
{
// Save the "next" as the new "current".
prevV = current.getValue();
current = itr.next();
} else if (utr.hasNext()) {
u = nextUpdate(utr, updates.getEnd());
current = itr.previous();
if (itr.hasPrevious())
current = itr.previous();
prevV = itr.tryPeekPrevious(current).getValue();
} else
break;
}
}
return itr.noEffects();
}
public static boolean refine(TimeChain s,
Update u,
BiPredicate refinable)
{
ChainIterator> itr = s.chainIterator();
Sample current = itr.next();
V prevV = current.getValue(); // Default when no previous value exists
while (stillRefining(itr, current, u, refinable, prevV, s.getEnd()))
{
// Save the "next" as the new "current".
prevV = current.getValue();
current = itr.next();
}
return itr.noEffects();
}
private static Update
nextUpdate(ChainIterator> itr, double end)
{
Sample fst = itr.next();
Double sEnd = itr.hasNext() ? itr.peekNext().getStart() : end;
return new Update<>(fst.getStart(), sEnd, fst.getValue());
}
/**
* Signals initialization
*
* @param itr segment iterator
* @param curr current segment
* @param u update data
* @return true
when the update won't affect the signal anymore,
* false
otherwise.
*/
private static boolean stillRefining(
ChainIterator> itr,
Sample curr,
Update u,
BiPredicate refinable,
V prevV, double lastT)
{
// Current segment unpacking
double t = curr.getStart();
V v = curr.getValue();
double tNext = itr.hasNext() ? itr.peekNext().getStart() : lastT;
// Update unpacking
double from = u.getStart();
double to = u.getEnd();
V vNew = u.getValue();
return !doRefine(itr, from, to, vNew, t, tNext, v, prevV, refinable);
}
/**
* Refinement logic.
* @return true
when the update has been completely processed.
*/
public static boolean doRefine(
ChainIterator> itr,
double from, double to, V vNew, // update
double t, double tNext, V v, // current segment
V prevV, // previous value
BiPredicate refinable)
{
processUpdate(itr, from, to, vNew, t, tNext, v, prevV, refinable);
// General Sub-case - to < tNext:
// This means the current segment contains the end of the
// area to update. Therefore, we must add a segment for the
// last (not to be changed) part of the segment.
// From now on the signal will not change.
if(to < tNext && t != to) {
add(itr, to, v);
return true;
}
if(t == from && itr.hasNext() && itr.peekNext().getValue().equals(vNew))
{
itr.next();
remove(itr);
itr.previous();
}
// Case 4 - t >= to:
// The current segment is beyond the update horizon,
// from now on, the signal will not change.
return t >= to;
}
private static void processUpdate(
ChainIterator> itr,
double from, double to, V vNew, // update
double t, double tNext, V v, // current segment
V prevV, // previous value
BiPredicate refinable)
{
// Case 1 - `from` in (t, tNext):
// This means the update starts in the current segment
if(t < from && tNext > from && refinable.test(v, vNew)) {
add(itr, from, vNew);
}
// Case 2 - from == t:
// This means the current segment starts exactly at
// update time, therefore, its value must be updated
if(t == from && !v.equals(vNew)) {
update(itr, t, v, vNew, refinable, prevV);
}
// Case 3 - t in (from, to):
// This means the current segment starts within the update
// horizon and must therefore be updated
if(t > from && t < to && refinable.test(v, vNew) && !v.equals(vNew)) {
remove(itr);
}
}
/**
* Method for checking whether the provided interval refines the current
* one, and to update it accordingly.
* @param itr iterator of the signal segments
* @param t current time instant
* @param v current value
* @param vNew new value from the update
* @param refinable boolean predicate that tells whether it is refinable
* @param prevV the value of the previous segment of the chain
*/
private static
void update(ChainIterator> itr,
double t, V v, V vNew, BiPredicate refinable, V prevV)
{
boolean isRefinable = refinable.test(v, vNew);
if(isRefinable && prevV.equals(vNew)) {
remove(itr);
} else if (isRefinable) {
Sample s = new TimeSegment<>(t, vNew);
itr.set(s);
} else {
throw new UnsupportedOperationException("Refining interval: " +
vNew + " is wider than " +
"the original:" + v);
}
}
/**
* Removes the last object seen by the iterator.
* Note that the iterator is one step ahead, so we have to bring it back
* first.
* @param itr iterator to update
*/
private static
void remove(ChainIterator> itr)
{
itr.previous();
itr.remove();
}
private static void add(ChainIterator> itr,
Double start, V vNew)
{
if(itr.hasPrevious() && !itr.peekPrevious().getValue().equals(vNew)) {
itr.add(new TimeSegment<>(start, vNew));
}
if(itr.hasNext() && itr.peekNext().getValue().equals(vNew)) {
itr.next();
remove(itr);
if(itr.hasPrevious())
itr.previous();
}
}
/**
* Selects a fragment of the given {@code TimeChain}
* @param segments original chain of segments
* @param from first time of interest for the caller
* @param to last time of interest for the caller
* @param Time domain of the chain
* @param value domain of the chain
* @return a sub-chain of the input signal
*/
public static , V>
TimeChain select(TimeChain segments, T from, T to)
{
if(from.compareTo(to) > 0)
throw new UnsupportedOperationException("Illegal selection span");
int start = 0;
int end = 1;
ChainIterator> itr = segments.chainIterator();
do {
Sample current = itr.next();
// We went too far, the last returned is the last one useful
if(current.getStart().compareTo(to) > 0) {
end = itr.previousIndex();
break;
}
// current is before/at `from`, so it's the last useful index
if(current.getStart().compareTo(from) <= 0)
start = itr.previousIndex();
// Last segment, this is necessarily the last interesting one.
if(itr.tryPeekNext(current).equals(current))
end = itr.previousIndex() + 1;
} while(itr.hasNext());
return segments.subChain(start, end, to);
}
}