Skip to content

Commit

Permalink
Merge pull request #1007 from NASA-AMMOS/830-add-shift-operation-for-…
Browse files Browse the repository at this point in the history
…profiles-to-edsl

Shift operation for profiles
  • Loading branch information
JoelCourtney authored Jul 6, 2023
2 parents 6ddc8a9 + 5351d2f commit b2c6785
Show file tree
Hide file tree
Showing 20 changed files with 391 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ static <P extends Profile<P>> JsonParser<AssignGaps<P>> assignGapsF(final JsonPa
);
}

static <P extends Profile<P>> JsonParser<ShiftBy<P>> shiftByF(final JsonParser<Expression<P>> profileParser) {
return productP
.field("kind", literalP("ProfileExpressionShiftBy"))
.field("expression", profileParser)
.field("duration", durationExprP)
.map(
untuple((kind, expression, duration) -> new ShiftBy<>(expression, duration)),
$ -> tuple(Unit.UNIT, $.expression(), $.duration())
);
}


static final JsonParser<DiscreteResource> discreteResourceP =
productP
.field("kind", literalP("DiscreteProfileResource"))
Expand Down Expand Up @@ -106,6 +118,7 @@ public static JsonParser<Expression<DiscreteProfile>> discreteProfileExprF(JsonP
discreteValueP,
discreteParameterP,
assignGapsF(selfP),
shiftByF(selfP),
valueAtExpressionF(profileExpressionP, spansExpressionP),
listExpressionF(profileExpressionP),
structExpressionF(profileExpressionP)
Expand Down Expand Up @@ -206,6 +219,7 @@ private static JsonParser<Expression<LinearProfile>> linearProfileExprF(JsonPars
timesF(selfP),
rateF(selfP),
assignGapsF(selfP),
shiftByF(selfP),
accumulatedDurationF(windowsP),
accumulatedDurationF(spansP)
));
Expand Down Expand Up @@ -288,14 +302,14 @@ static JsonParser<Transition> transitionP(JsonParser<ProfileExpression<?>> profi
$ -> tuple(Unit.UNIT, $.value(), $.interval())
);

static JsonParser<ShiftBy> shiftByF(JsonParser<Expression<Windows>> windowsExpressionP) {
static JsonParser<ShiftWindowsEdges> shiftWindowsEdgesF(JsonParser<Expression<Windows>> windowsExpressionP) {
return productP
.field("kind", literalP("WindowsExpressionShiftBy"))
.field("windowExpression", windowsExpressionP)
.field("fromStart", durationExprP)
.field("fromEnd", durationExprP)
.map(
untuple((kind, windowsExpression, fromStart, fromEnd) -> new ShiftBy(windowsExpression, fromStart, fromEnd)),
untuple((kind, windowsExpression, fromStart, fromEnd) -> new ShiftWindowsEdges(windowsExpression, fromStart, fromEnd)),
$ -> tuple(Unit.UNIT, $.windows, $.fromStart, $.fromEnd));
}
static final JsonParser<EndOf> endOfP =
Expand Down Expand Up @@ -515,12 +529,13 @@ private static JsonParser<Expression<Windows>> windowsExpressionF(JsonParser<Exp
andF(selfP),
orF(selfP),
notF(selfP),
shiftByF(selfP),
shiftWindowsEdgesF(selfP),
startsF(selfP),
endsF(selfP),
windowsFromSpansF(spansP),
activityWindowP,
assignGapsF(selfP)
assignGapsF(selfP),
shiftByF(selfP)
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,19 @@ public DiscreteProfile assignGaps(final DiscreteProfile def) {
);
}

@Override
public DiscreteProfile shiftBy(final Duration duration) {
final var builder = IntervalMap.<SerializedValue>builder();

for (final var segment : this.profilePieces) {
final var interval = segment.interval();
final var shiftedInterval = interval.shiftBy(duration);

builder.set(Segment.of(shiftedInterval, segment.value()));
}
return new DiscreteProfile(builder.build());
}

@Override
public Optional<SerializedValue> valueAt(final Duration timepoint) {
final var matchPiece = profilePieces
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,25 @@ public Optional<SerializedValue> valueAt(final Duration timepoint) {
.map(linearEquationSegment -> SerializedValue.of(linearEquationSegment.value().valueAt(timepoint)));
}

@Override
public LinearProfile shiftBy(final Duration duration) {
final var builder = IntervalMap.<LinearEquation>builder();

for (final var segment : this.profilePieces) {
final var interval = segment.interval();
final var shiftedInterval = interval.shiftBy(duration);

final var shiftedValue = new LinearEquation(
segment.value().initialTime.saturatingPlus(duration),
segment.value().initialValue,
segment.value().rate
);

builder.set(Segment.of(shiftedInterval, shiftedValue));
}
return new LinearProfile(builder.build());
}

public static LinearProfile fromSimulatedProfile(final List<ProfileSegment<RealDynamics>> simulatedProfile) {
return fromProfileHelper(Duration.ZERO, simulatedProfile, Optional::of);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public interface Profile<P extends Profile<P>> {
boolean isConstant();

P assignGaps(P def);
P shiftBy(Duration duration);

Optional<SerializedValue> valueAt(Duration timepoint);
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@ public boolean isPoint() {
this.start == this.end;
}

public Interval shiftBy(final Duration duration) {
return Interval.between(
this.start.saturatingPlus(duration),
this.startInclusivity,
this.end.saturatingPlus(duration),
this.endInclusivity
);
}

public Duration duration() {
if (this.isEmpty()) return Duration.ZERO;
return this.end.minus(this.start);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ public Windows filterByDuration(Duration minDur, Duration maxDur) {
* @param fromEnd duration to shift true -> false falling edges
* @return a new Windows
*/
public Windows shiftBy(Duration fromStart, Duration fromEnd) {
public Windows shiftEdges(Duration fromStart, Duration fromEnd) {
final var builder = IntervalMap.<Boolean>builder();

for (final var segment : this.segments) {
Expand All @@ -363,6 +363,11 @@ public Windows shiftBy(Duration fromStart, Duration fromEnd) {
return new Windows(builder.build());
}

@Override
public Windows shiftBy(Duration duration) {
return this.shiftEdges(duration, duration);
}

/**
* Converts this into Spans and splits each Span into sub-spans.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public ActivityWindow(final String activityAlias) {
public Windows evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
final var activity = environment.activityInstances().get(this.activityAlias);
return new Windows(
Segment.of(Interval.FOREVER, false),
Segment.of(bounds, false),
Segment.of(activity.interval, true)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public DiscreteParameter(final String activityAlias, final String parameterName)
public DiscreteProfile evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
final var activity = environment.activityInstances().get(this.activityAlias);
return new DiscreteProfile(
Segment.of(Interval.FOREVER, activity.parameters.get(this.parameterName))
Segment.of(bounds, activity.parameters.get(this.parameterName))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public record DiscreteProfileFromDuration(
@Override
public DiscreteProfile evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
final Duration duration = this.duration.evaluate(results, bounds, environment);
return new DiscreteProfile(Segment.of(Interval.FOREVER, SerializedValue.of(duration.in(Duration.MICROSECOND))));
return new DiscreteProfile(Segment.of(bounds, SerializedValue.of(duration.in(Duration.MICROSECOND))));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public EndOf(final String activityAlias) {
public Windows evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
final var activity = environment.activityInstances().get(this.activityAlias);
return new Windows(
Segment.of(Interval.FOREVER, false),
Segment.of(bounds, false),
Segment.of(Interval.at(activity.interval.end), true)
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,41 @@
package gov.nasa.jpl.aerie.constraints.tree;

import gov.nasa.jpl.aerie.constraints.model.EvaluationEnvironment;
import gov.nasa.jpl.aerie.constraints.model.Profile;
import gov.nasa.jpl.aerie.constraints.model.SimulationResults;
import gov.nasa.jpl.aerie.constraints.time.Interval;
import gov.nasa.jpl.aerie.constraints.time.Windows;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

import java.util.Objects;
import java.util.Set;

public final class ShiftBy implements Expression<Windows> {
public final Expression<Windows> windows;
public final Expression<Duration> fromStart;
public final Expression<Duration> fromEnd;

public ShiftBy(final Expression<Windows> left, final Expression<Duration> fromStart, final Expression<Duration> fromEnd) {
this.windows = left;
this.fromStart = fromStart;
this.fromEnd = fromEnd;
}
public record ShiftBy<P extends Profile<P>>(
Expression<P> expression,
Expression<Duration> duration) implements Expression<P> {

@Override
public Windows evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
final var windows = this.windows.evaluate(results, bounds, environment);
return windows.shiftBy(this.fromStart.evaluate(results, bounds, environment), this.fromEnd.evaluate(results, bounds, environment));
public P evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
// bounds aren't shifted here because duration expressions don't care about them; durations don't exist on the timeline.
final var duration = this.duration.evaluate(results, bounds, environment);

final var shiftedBounds = bounds.shiftBy(Duration.negate(duration));
final var originalProfile = this.expression.evaluate(results, shiftedBounds, environment);

return originalProfile.shiftBy(duration);
}

@Override
public void extractResources(final Set<String> names) {
this.windows.extractResources(names);
this.expression.extractResources(names);
this.duration.extractResources(names);
}

@Override
public String prettyPrint(final String prefix) {
return String.format(
"\n%s(shift %s by %s %s)",
"\n%s(shiftBy %s %s)",
prefix,
this.windows.prettyPrint(prefix + " "),
this.fromStart.toString(),
this.fromEnd.toString()
this.expression.prettyPrint(prefix + " "),
this.duration.prettyPrint(prefix + " ")
);
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof ShiftBy)) return false;
final var o = (ShiftBy)obj;

return Objects.equals(this.windows, o.windows) &&
Objects.equals(this.fromStart, o.fromStart) &&
Objects.equals(this.fromEnd, o.fromEnd);
}

@Override
public int hashCode() {
return Objects.hash(this.windows, this.fromStart, this.fromEnd);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package gov.nasa.jpl.aerie.constraints.tree;

import gov.nasa.jpl.aerie.constraints.model.EvaluationEnvironment;
import gov.nasa.jpl.aerie.constraints.model.SimulationResults;
import gov.nasa.jpl.aerie.constraints.time.Interval;
import gov.nasa.jpl.aerie.constraints.time.Windows;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

import java.util.Objects;
import java.util.Set;

public final class ShiftWindowsEdges implements Expression<Windows> {
public final Expression<Windows> windows;
public final Expression<Duration> fromStart;
public final Expression<Duration> fromEnd;

public ShiftWindowsEdges(final Expression<Windows> left, final Expression<Duration> fromStart, final Expression<Duration> fromEnd) {
this.windows = left;
this.fromStart = fromStart;
this.fromEnd = fromEnd;
}

@Override
public Windows evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
final var shiftRising = this.fromStart.evaluate(results, bounds, environment);
final var shiftFalling = this.fromEnd.evaluate(results, bounds, environment);

final var newBounds = Interval.between(
Duration.min(bounds.start.minus(shiftRising), bounds.start.minus(shiftFalling)),
bounds.startInclusivity,
Duration.max(bounds.end.minus(shiftRising), bounds.end.minus(shiftFalling)),
bounds.endInclusivity
);

final var windows = this.windows.evaluate(results, newBounds, environment);
return windows.shiftEdges(shiftRising, shiftFalling).select(bounds);
}

@Override
public void extractResources(final Set<String> names) {
this.windows.extractResources(names);
}

@Override
public String prettyPrint(final String prefix) {
return String.format(
"\n%s(shiftWindowsEdges %s by %s %s)",
prefix,
this.windows.prettyPrint(prefix + " "),
this.fromStart.toString(),
this.fromEnd.toString()
);
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof ShiftWindowsEdges)) return false;
final var o = (ShiftWindowsEdges)obj;

return Objects.equals(this.windows, o.windows) &&
Objects.equals(this.fromStart, o.fromStart) &&
Objects.equals(this.fromEnd, o.fromEnd);
}

@Override
public int hashCode() {
return Objects.hash(this.windows, this.fromStart, this.fromEnd);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public StartOf(final String activityAlias) {
public Windows evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
final var activity = environment.activityInstances().get(this.activityAlias);
return new Windows(
Segment.of(Interval.FOREVER, false),
Segment.of(bounds, false),
Segment.of(Interval.at(activity.interval.start), true)
);
}
Expand Down
Loading

0 comments on commit b2c6785

Please sign in to comment.