Skip to content

Commit

Permalink
implement option for run length compression in resource profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
Marsette Vona committed Mar 2, 2024
1 parent 7526784 commit 6291dfc
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -444,19 +444,20 @@ public static SimulationResults computeResults(

final var name = id.id();
final var resource = state.resource();
final boolean allowRLE = resource.allowRunLengthCompression();

switch (resource.getType()) {
case "real" -> realProfiles.put(
name,
Pair.of(
resource.getOutputType().getSchema(),
serializeProfile(elapsedTime, state, SimulationEngine::extractRealDynamics)));
serializeProfile(elapsedTime, state, SimulationEngine::extractRealDynamics, allowRLE)));

case "discrete" -> discreteProfiles.put(
name,
Pair.of(
resource.getOutputType().getSchema(),
serializeProfile(elapsedTime, state, SimulationEngine::extractDiscreteDynamics)));
serializeProfile(elapsedTime, state, SimulationEngine::extractDiscreteDynamics, allowRLE)));

default ->
throw new IllegalArgumentException(
Expand Down Expand Up @@ -608,11 +609,24 @@ private interface Translator<Target> {
<Dynamics> Target apply(Resource<Dynamics> resource, Dynamics dynamics);
}

private static <Target>
void appendProfileSegment(ArrayList<ProfileSegment<Target>> profile, Duration duration, Target value,
boolean allowRunLengthCompression) {
final int s = profile.size();
final ProfileSegment lastSeg = s > 0 ? profile.get(s - 1) : null;
if (allowRunLengthCompression && lastSeg != null && value.equals(lastSeg.dynamics())) {
profile.set(s - 1, new ProfileSegment<>(lastSeg.extent().plus(duration), value));
} else {
profile.add(new ProfileSegment<>(duration, value));
}
}

private static <Target, Dynamics>
List<ProfileSegment<Target>> serializeProfile(
final Duration elapsedTime,
final ProfilingState<Dynamics> state,
final Translator<Target> translator
final Translator<Target> translator,
final boolean allowRunLengthCompression
) {
final var profile = new ArrayList<ProfileSegment<Target>>(state.profile().segments().size());

Expand All @@ -621,18 +635,21 @@ List<ProfileSegment<Target>> serializeProfile(
var segment = iter.next();
while (iter.hasNext()) {
final var nextSegment = iter.next();

profile.add(new ProfileSegment<>(
nextSegment.startOffset().minus(segment.startOffset()),
translator.apply(state.resource(), segment.dynamics())));
appendProfileSegment(profile,
nextSegment.startOffset().minus(segment.startOffset()),
translator.apply(state.resource(), segment.dynamics()),
allowRunLengthCompression);
segment = nextSegment;
}

profile.add(new ProfileSegment<>(
elapsedTime.minus(segment.startOffset()),
translator.apply(state.resource(), segment.dynamics())));
appendProfileSegment(profile,
elapsedTime.minus(segment.startOffset()),
translator.apply(state.resource(), segment.dynamics()),
allowRunLengthCompression);
}

profile.trimToSize();

return profile;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,34 @@
import java.util.function.UnaryOperator;

public final class Registrar {

/**
* Whether to allow run length compression when saving resource profiles at the end of simulation by default.
*
* This compression is lossless in terms of the overall shape of the profile, but it will combine adjacent profile
* segments with the same value, thus obscuring the fact that multiple resource samples (again, all returning the same
* value) were taken within the segment.
*/
private static final boolean ALLOW_RUN_LENGTH_COMPRESSION_BY_DEFAULT = false;

private final Initializer builder;
private boolean allowRunLengthCompression = ALLOW_RUN_LENGTH_COMPRESSION_BY_DEFAULT;

public Registrar(final Initializer builder) {
this.builder = Objects.requireNonNull(builder);
}

public void allowRunLengthCompression(final boolean allow) {
this.allowRunLengthCompression = allow;
}

public boolean isInitializationComplete() {
return (ModelActions.context.get().getContextType() != Context.ContextType.Initializing);
}

public <Value> void discrete(final String name, final Resource<Value> resource, final ValueMapper<Value> mapper) {
this.builder.resource(name, makeResource("discrete", resource, mapper.getValueSchema(), mapper::serializeValue));
this.builder.resource(name, makeResource("discrete", resource, mapper.getValueSchema(), mapper::serializeValue,
allowRunLengthCompression));
}

public void real(final String name, final Resource<RealDynamics> resource) {
Expand All @@ -46,14 +62,16 @@ private void real(final String name, final Resource<RealDynamics> resource, Unar
"rate", ValueSchema.REAL))),
dynamics -> SerializedValue.of(Map.of(
"initial", SerializedValue.of(dynamics.initial),
"rate", SerializedValue.of(dynamics.rate)))));
"rate", SerializedValue.of(dynamics.rate))),
allowRunLengthCompression));
}

private static <Value> gov.nasa.jpl.aerie.merlin.protocol.model.Resource<Value> makeResource(
final String type,
final Resource<Value> resource,
final ValueSchema valueSchema,
final Function<Value, SerializedValue> serializer
final Function<Value, SerializedValue> serializer,
final boolean allowRunLengthCompression
) {
return new gov.nasa.jpl.aerie.merlin.protocol.model.Resource<>() {
@Override
Expand Down Expand Up @@ -82,6 +100,11 @@ public Value getDynamics(final Querier querier) {
return resource.getDynamics();
}
}

@Override
public boolean allowRunLengthCompression() {
return allowRunLengthCompression;
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,18 @@ public interface Resource<Dynamics> {
* this resource. In other words, it cannot depend on any hidden state. </p>
*/
Dynamics getDynamics(Querier querier);

/**
* After a simulation completes the entire evolution of the dynamics of this resource will typically be serialized as
* a resource profile consisting of some number of sequential segments.
*
* If run length compression is allowed for this resource then whenever there is a "run" of two or more such segments,
* one after another with the same dynamics, they will be compressed into a single segment during that serialization.
* This does not change the represented evolution of the dynamics of the resource, but it loses the information that a
* sample was taken at the start of each segment after the first in such a run. If a mission model prefers not to
* lose that information then it can return false here.
*/
default boolean allowRunLengthCompression() {
return false;
}
}

0 comments on commit 6291dfc

Please sign in to comment.