From 4073238f3958ff02a3201e2b7b4472e70b1fa4ba Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 28 Jun 2023 12:12:32 -0700 Subject: [PATCH 1/6] Add universal shiftBy node, rename old node to ShiftWindowsEdges --- .../constraints/json/ConstraintParsers.java | 23 ++++++-- .../constraints/model/DiscreteProfile.java | 13 ++++ .../constraints/model/LinearProfile.java | 19 ++++++ .../jpl/aerie/constraints/model/Profile.java | 1 + .../jpl/aerie/constraints/time/Interval.java | 9 +++ .../jpl/aerie/constraints/time/Windows.java | 7 ++- .../jpl/aerie/constraints/tree/ShiftBy.java | 49 +++++---------- .../constraints/tree/ShiftWindowsEdges.java | 59 +++++++++++++++++++ .../aerie/constraints/time/WindowsTest.java | 20 +++---- .../jpl/aerie/constraints/tree/ASTTests.java | 4 +- .../src/libs/constraints-ast.ts | 14 ++++- .../src/libs/constraints-edsl-fluent-api.ts | 44 +++++++++++--- ...ConstraintsDSLCompilationServiceTests.java | 7 ++- .../transformers/TransformerAfterEach.java | 2 +- .../transformers/TransformerBeforeEach.java | 2 +- 15 files changed, 206 insertions(+), 67 deletions(-) create mode 100644 constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftWindowsEdges.java diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/json/ConstraintParsers.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/json/ConstraintParsers.java index cfb882db49..2d3fd6fff0 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/json/ConstraintParsers.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/json/ConstraintParsers.java @@ -74,6 +74,18 @@ static

> JsonParser> assignGapsF(final JsonPa ); } + static

> JsonParser> shiftByF(final JsonParser> 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 discreteResourceP = productP .field("kind", literalP("DiscreteProfileResource")) @@ -106,6 +118,7 @@ public static JsonParser> discreteProfileExprF(JsonP discreteValueP, discreteParameterP, assignGapsF(selfP), + shiftByF(selfP), valueAtExpressionF(profileExpressionP, spansExpressionP), listExpressionF(profileExpressionP), structExpressionF(profileExpressionP) @@ -206,6 +219,7 @@ private static JsonParser> linearProfileExprF(JsonPars timesF(selfP), rateF(selfP), assignGapsF(selfP), + shiftByF(selfP), accumulatedDurationF(windowsP), accumulatedDurationF(spansP) )); @@ -288,14 +302,14 @@ static JsonParser transitionP(JsonParser> profi $ -> tuple(Unit.UNIT, $.value(), $.interval()) ); - static JsonParser shiftByF(JsonParser> windowsExpressionP) { + static JsonParser shiftWindowsEdgesF(JsonParser> 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 endOfP = @@ -515,12 +529,13 @@ private static JsonParser> windowsExpressionF(JsonParserbuilder(); + + 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 valueAt(final Duration timepoint) { final var matchPiece = profilePieces diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/LinearProfile.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/LinearProfile.java index 18325e07d9..e9f9469151 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/LinearProfile.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/LinearProfile.java @@ -156,6 +156,25 @@ public Optional valueAt(final Duration timepoint) { .map(linearEquationSegment -> SerializedValue.of(linearEquationSegment.value().valueAt(timepoint))); } + @Override + public LinearProfile shiftBy(final Duration duration) { + final var builder = IntervalMap.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> simulatedProfile) { return fromProfileHelper(Duration.ZERO, simulatedProfile, Optional::of); } diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/Profile.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/Profile.java index 2b34ab28fa..6c64987622 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/Profile.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/model/Profile.java @@ -13,6 +13,7 @@ public interface Profile

> { boolean isConstant(); P assignGaps(P def); + P shiftBy(Duration duration); Optional valueAt(Duration timepoint); } diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Interval.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Interval.java index e215e710ee..0767bd7302 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Interval.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Interval.java @@ -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); diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Windows.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Windows.java index 51aa398733..d7ab1ca3ab 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Windows.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/time/Windows.java @@ -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.builder(); for (final var segment : this.segments) { @@ -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. * diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftBy.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftBy.java index dc62fdd5cb..2db8a1a1f6 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftBy.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftBy.java @@ -1,59 +1,38 @@ 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 { - public final Expression windows; - public final Expression fromStart; - public final Expression fromEnd; - - public ShiftBy(final Expression left, final Expression fromStart, final Expression fromEnd) { - this.windows = left; - this.fromStart = fromStart; - this.fromEnd = fromEnd; - } +public record ShiftBy

>( + Expression

expression, + Expression duration) implements Expression

{ @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) { + final var originalProfile = this.expression.evaluate(results, bounds, environment); + final var duration = this.duration.evaluate(results, bounds, environment); + + return originalProfile.shiftBy(duration); } @Override public void extractResources(final Set 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); - } } diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftWindowsEdges.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftWindowsEdges.java new file mode 100644 index 0000000000..020a9ed560 --- /dev/null +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftWindowsEdges.java @@ -0,0 +1,59 @@ +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 { + public final Expression windows; + public final Expression fromStart; + public final Expression fromEnd; + + public ShiftWindowsEdges(final Expression left, final Expression fromStart, final Expression 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 windows = this.windows.evaluate(results, bounds, environment); + return windows.shiftEdges(this.fromStart.evaluate(results, bounds, environment), this.fromEnd.evaluate(results, bounds, environment)); + } + + @Override + public void extractResources(final Set 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); + } +} diff --git a/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/time/WindowsTest.java b/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/time/WindowsTest.java index 3b4c3fe9b0..eebf3e415d 100644 --- a/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/time/WindowsTest.java +++ b/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/time/WindowsTest.java @@ -347,7 +347,7 @@ public void shiftByStretch() { Segment.of(interval(14, Exclusive, 17, Exclusive, SECONDS), false), Segment.of(at(Duration.MAX_VALUE), true)); //long overflow if at max value - Windows result = orig.shiftBy(Duration.of(-1, SECONDS), Duration.of(1, SECONDS)); + Windows result = orig.shiftEdges(Duration.of(-1, SECONDS), Duration.of(1, SECONDS)); Windows expected = new Windows( @@ -392,7 +392,7 @@ public void shiftByConnectedIntervals() { .set(interval(0, 2, SECONDS), true) .set(interval(8, 10, SECONDS), true); - var fromStartPosFromEndPos = orig.shiftBy(Duration.of(-1, SECONDS), Duration.of(1, SECONDS)); + var fromStartPosFromEndPos = orig.shiftEdges(Duration.of(-1, SECONDS), Duration.of(1, SECONDS)); assertIterableEquals( new Windows(interval(0, 10, SECONDS), false) .set(interval(-1, 3, SECONDS), true) @@ -400,7 +400,7 @@ public void shiftByConnectedIntervals() { fromStartPosFromEndPos ); - var fromStartPosFromEndNeg = orig.shiftBy(Duration.of(1, SECONDS), Duration.of(-1, SECONDS)); + var fromStartPosFromEndNeg = orig.shiftEdges(Duration.of(1, SECONDS), Duration.of(-1, SECONDS)); assertEquals( new Windows(interval(1, Exclusive, 9, Exclusive, SECONDS), false) .set(at(1, SECONDS), true) @@ -408,7 +408,7 @@ public void shiftByConnectedIntervals() { fromStartPosFromEndNeg ); - var fromStartNegFromEndPos = orig.shiftBy(Duration.of(1, SECONDS), Duration.of(1, SECONDS)); + var fromStartNegFromEndPos = orig.shiftEdges(Duration.of(1, SECONDS), Duration.of(1, SECONDS)); assertEquals( new Windows(interval(1, 11, SECONDS), false) .set(interval(1, 3, SECONDS), true) @@ -416,7 +416,7 @@ public void shiftByConnectedIntervals() { fromStartNegFromEndPos ); - var fromStartNegFromEndNeg = orig.shiftBy(Duration.of(-1, SECONDS), Duration.of(-1, SECONDS)); + var fromStartNegFromEndNeg = orig.shiftEdges(Duration.of(-1, SECONDS), Duration.of(-1, SECONDS)); assertEquals( new Windows(interval(-1, 9, SECONDS), false) .set(interval(-1, 1, SECONDS), true) @@ -424,7 +424,7 @@ public void shiftByConnectedIntervals() { fromStartNegFromEndNeg ); - var removal = orig.shiftBy(Duration.of(0, SECONDS), Duration.of(-3, SECONDS)); + var removal = orig.shiftEdges(Duration.of(0, SECONDS), Duration.of(-3, SECONDS)); assertEquals( new Windows(interval(-1, Exclusive, 8, Exclusive, SECONDS), false), removal @@ -437,19 +437,19 @@ public void shiftByDisconnectedPoints() { .set(at(0, SECONDS), true) .set(at(2, SECONDS), false); - var fromStartPosFromEndPos = orig.shiftBy(Duration.of(-1, SECONDS), Duration.of(1, SECONDS)); + var fromStartPosFromEndPos = orig.shiftEdges(Duration.of(-1, SECONDS), Duration.of(1, SECONDS)); assertIterableEquals( new Windows(interval(-1, 1, SECONDS), true), fromStartPosFromEndPos ); - var fromStartPosFromEndNeg = orig.shiftBy(Duration.of(1, SECONDS), Duration.of(-1, SECONDS)); + var fromStartPosFromEndNeg = orig.shiftEdges(Duration.of(1, SECONDS), Duration.of(-1, SECONDS)); assertIterableEquals( new Windows(interval(1, 3, SECONDS), false), fromStartPosFromEndNeg ); - var fromStartNegFromEndPos = orig.shiftBy(Duration.of(1, SECONDS), Duration.of(1, SECONDS)); + var fromStartNegFromEndPos = orig.shiftEdges(Duration.of(1, SECONDS), Duration.of(1, SECONDS)); assertIterableEquals( new Windows() .set(at(1, SECONDS), true) @@ -457,7 +457,7 @@ public void shiftByDisconnectedPoints() { fromStartNegFromEndPos ); - var fromStartNegFromEndNeg = orig.shiftBy(Duration.of(-1, SECONDS), Duration.of(-1, SECONDS)); + var fromStartNegFromEndNeg = orig.shiftEdges(Duration.of(-1, SECONDS), Duration.of(-1, SECONDS)); assertIterableEquals( new Windows() .set(at(-1, SECONDS), true) diff --git a/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java b/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java index 05c4eaf98d..ee9a0ca0db 100644 --- a/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java +++ b/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java @@ -376,7 +376,7 @@ public void testExpandBy() { final var expandByFromStart = Duration.of(-1, SECONDS); final var expandByFromEnd = Duration.of(0, SECONDS); - final var result = new ShiftBy(Supplier.of(left), Supplier.of(expandByFromStart), Supplier.of(expandByFromEnd)).evaluate(simResults, new EvaluationEnvironment()); + final var result = new ShiftWindowsEdges(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) @@ -408,7 +408,7 @@ public void testShrink() { final var clampFromStart = Duration.of(1, SECONDS); final var clampFromEnd = Duration.negate(Duration.of(1, SECONDS)); - final var result = new ShiftBy(Supplier.of(left), Supplier.of(clampFromStart), Supplier.of(clampFromEnd)).evaluate(simResults, new EvaluationEnvironment()); + final var result = new ShiftWindowsEdges(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) diff --git a/merlin-server/constraints-dsl-compiler/src/libs/constraints-ast.ts b/merlin-server/constraints-dsl-compiler/src/libs/constraints-ast.ts index ddc771c33b..369aeda9c6 100644 --- a/merlin-server/constraints-dsl-compiler/src/libs/constraints-ast.ts +++ b/merlin-server/constraints-dsl-compiler/src/libs/constraints-ast.ts @@ -43,6 +43,7 @@ export enum NodeKind { ForEachActivitySpans = 'ForEachActivitySpans', ForEachActivityViolations = 'ForEachActivityViolations', ProfileChanges = 'ProfileChanges', + ProfileExpressionShiftBy = 'ProfileExpressionShiftBy', ViolationsOf = 'ViolationsOf', AbsoluteInterval = 'AbsoluteInterval', IntervalAlias = 'IntervalAlias', @@ -82,6 +83,7 @@ export type WindowsExpression = | WindowsExpressionStartOf | WindowsExpressionEndOf | ProfileChanges + | ProfileExpressionShiftBy | RealProfileLessThan | RealProfileLessThanOrEqual | RealProfileGreaterThan @@ -120,6 +122,12 @@ export interface ProfileChanges { expression: ProfileExpression; } +export interface ProfileExpressionShiftBy

{ + kind: NodeKind.ProfileExpressionShiftBy, + expression: P, + duration: Duration +} + export interface WindowsExpressionValue { kind: NodeKind.WindowsExpressionValue, value: boolean, @@ -266,7 +274,8 @@ export type RealProfileExpression = | RealProfileValue | RealProfileParameter | AssignGapsExpression - | RealProfileAccumulatedDuration; + | RealProfileAccumulatedDuration + | ProfileExpressionShiftBy; export interface StructProfileExpression { kind: NodeKind.StructProfileExpression, @@ -333,7 +342,8 @@ export type DiscreteProfileExpression = | StructProfileExpression | ListProfileExpression | ValueAtExpression - | IntervalDuration; + | IntervalDuration + | ProfileExpressionShiftBy; export interface DiscreteProfileResource { kind: NodeKind.DiscreteProfileResource; diff --git a/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts b/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts index 48fc9c63b9..c039b3c25e 100644 --- a/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts +++ b/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts @@ -226,13 +226,21 @@ export class Windows { * @param fromStart duration to add from the start of each true segment * @param fromEnd duration to add from the end of each true segment */ - public shiftBy(fromStart: AST.Duration, fromEnd: AST.Duration) : Windows { - return new Windows({ - kind: AST.NodeKind.WindowsExpressionShiftBy, - windowExpression: this.__astNode, - fromStart, - fromEnd - }) + public shiftBy(fromStart: AST.Duration, fromEnd?: AST.Duration | undefined) : Windows { + if (fromEnd === undefined) { + return new Windows({ + kind: AST.NodeKind.ProfileExpressionShiftBy, + expression: this.__astNode, + duration: fromStart + }); + } else { + return new Windows({ + kind: AST.NodeKind.WindowsExpressionShiftBy, + windowExpression: this.__astNode, + fromStart, + fromEnd + }) + } } /** @@ -707,6 +715,14 @@ export class Real { timepoint : timepoint.__astNode }); } + + public shiftBy(duration: Temporal.Duration): Real { + return new Real({ + kind: AST.NodeKind.ProfileExpressionShiftBy, + expression: this.__astNode, + duration + }) + } } /** @@ -868,6 +884,14 @@ export class Discrete { defaultProfile: defaultProfile.__astNode }); } + + public shiftBy(duration: Temporal.Duration): Discrete { + return new Discrete({ + kind: AST.NodeKind.ProfileExpressionShiftBy, + expression: this.__astNode, + duration + }) + } } /** Represents an instance of an activity in the plan. */ @@ -1071,7 +1095,7 @@ declare global { * @param fromStart duration to add from the start of each true segment * @param fromEnd duration to add from the end of each true segment */ - public shiftBy(fromStart: AST.Duration, fromEnd: AST.Duration): Windows; + public shiftBy(fromStart: AST.Duration, fromEnd?: AST.Duration | undefined): Windows; /** * Returns a new windows object, with all true segments shorter than or equal to the given @@ -1334,6 +1358,8 @@ declare global { * @param timepoint the timepoint, represented by a Spans (must be reduced to a single point) */ public valueAt(timepoint: Spans): Discrete; + + public shiftBy(duration: Temporal.Duration): Real; } /** @@ -1422,6 +1448,8 @@ declare global { * @param timepoint the timepoint, represented by a Spans (must be reduced to a single point) */ public valueAt(timepoint: Spans): Discrete; + + public shiftBy(duration: Temporal.Duration): Discrete; } /** An enum for whether an interval includes its bounds. */ diff --git a/merlin-server/src/test/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintsDSLCompilationServiceTests.java b/merlin-server/src/test/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintsDSLCompilationServiceTests.java index c667ab35c2..b8f469ca8e 100644 --- a/merlin-server/src/test/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintsDSLCompilationServiceTests.java +++ b/merlin-server/src/test/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintsDSLCompilationServiceTests.java @@ -31,7 +31,7 @@ import gov.nasa.jpl.aerie.constraints.tree.RealParameter; import gov.nasa.jpl.aerie.constraints.tree.RealResource; import gov.nasa.jpl.aerie.constraints.tree.RealValue; -import gov.nasa.jpl.aerie.constraints.tree.ShiftBy; +import gov.nasa.jpl.aerie.constraints.tree.ShiftWindowsEdges; import gov.nasa.jpl.aerie.constraints.tree.ShorterThan; import gov.nasa.jpl.aerie.constraints.tree.SpansFromWindows; import gov.nasa.jpl.aerie.constraints.tree.Split; @@ -486,7 +486,7 @@ export default() => { } @Test - void testShiftBy() { + void testShiftWindowsEdges() { checkSuccessfulCompilation( """ const minute = (m: number) => Temporal.Duration.from({minutes: m}); @@ -494,7 +494,7 @@ export default() => { return Real.Resource("state of charge").rate().equal(Real.Value(4.0)).shiftBy(minute(2), minute(-20)) } """, - new ViolationsOfWindows(new ShiftBy( + new ViolationsOfWindows(new ShiftWindowsEdges( 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))) @@ -1227,4 +1227,5 @@ export default () => { "TypeError: TS2345 Argument of type 'Discrete' is not assignable to parameter of type '\"Option1\" | \"Option2\" | Discrete<\"Option1\" | \"Option2\">'." ); } + } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/transformers/TransformerAfterEach.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/transformers/TransformerAfterEach.java index 07dad57f5b..2459d7ac53 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/transformers/TransformerAfterEach.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/transformers/TransformerAfterEach.java @@ -19,7 +19,7 @@ public Windows transformWindows(final Plan plan, final Windows windows, final Si var retWin = windows; retWin = retWin.not(); retWin = retWin.removeTrueSegment(0); - retWin = retWin.shiftBy(dur, Duration.ZERO); + retWin = retWin.shiftEdges(dur, Duration.ZERO); return retWin; } } diff --git a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/transformers/TransformerBeforeEach.java b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/transformers/TransformerBeforeEach.java index 493f07f25b..cafd1945a7 100644 --- a/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/transformers/TransformerBeforeEach.java +++ b/scheduler-driver/src/main/java/gov/nasa/jpl/aerie/scheduler/constraints/transformers/TransformerBeforeEach.java @@ -19,7 +19,7 @@ public Windows transformWindows(final Plan plan, final Windows windows, final Si var retWin = windows; retWin = retWin.not(); retWin = retWin.removeTrueSegment(-1); - retWin = retWin.shiftBy(Duration.ZERO, Duration.negate(dur)); + retWin = retWin.shiftEdges(Duration.ZERO, Duration.negate(dur)); return retWin; } } From d86d513d2945230396c1c3bf6ca140b66c34c734 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 28 Jun 2023 13:31:10 -0700 Subject: [PATCH 2/6] Add doc comments to shiftBy --- .../src/libs/constraints-edsl-fluent-api.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts b/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts index c039b3c25e..87e89e74b3 100644 --- a/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts +++ b/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts @@ -716,6 +716,11 @@ export class Real { }); } + /** + * Shifts the profile forward or backward in time. + * + * @param duration duration shift each segment (can be negative) + */ public shiftBy(duration: Temporal.Duration): Real { return new Real({ kind: AST.NodeKind.ProfileExpressionShiftBy, @@ -885,6 +890,11 @@ export class Discrete { }); } + /** + * Shifts the profile forward or backward in time. + * + * @param duration duration shift each segment (can be negative) + */ public shiftBy(duration: Temporal.Duration): Discrete { return new Discrete({ kind: AST.NodeKind.ProfileExpressionShiftBy, @@ -1359,6 +1369,11 @@ declare global { */ public valueAt(timepoint: Spans): Discrete; + /** + * Shifts the profile forward or backward in time. + * + * @param duration duration shift each segment (can be negative) + */ public shiftBy(duration: Temporal.Duration): Real; } @@ -1449,6 +1464,11 @@ declare global { */ public valueAt(timepoint: Spans): Discrete; + /** + * Shifts the profile forward or backward in time. + * + * @param duration duration shift each segment (can be negative) + */ public shiftBy(duration: Temporal.Duration): Discrete; } From ef060ed24ab4067811b8e649db176729d196732d Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 28 Jun 2023 13:18:36 -0700 Subject: [PATCH 3/6] Add shiftBy tests --- .../jpl/aerie/constraints/tree/ASTTests.java | 37 +++++++++++++++++++ ...ConstraintsDSLCompilationServiceTests.java | 28 ++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java b/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java index ee9a0ca0db..dd7a9a55f9 100644 --- a/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java +++ b/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java @@ -505,6 +505,25 @@ public void testDiscreteResource() { assertEquivalent(expected, result); } + + @Test + public void testDiscreteShiftBy() { + final var simResults = new SimulationResults( + Instant.EPOCH, Interval.between(0, 20, SECONDS), + List.of(), + Map.of(), + Map.of( + "discrete", new DiscreteProfile(Segment.of(Interval.between(1, 2, SECONDS), SerializedValue.of("much value"))) + ) + ); + + final var result = new ShiftBy<>(new DiscreteResource("discrete"), new DurationLiteral(Duration.of(1, SECONDS))).evaluate(simResults, new EvaluationEnvironment()); + + final var expected = new DiscreteProfile(Segment.of(Interval.between(2, 3, SECONDS), SerializedValue.of("much value"))); + + assertEquivalent(expected, result); + } + @Test public void testValueAt(){ final var simResults = new SimulationResults( @@ -621,6 +640,24 @@ public void testRealResourceOnNonexistentResource() { fail("Expected RealResource node to fail on non-existent resource"); } + @Test + public void testRealShiftBy() { + final var simResults = new SimulationResults( + Instant.EPOCH, Interval.between(0, 20, SECONDS), + List.of(), + Map.of( + "real", new LinearProfile(Segment.of(Interval.between(1, 2, SECONDS), new LinearEquation(Duration.of(1, SECONDS), 1, 1))) + ), + Map.of() + ); + + final var result = new ShiftBy<>(new RealResource("real"), new DurationLiteral(Duration.of(1, SECONDS))).evaluate(simResults, new EvaluationEnvironment()); + + final var expected = new LinearProfile(Segment.of(Interval.between(2, 3, SECONDS), new LinearEquation(Duration.of(2, SECONDS), 1, 1))); + + assertEquivalent(expected, result); + } + @Test public void testForEachActivity() { final var simResults = new SimulationResults( diff --git a/merlin-server/src/test/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintsDSLCompilationServiceTests.java b/merlin-server/src/test/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintsDSLCompilationServiceTests.java index b8f469ca8e..1e008e28f1 100644 --- a/merlin-server/src/test/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintsDSLCompilationServiceTests.java +++ b/merlin-server/src/test/java/gov/nasa/jpl/aerie/merlin/server/services/ConstraintsDSLCompilationServiceTests.java @@ -31,6 +31,7 @@ import gov.nasa.jpl.aerie.constraints.tree.RealParameter; import gov.nasa.jpl.aerie.constraints.tree.RealResource; import gov.nasa.jpl.aerie.constraints.tree.RealValue; +import gov.nasa.jpl.aerie.constraints.tree.ShiftBy; import gov.nasa.jpl.aerie.constraints.tree.ShiftWindowsEdges; import gov.nasa.jpl.aerie.constraints.tree.ShorterThan; import gov.nasa.jpl.aerie.constraints.tree.SpansFromWindows; @@ -1228,4 +1229,31 @@ export default () => { ); } + @Test + void testProfileShiftBy() { + checkSuccessfulCompilation( + """ + const minute = (m: number) => Temporal.Duration.from({minutes: m}); + export default() => { + return Real.Resource("state of charge").shiftBy(minute(2)).equal(Real.Value(4.0)) + } + """, + new ViolationsOfWindows( + new Equal<>(new ShiftBy<>(new RealResource("state of charge"), new DurationLiteral(Duration.of(2, Duration.MINUTE))), new RealValue(4.0)) + ) + ); + + checkSuccessfulCompilation( + """ + const minute = (m: number) => Temporal.Duration.from({minutes: m}); + export default() => { + return Discrete.Resource("mode").shiftBy(minute(2)).equal("Option1") + } + """, + new ViolationsOfWindows( + new Equal<>(new ShiftBy<>(new DiscreteResource("mode"), new DurationLiteral(Duration.of(2, Duration.MINUTE))), new DiscreteValue(SerializedValue.of("Option1"))) + ) + ); + } + } From 624d1f54268648c3d5da752f0e09c0b0f0716c67 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 28 Jun 2023 13:52:16 -0700 Subject: [PATCH 4/6] Shift bounds to avoid unnecessary gaps in profile evaluation --- .../jpl/aerie/constraints/tree/ActivityWindow.java | 2 +- .../aerie/constraints/tree/DiscreteParameter.java | 2 +- .../tree/DiscreteProfileFromDuration.java | 2 +- .../gov/nasa/jpl/aerie/constraints/tree/EndOf.java | 2 +- .../nasa/jpl/aerie/constraints/tree/ShiftBy.java | 5 ++++- .../aerie/constraints/tree/ShiftWindowsEdges.java | 14 ++++++++++++-- .../nasa/jpl/aerie/constraints/tree/StartOf.java | 2 +- .../nasa/jpl/aerie/constraints/tree/ASTTests.java | 4 ++-- 8 files changed, 23 insertions(+), 10 deletions(-) diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ActivityWindow.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ActivityWindow.java index 9a566eebfa..1ad673d159 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ActivityWindow.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ActivityWindow.java @@ -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) ); } diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/DiscreteParameter.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/DiscreteParameter.java index 10409e5a1b..4c336c4219 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/DiscreteParameter.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/DiscreteParameter.java @@ -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)) ); } diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/DiscreteProfileFromDuration.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/DiscreteProfileFromDuration.java index dce82b04f9..0409afbc3f 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/DiscreteProfileFromDuration.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/DiscreteProfileFromDuration.java @@ -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 diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/EndOf.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/EndOf.java index 77654d0967..1fd684bcd2 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/EndOf.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/EndOf.java @@ -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) ); } diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftBy.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftBy.java index 2db8a1a1f6..903e9edf3b 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftBy.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftBy.java @@ -14,9 +14,12 @@ public record ShiftBy

>( @Override public P evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) { - final var originalProfile = this.expression.evaluate(results, bounds, 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); } diff --git a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftWindowsEdges.java b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftWindowsEdges.java index 020a9ed560..0db61054bc 100644 --- a/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftWindowsEdges.java +++ b/constraints/src/main/java/gov/nasa/jpl/aerie/constraints/tree/ShiftWindowsEdges.java @@ -22,8 +22,18 @@ public ShiftWindowsEdges(final Expression left, final Expression Date: Wed, 28 Jun 2023 13:52:44 -0700 Subject: [PATCH 5/6] Update tests for bounds shifting behavior --- .../jpl/aerie/constraints/tree/ASTTests.java | 73 ++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java b/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java index dece8bca8c..b5dc5e5bdd 100644 --- a/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java +++ b/constraints/src/test/java/gov/nasa/jpl/aerie/constraints/tree/ASTTests.java @@ -359,7 +359,7 @@ public void testOr() { @Test public void testExpandBy() { final var simResults = new SimulationResults( - Instant.EPOCH, Interval.between(0, 20, SECONDS), + Instant.EPOCH, Interval.between(-100, 200, SECONDS), List.of(), Map.of(), Map.of() @@ -391,7 +391,7 @@ public void testExpandBy() { @Test public void testShrink() { final var simResults = new SimulationResults( - Instant.EPOCH, Interval.between(0, 20, SECONDS), + Instant.EPOCH, Interval.between(-100, 200, SECONDS), List.of(), Map.of(), Map.of() @@ -1054,6 +1054,75 @@ public void testSpansFromInterval() { assertEquals(expected, result); } + @Test + public void testShiftByBoundsAdjustment() { + final var simResults = new SimulationResults( + Instant.EPOCH, Interval.between(0, 20, SECONDS), + List.of(), + Map.of(), + Map.of() + ); + + final var expression = new ShiftBy<>( + new DiscreteValue(SerializedValue.of("strang")), + Supplier.of(Duration.of(10, SECONDS)) + ); + + final var result = expression.evaluate(simResults); + + final var expected = new DiscreteProfile( + Segment.of(Interval.between(0, 20, SECONDS), SerializedValue.of("strang")) + ); + + assertIterableEquals(expected, result); + } + + @Test + public void testShiftWindowsEdgesBoundsAdjustment() { + final var simResults = new SimulationResults( + Instant.EPOCH, Interval.between(0, 20, SECONDS), + List.of(), + Map.of(), + Map.of() + ); + + final var crossingStartOfPlan = new Windows(false).set(Interval.between(-1, 1, SECONDS), true); + + final var result1 = new ShiftWindowsEdges( + 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( + Supplier.of(crossingStartOfPlan), + Supplier.of(Duration.of(-10, SECONDS)), + Supplier.of(Duration.ZERO) + ).evaluate(simResults); + final var expected2 = new Windows(false).set(Interval.between(0, 1, SECONDS), true).select(simResults.bounds); + assertIterableEquals(expected2, result2); + + final var crossingEndOfPlan = new Windows(false).set(Interval.between(19, 21, SECONDS), true); + + final var result3 = new ShiftWindowsEdges( + 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( + Supplier.of(crossingEndOfPlan), + Supplier.of(Duration.of(-10, SECONDS)), + Supplier.of(Duration.ZERO) + ).evaluate(simResults); + final var expected4 = new Windows(false).set(Interval.between(9, 21, SECONDS), true).select(simResults.bounds); + assertIterableEquals(expected2, result2); + } + private static final class Supplier implements Expression { private final T value; From 5351d2fe680167aa44681e55af3f573484ea8ea3 Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Wed, 5 Jul 2023 14:25:40 -0700 Subject: [PATCH 6/6] Update doc comment for windows.shiftBy --- .../src/libs/constraints-edsl-fluent-api.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts b/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts index 87e89e74b3..472c18c09d 100644 --- a/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts +++ b/merlin-server/constraints-dsl-compiler/src/libs/constraints-edsl-fluent-api.ts @@ -220,11 +220,14 @@ export class Windows { /** * Shifts the start and end of all true segments by a duration. * - * Shifts the start and end of all false segment by the opposite directions (i.e. the start of each false segment + * The second argument is optional: if omitted, `shiftBy(dur)` shifts all segments uniformly by `dur`, which + * is equivalent to `shiftBy(dur, dur)`. + * + * Shifts the start and end of all false segment by the reversed directions (i.e. the start of each false segment * is shifted by `fromEnd`). * * @param fromStart duration to add from the start of each true segment - * @param fromEnd duration to add from the end of each true segment + * @param fromEnd duration to add from the end of each true segment. Default is equal to `fromStart` if omitted. */ public shiftBy(fromStart: AST.Duration, fromEnd?: AST.Duration | undefined) : Windows { if (fromEnd === undefined) { @@ -1099,11 +1102,14 @@ declare global { /** * Shifts the start and end of all true segments by a duration. * - * Shifts the start and end of all false segment by the opposite directions (i.e. the start of each false segment + * The second argument is optional: if omitted, `shiftBy(dur)` shifts all segments uniformly by `dur`, which + * is equivalent to `shiftBy(dur, dur)`. + * + * Shifts the start and end of all false segment by the reversed directions (i.e. the start of each false segment * is shifted by `fromEnd`). * * @param fromStart duration to add from the start of each true segment - * @param fromEnd duration to add from the end of each true segment + * @param fromEnd duration to add from the end of each true segment. Default is equal to `fromStart` if omitted. */ public shiftBy(fromStart: AST.Duration, fromEnd?: AST.Duration | undefined): Windows;