Skip to content

Commit

Permalink
Implement ShiftEdges for Spans
Browse files Browse the repository at this point in the history
  • Loading branch information
JoelCourtney committed Sep 18, 2023
1 parent 0c7891d commit c698611
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -335,15 +335,15 @@ static JsonParser<Transition> transitionP(JsonParser<ProfileExpression<?>> profi
$ -> tuple(Unit.UNIT, $.value(), $.interval())
);

static JsonParser<ShiftWindowsEdges> shiftWindowsEdgesF(JsonParser<Expression<Windows>> windowsExpressionP) {
static <I extends IntervalContainer<I>> JsonParser<ShiftEdges<I>> shiftEdgesF(final JsonParser<Expression<I>> intervalExpressionP) {
return productP
.field("kind", literalP("WindowsExpressionShiftBy"))
.field("windowExpression", windowsExpressionP)
.field("kind", literalP("IntervalsExpressionShiftEdges"))
.field("expression", intervalExpressionP)
.field("fromStart", durationExprP)
.field("fromEnd", durationExprP)
.map(
untuple((kind, windowsExpression, fromStart, fromEnd) -> new ShiftWindowsEdges(windowsExpression, fromStart, fromEnd)),
$ -> tuple(Unit.UNIT, $.windows, $.fromStart, $.fromEnd));
untuple((kind, expr, fromStart, fromEnd) -> new ShiftEdges<I>(expr, fromStart, fromEnd)),
$ -> tuple(Unit.UNIT, $.expression, $.fromStart, $.fromEnd));
}
static final JsonParser<EndOf> endOfP =
productP
Expand Down Expand Up @@ -562,7 +562,7 @@ private static JsonParser<Expression<Windows>> windowsExpressionF(JsonParser<Exp
andF(selfP),
orF(selfP),
notF(selfP),
shiftWindowsEdgesF(selfP),
shiftEdgesF(selfP),
startsF(selfP),
endsF(selfP),
windowsFromSpansF(spansP),
Expand Down Expand Up @@ -595,6 +595,7 @@ private static JsonParser<Expression<Spans>> spansExpressionF(JsonParser<Express
spansIntervalP,
startsF(selfP),
endsF(selfP),
shiftEdgesF(selfP),
splitF(selfP),
splitF(windowsP),
spansFromWindowsF(windowsP),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ public interface IntervalContainer<T extends IntervalContainer<T>> {
LinearProfile accumulatedDuration(final Duration unit);
T starts();
T ends();
T shiftEdges(final Duration fromStart, final Duration fromEnd);
T select(final Interval... intervals);
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,16 @@ public Spans ends() {
return this.map($ -> Interval.at($.end));
}

@Override
public Spans shiftEdges(final Duration fromStart, final Duration fromEnd) {
return this.map($ -> Interval.between($.start.plus(fromStart), $.startInclusivity, $.end.plus(fromEnd), $.endInclusivity));
}

@Override
public Spans select(final Interval... intervals) {
return this.flatMap($ -> Arrays.stream(intervals).map(selection -> Interval.intersect($, selection)));
}

@Override
public boolean equals(final Object obj) {
if (!(obj instanceof final Spans spans)) return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ public Windows filterByDuration(Duration minDur, Duration maxDur) {
* @param fromEnd duration to shift true -> false falling edges
* @return a new Windows
*/
@Override
public Windows shiftEdges(Duration fromStart, Duration fromEnd) {
final var builder = IntervalMap.<Boolean>builder();

Expand Down Expand Up @@ -622,6 +623,7 @@ public Windows unset(final List<Interval> intervals) {
}

/** Delegated to {@link IntervalMap#select(Interval...)} */
@Override
public Windows select(final Interval... intervals) {
return new Windows(segments.select(intervals));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
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.constraints.time.IntervalContainer;
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 class ShiftEdges<I extends IntervalContainer<I>> implements Expression<I> {
public final Expression<I> expression;
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;
public ShiftEdges(final Expression<I> left, final Expression<Duration> fromStart, final Expression<Duration> fromEnd) {
this.expression = left;
this.fromStart = fromStart;
this.fromEnd = fromEnd;
}

@Override
public Windows evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
public I 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);

Expand All @@ -32,38 +32,38 @@ public Windows evaluate(final SimulationResults results, final Interval bounds,
bounds.endInclusivity
);

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

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

@Override
public String prettyPrint(final String prefix) {
return String.format(
"\n%s(shiftWindowsEdges %s by %s %s)",
"\n%s(shiftEdges %s by %s %s)",
prefix,
this.windows.prettyPrint(prefix + " "),
this.expression.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;
if (!(obj instanceof ShiftEdges)) return false;
final var o = (ShiftEdges<?>)obj;

return Objects.equals(this.windows, o.windows) &&
return Objects.equals(this.expression, o.expression) &&
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);
return Objects.hash(this.expression, this.fromStart, this.fromEnd);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ public void testExpandBy() {
final var expandByFromStart = Duration.of(-1, SECONDS);
final var expandByFromEnd = Duration.of(0, SECONDS);

final var result = new ShiftWindowsEdges(Supplier.of(left), Supplier.of(expandByFromStart), Supplier.of(expandByFromEnd)).evaluate(simResults, new EvaluationEnvironment());
final var result = new ShiftEdges<>(Supplier.of(left), Supplier.of(expandByFromStart), Supplier.of(expandByFromEnd)).evaluate(simResults, new EvaluationEnvironment());

final var expected = new Windows()
.set(Interval.between(-1, Inclusive, 7, Inclusive, SECONDS), true)
Expand Down Expand Up @@ -410,7 +410,7 @@ public void testShrink() {
final var clampFromStart = Duration.of(1, SECONDS);
final var clampFromEnd = Duration.negate(Duration.of(1, SECONDS));

final var result = new ShiftWindowsEdges(Supplier.of(left), Supplier.of(clampFromStart), Supplier.of(clampFromEnd)).evaluate(simResults, new EvaluationEnvironment());
final var result = new ShiftEdges<>(Supplier.of(left), Supplier.of(clampFromStart), Supplier.of(clampFromEnd)).evaluate(simResults, new EvaluationEnvironment());

final var expected = new Windows()
.set(Interval.between(1, Inclusive, 4, Exclusive, SECONDS), true)
Expand Down Expand Up @@ -1092,15 +1092,15 @@ public void testShiftWindowsEdgesBoundsAdjustment() {

final var crossingStartOfPlan = new Windows(false).set(Interval.between(-1, 1, SECONDS), true);

final var result1 = new ShiftWindowsEdges(
final var result1 = new ShiftEdges<>(
Supplier.of(crossingStartOfPlan),
Supplier.of(Duration.ZERO),
Supplier.of(Duration.of(10, SECONDS))
).evaluate(simResults);
final var expected1 = new Windows(false).set(Interval.between(-1, 11, SECONDS), true).select(simResults.bounds);
assertIterableEquals(expected1, result1);

final var result2 = new ShiftWindowsEdges(
final var result2 = new ShiftEdges<>(
Supplier.of(crossingStartOfPlan),
Supplier.of(Duration.of(-10, SECONDS)),
Supplier.of(Duration.ZERO)
Expand All @@ -1110,15 +1110,15 @@ public void testShiftWindowsEdgesBoundsAdjustment() {

final var crossingEndOfPlan = new Windows(false).set(Interval.between(19, 21, SECONDS), true);

final var result3 = new ShiftWindowsEdges(
final var result3 = new ShiftEdges<>(
Supplier.of(crossingEndOfPlan),
Supplier.of(Duration.ZERO),
Supplier.of(Duration.of(10, SECONDS))
).evaluate(simResults);
final var expected3 = new Windows(false).set(Interval.between(19, 20, SECONDS), true).select(simResults.bounds);
assertIterableEquals(expected3, result3);

final var result4 = new ShiftWindowsEdges(
final var result4 = new ShiftEdges<>(
Supplier.of(crossingEndOfPlan),
Supplier.of(Duration.of(-10, SECONDS)),
Supplier.of(Duration.ZERO)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export enum NodeKind {
WindowsExpressionEndOf = 'WindowsExpressionEndOf',
WindowsExpressionLongerThan = 'WindowsExpressionLongerThan',
WindowsExpressionShorterThan = 'WindowsExpressionShorterThan',
WindowsExpressionShiftBy = 'WindowsExpressionShiftBy',
WindowsExpressionFromSpans = 'WindowsExpressionFromSpans',
SpansExpressionFromWindows = 'SpansExpressionFromWindows',
SpansExpressionSplit = 'SpansExpressionSplit',
Expand All @@ -40,6 +39,7 @@ export enum NodeKind {
WindowsExpressionNot = 'WindowsExpressionNot',
IntervalsExpressionStarts = 'IntervalsExpressionStarts',
IntervalsExpressionEnds = 'IntervalsExpressionEnds',
IntervalsExpressionShiftEdges = 'IntervalsExpressionShiftEdges',
ForEachActivitySpans = 'ForEachActivitySpans',
ForEachActivityViolations = 'ForEachActivityViolations',
ProfileChanges = 'ProfileChanges',
Expand Down Expand Up @@ -107,7 +107,7 @@ export type WindowsExpression =
| WindowsExpressionLongerThan
| WindowsExpressionShorterThan
| WindowsExpressionNot
| WindowsExpressionShiftBy
| IntervalsExpressionShiftEdges
| WindowsExpressionFromSpans
| IntervalsExpressionStarts
| IntervalsExpressionEnds
Expand All @@ -118,6 +118,7 @@ export type SpansExpression =
| SpansExpressionSplit
| IntervalsExpressionStarts
| IntervalsExpressionEnds
| IntervalsExpressionShiftEdges
| SpansExpressionFromWindows
| ForEachActivitySpans
| SpansExpressionInterval;
Expand Down Expand Up @@ -148,9 +149,9 @@ export interface WindowsExpressionNot {
expression: WindowsExpression;
}

export interface WindowsExpressionShiftBy {
kind: NodeKind.WindowsExpressionShiftBy,
windowExpression: WindowsExpression,
export interface IntervalsExpressionShiftEdges {
kind: NodeKind.IntervalsExpressionShiftEdges,
expression: IntervalsExpression,
fromStart: Duration,
fromEnd: Duration,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,8 @@ export class Windows {
});
} else {
return new Windows({
kind: AST.NodeKind.WindowsExpressionShiftBy,
windowExpression: this.__astNode,
kind: AST.NodeKind.IntervalsExpressionShiftEdges,
expression: this.__astNode,
fromStart,
fromEnd
})
Expand Down Expand Up @@ -471,6 +471,21 @@ export class Spans {
})
}

/**
* Shifts the start and end of each Span by a duration.
*
* @param fromStart duration to shift start by
* @param fromEnd duration to shift end by (defaults is `fromStart` if omitted)
*/
public shiftBy(fromStart: AST.Duration, fromEnd?: AST.Duration | undefined): Spans {
return new Spans({
kind: AST.NodeKind.IntervalsExpressionShiftEdges,
expression: this.__astNode,
fromStart,
fromEnd: fromEnd !== undefined ? fromEnd : fromStart
})
}

/**
* Convert this into a set of Windows. Each span is a true segment, and everything else is false.
*
Expand Down Expand Up @@ -1314,6 +1329,14 @@ declare global {
*/
public ends(): Spans;

/**
* Shifts the start and end of each Span by a duration.
*
* @param fromStart duration to shift start by
* @param fromEnd duration to shift end by (defaults is `fromStart` if omitted)
*/
public shiftBy(fromStart: AST.Duration, fromEnd?: AST.Duration | undefined): Spans;

/**
* Splits each span into equal sized sub-spans.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import gov.nasa.jpl.aerie.constraints.tree.RealValue;
import gov.nasa.jpl.aerie.constraints.tree.RollingThreshold;
import gov.nasa.jpl.aerie.constraints.tree.ShiftBy;
import gov.nasa.jpl.aerie.constraints.tree.ShiftWindowsEdges;
import gov.nasa.jpl.aerie.constraints.tree.ShiftEdges;
import gov.nasa.jpl.aerie.constraints.tree.ShorterThan;
import gov.nasa.jpl.aerie.constraints.tree.SpansFromWindows;
import gov.nasa.jpl.aerie.constraints.tree.Split;
Expand Down Expand Up @@ -501,7 +501,7 @@ export default() => {
return Real.Resource("state of charge").rate().equal(Real.Value(4.0)).shiftBy(minute(2), minute(-20))
}
""",
new ViolationsOfWindows(new ShiftWindowsEdges(
new ViolationsOfWindows(new ShiftEdges(
new Equal<>(new Rate(new RealResource("state of charge")), new RealValue(4.0)),
new DurationLiteral(Duration.of(2, Duration.MINUTE)),
new DurationLiteral(Duration.of(-20, Duration.MINUTE)))
Expand Down

0 comments on commit c698611

Please sign in to comment.