/// <summary> /// This method does all the work of converting literal indexing expressions. /// </summary> /// <param name="iaie"></param> /// <returns></returns> protected override IExpression ConvertArrayIndexer(IArrayIndexerExpression iaie) { IndexAnalysisTransform.IndexInfo info; if (!analysis.indexInfoOf.TryGetValue(iaie, out info)) { return(base.ConvertArrayIndexer(iaie)); } // Determine if this is a definition i.e. the variable is on the left hand side of an assignment // This must be done before base.ConvertArrayIndexer changes the expression! bool isDef = Recognizer.IsBeingMutated(context, iaie); if (info.clone != null) { if (isDef) { // check that extra literal indices in the target are zero. // for example, if iae is x[i][0] = (...) then it is safe to add x_uses[i] = Rep(x[i]) // if iae is x[i][1] = (...) then it is safe to add x_uses[i][1] = Rep(x[i][1]) // but not x_uses[i] = Rep(x[i]) since this will be a duplicate. bool extraLiteralsAreZero = true; int parentIndex = context.InputStack.Count - 2; object parent = context.GetAncestor(parentIndex); while (parent is IArrayIndexerExpression) { IArrayIndexerExpression parent_iaie = (IArrayIndexerExpression)parent; foreach (IExpression index in parent_iaie.Indices) { if (index is ILiteralExpression) { int value = (int)((ILiteralExpression)index).Value; if (value != 0) { extraLiteralsAreZero = false; break; } } } parentIndex--; parent = context.GetAncestor(parentIndex); } if (false && extraLiteralsAreZero) { // change: // array[0] = f() // into: // array_item0 = f() // array[0] = Copy(array_item0) IExpression copy = Builder.StaticGenericMethod(new Func <PlaceHolder, PlaceHolder>(Clone.Copy <PlaceHolder>), new Type[] { iaie.GetExpressionType() }, info.clone); IStatement copySt = Builder.AssignStmt(iaie, copy); context.AddStatementAfterCurrent(copySt); } } return(info.clone); } if (isDef) { // do not clone the lhs of an array create assignment. IAssignExpression assignExpr = context.FindAncestor <IAssignExpression>(); if (assignExpr.Expression is IArrayCreateExpression) { return(iaie); } } IVariableDeclaration originalBaseVar = Recognizer.GetVariableDeclaration(iaie); // If the variable is not stochastic, return if (!CodeRecognizer.IsStochastic(context, originalBaseVar)) { return(iaie); } IExpression newExpr = null; IVariableDeclaration baseVar = originalBaseVar; IVariableDeclaration newvd = null; IExpression rhsExpr = null; Containers containers = info.containers; Type tp = iaie.GetExpressionType(); if (tp == null) { Error("Could not determine type of expression: " + iaie); return(iaie); } var stmts = Builder.StmtCollection(); var stmtsAfter = Builder.StmtCollection(); // does the expression have the form array[indices[k]][indices2[k]][indices3[k]]? if (newvd == null && UseGetItems && iaie.Target is IArrayIndexerExpression && iaie.Indices.Count == 1 && iaie.Indices[0] is IArrayIndexerExpression) { IArrayIndexerExpression index3 = (IArrayIndexerExpression)iaie.Indices[0]; IArrayIndexerExpression iaie2 = (IArrayIndexerExpression)iaie.Target; if (index3.Indices.Count == 1 && index3.Indices[0] is IVariableReferenceExpression && iaie2.Target is IArrayIndexerExpression && iaie2.Indices.Count == 1 && iaie2.Indices[0] is IArrayIndexerExpression) { IArrayIndexerExpression index2 = (IArrayIndexerExpression)iaie2.Indices[0]; IArrayIndexerExpression iaie3 = (IArrayIndexerExpression)iaie2.Target; if (index2.Indices.Count == 1 && index2.Indices[0] is IVariableReferenceExpression && iaie3.Indices.Count == 1 && iaie3.Indices[0] is IArrayIndexerExpression) { IArrayIndexerExpression index = (IArrayIndexerExpression)iaie3.Indices[0]; IVariableReferenceExpression innerIndex = (IVariableReferenceExpression)index.Indices[0]; IForStatement innerLoop = Recognizer.GetLoopForVariable(context, innerIndex); if (index.Indices.Count == 1 && index2.Indices[0].Equals(innerIndex) && index3.Indices[0].Equals(innerIndex) && innerLoop != null && AreLoopsDisjoint(innerLoop, iaie3.Target, index.Target)) { // expression has the form array[indices[k]][indices2[k]][indices3[k]] if (isDef) { Error("fancy indexing not allowed on left hand side"); return(iaie); } WarnIfLocal(index.Target, iaie3.Target, iaie); WarnIfLocal(index2.Target, iaie3.Target, iaie); WarnIfLocal(index3.Target, iaie3.Target, iaie); containers = RemoveReferencesTo(containers, innerIndex); IExpression loopSize = Recognizer.LoopSizeExpression(innerLoop); var indices = Recognizer.GetIndices(iaie); // Build name of replacement variable from index values StringBuilder sb = new StringBuilder("_item"); AppendIndexString(sb, iaie3); AppendIndexString(sb, iaie2); AppendIndexString(sb, iaie); string name = ToString(iaie3.Target) + sb.ToString(); VariableInformation varInfo = VariableInformation.GetVariableInformation(context, baseVar); newvd = varInfo.DeriveArrayVariable(stmts, context, name, loopSize, Recognizer.GetVariableDeclaration(innerIndex), indices); if (!context.InputAttributes.Has <DerivedVariable>(newvd)) { context.InputAttributes.Set(newvd, new DerivedVariable()); } IExpression getItems = Builder.StaticGenericMethod(new Func <IReadOnlyList <IReadOnlyList <IReadOnlyList <PlaceHolder> > >, IReadOnlyList <int>, IReadOnlyList <int>, IReadOnlyList <int>, PlaceHolder[]>(Collection.GetItemsFromDeepJagged), new Type[] { tp }, iaie3.Target, index.Target, index2.Target, index3.Target); context.InputAttributes.CopyObjectAttributesTo <Algorithm>(baseVar, context.OutputAttributes, getItems); stmts.Add(Builder.AssignStmt(Builder.VarRefExpr(newvd), getItems)); newExpr = Builder.ArrayIndex(Builder.VarRefExpr(newvd), innerIndex); rhsExpr = getItems; } } } } // does the expression have the form array[indices[k]][indices2[k]]? if (newvd == null && UseGetItems && iaie.Target is IArrayIndexerExpression && iaie.Indices.Count == 1 && iaie.Indices[0] is IArrayIndexerExpression) { IArrayIndexerExpression index2 = (IArrayIndexerExpression)iaie.Indices[0]; IArrayIndexerExpression target = (IArrayIndexerExpression)iaie.Target; if (index2.Indices.Count == 1 && index2.Indices[0] is IVariableReferenceExpression && target.Indices.Count == 1 && target.Indices[0] is IArrayIndexerExpression) { IVariableReferenceExpression innerIndex = (IVariableReferenceExpression)index2.Indices[0]; IArrayIndexerExpression index = (IArrayIndexerExpression)target.Indices[0]; IForStatement innerLoop = Recognizer.GetLoopForVariable(context, innerIndex); if (index.Indices.Count == 1 && index.Indices[0].Equals(innerIndex) && innerLoop != null && AreLoopsDisjoint(innerLoop, target.Target, index.Target)) { // expression has the form array[indices[k]][indices2[k]] if (isDef) { Error("fancy indexing not allowed on left hand side"); return(iaie); } var innerLoops = new List <IForStatement>(); innerLoops.Add(innerLoop); var indexTarget = index.Target; var index2Target = index2.Target; // check if the index array is jagged, i.e. array[indices[k][j]] while (indexTarget is IArrayIndexerExpression && index2Target is IArrayIndexerExpression) { IArrayIndexerExpression indexTargetExpr = (IArrayIndexerExpression)indexTarget; IArrayIndexerExpression index2TargetExpr = (IArrayIndexerExpression)index2Target; if (indexTargetExpr.Indices.Count == 1 && indexTargetExpr.Indices[0] is IVariableReferenceExpression && index2TargetExpr.Indices.Count == 1 && index2TargetExpr.Indices[0] is IVariableReferenceExpression) { IVariableReferenceExpression innerIndexTarget = (IVariableReferenceExpression)indexTargetExpr.Indices[0]; IVariableReferenceExpression innerIndex2Target = (IVariableReferenceExpression)index2TargetExpr.Indices[0]; IForStatement indexTargetLoop = Recognizer.GetLoopForVariable(context, innerIndexTarget); if (indexTargetLoop != null && AreLoopsDisjoint(indexTargetLoop, target.Target, indexTargetExpr.Target) && innerIndexTarget.Equals(innerIndex2Target)) { innerLoops.Add(indexTargetLoop); indexTarget = indexTargetExpr.Target; index2Target = index2TargetExpr.Target; } else { break; } } else { break; } } WarnIfLocal(indexTarget, target.Target, iaie); WarnIfLocal(index2Target, target.Target, iaie); innerLoops.Reverse(); var loopSizes = innerLoops.ListSelect(ifs => new[] { Recognizer.LoopSizeExpression(ifs) }); var newIndexVars = innerLoops.ListSelect(ifs => new[] { Recognizer.LoopVariable(ifs) }); // Build name of replacement variable from index values StringBuilder sb = new StringBuilder("_item"); AppendIndexString(sb, target); AppendIndexString(sb, iaie); string name = ToString(target.Target) + sb.ToString(); VariableInformation varInfo = VariableInformation.GetVariableInformation(context, baseVar); var indices = Recognizer.GetIndices(iaie); newvd = varInfo.DeriveArrayVariable(stmts, context, name, loopSizes, newIndexVars, indices); if (!context.InputAttributes.Has <DerivedVariable>(newvd)) { context.InputAttributes.Set(newvd, new DerivedVariable()); } IExpression getItems; if (innerLoops.Count == 1) { getItems = Builder.StaticGenericMethod(new Func <IReadOnlyList <IReadOnlyList <PlaceHolder> >, IReadOnlyList <int>, IReadOnlyList <int>, PlaceHolder[]>(Collection.GetItemsFromJagged), new Type[] { tp }, target.Target, indexTarget, index2Target); } else if (innerLoops.Count == 2) { getItems = Builder.StaticGenericMethod(new Func <IReadOnlyList <IReadOnlyList <PlaceHolder> >, IReadOnlyList <IReadOnlyList <int> >, IReadOnlyList <IReadOnlyList <int> >, PlaceHolder[][]>(Collection.GetJaggedItemsFromJagged), new Type[] { tp }, target.Target, indexTarget, index2Target); } else { throw new NotImplementedException($"innerLoops.Count = {innerLoops.Count}"); } context.InputAttributes.CopyObjectAttributesTo <Algorithm>(baseVar, context.OutputAttributes, getItems); stmts.Add(Builder.AssignStmt(Builder.VarRefExpr(newvd), getItems)); var newIndices = newIndexVars.ListSelect(ivds => Util.ArrayInit(ivds.Length, i => Builder.VarRefExpr(ivds[i]))); newExpr = Builder.JaggedArrayIndex(Builder.VarRefExpr(newvd), newIndices); rhsExpr = getItems; } else if (HasAnyCommonLoops(index, index2)) { Warning($"This model will consume excess memory due to the indexing expression {iaie} since {index} and {index2} have larger depth than the compiler can handle."); } } } if (newvd == null) { IArrayIndexerExpression originalExpr = iaie; if (UseGetItems) { iaie = (IArrayIndexerExpression)base.ConvertArrayIndexer(iaie); } if (!object.ReferenceEquals(iaie.Target, originalExpr.Target) && false) { // TODO: determine if this warning is useful or not string warningText = "This model may consume excess memory due to the jagged indexing expression {0}"; Warning(string.Format(warningText, originalExpr)); } // get the baseVar of the new expression. baseVar = Recognizer.GetVariableDeclaration(iaie); VariableInformation varInfo = VariableInformation.GetVariableInformation(context, baseVar); var indices = Recognizer.GetIndices(iaie); // Build name of replacement variable from index values StringBuilder sb = new StringBuilder("_item"); AppendIndexString(sb, iaie); string name = ToString(iaie.Target) + sb.ToString(); // does the expression have the form array[indices[k]]? if (UseGetItems && iaie.Indices.Count == 1 && iaie.Indices[0] is IArrayIndexerExpression) { IArrayIndexerExpression index = (IArrayIndexerExpression)iaie.Indices[0]; if (index.Indices.Count == 1 && index.Indices[0] is IVariableReferenceExpression) { // expression has the form array[indices[k]] IVariableReferenceExpression innerIndex = (IVariableReferenceExpression)index.Indices[0]; IForStatement innerLoop = Recognizer.GetLoopForVariable(context, innerIndex); if (innerLoop != null && AreLoopsDisjoint(innerLoop, iaie.Target, index.Target)) { if (isDef) { Error("fancy indexing not allowed on left hand side"); return(iaie); } var innerLoops = new List <IForStatement>(); innerLoops.Add(innerLoop); var indexTarget = index.Target; // check if the index array is jagged, i.e. array[indices[k][j]] while (indexTarget is IArrayIndexerExpression) { IArrayIndexerExpression index2 = (IArrayIndexerExpression)indexTarget; if (index2.Indices.Count == 1 && index2.Indices[0] is IVariableReferenceExpression) { IVariableReferenceExpression innerIndex2 = (IVariableReferenceExpression)index2.Indices[0]; IForStatement innerLoop2 = Recognizer.GetLoopForVariable(context, innerIndex2); if (innerLoop2 != null && AreLoopsDisjoint(innerLoop2, iaie.Target, index2.Target)) { innerLoops.Add(innerLoop2); indexTarget = index2.Target; // This limit must match the number of handled cases below. if (innerLoops.Count == 3) { break; } } else { break; } } else { break; } } WarnIfLocal(indexTarget, iaie.Target, originalExpr); innerLoops.Reverse(); var loopSizes = innerLoops.ListSelect(ifs => new[] { Recognizer.LoopSizeExpression(ifs) }); var newIndexVars = innerLoops.ListSelect(ifs => new[] { Recognizer.LoopVariable(ifs) }); newvd = varInfo.DeriveArrayVariable(stmts, context, name, loopSizes, newIndexVars, indices); if (!context.InputAttributes.Has <DerivedVariable>(newvd)) { context.InputAttributes.Set(newvd, new DerivedVariable()); } IExpression getItems; if (innerLoops.Count == 1) { getItems = Builder.StaticGenericMethod(new Func <IReadOnlyList <PlaceHolder>, IReadOnlyList <int>, PlaceHolder[]>(Collection.GetItems), new Type[] { tp }, iaie.Target, indexTarget); } else if (innerLoops.Count == 2) { getItems = Builder.StaticGenericMethod(new Func <IReadOnlyList <PlaceHolder>, IReadOnlyList <IReadOnlyList <int> >, PlaceHolder[][]>(Collection.GetJaggedItems), new Type[] { tp }, iaie.Target, indexTarget); } else if (innerLoops.Count == 3) { getItems = Builder.StaticGenericMethod(new Func <IReadOnlyList <PlaceHolder>, IReadOnlyList <IReadOnlyList <IReadOnlyList <int> > >, PlaceHolder[][][]>(Collection.GetDeepJaggedItems), new Type[] { tp }, iaie.Target, indexTarget); } else { throw new NotImplementedException($"innerLoops.Count = {innerLoops.Count}"); } context.InputAttributes.CopyObjectAttributesTo <Algorithm>(baseVar, context.OutputAttributes, getItems); stmts.Add(Builder.AssignStmt(Builder.VarRefExpr(newvd), getItems)); var newIndices = newIndexVars.ListSelect(ivds => Util.ArrayInit(ivds.Length, i => Builder.VarRefExpr(ivds[i]))); newExpr = Builder.JaggedArrayIndex(Builder.VarRefExpr(newvd), newIndices); rhsExpr = getItems; } } } if (newvd == null) { if (UseGetItems && info.count < 2) { return(iaie); } try { newvd = varInfo.DeriveIndexedVariable(stmts, context, name, indices, copyInitializer: isDef); } catch (Exception ex) { Error(ex.Message, ex); return(iaie); } context.OutputAttributes.Remove <DerivedVariable>(newvd); newExpr = Builder.VarRefExpr(newvd); rhsExpr = iaie; if (isDef) { // change: // array[0] = f() // into: // array_item0 = f() // array[0] = Copy(array_item0) IExpression copy = Builder.StaticGenericMethod(new Func <PlaceHolder, PlaceHolder>(Clone.Copy), new Type[] { tp }, newExpr); IStatement copySt = Builder.AssignStmt(iaie, copy); stmtsAfter.Add(copySt); if (!context.InputAttributes.Has <DerivedVariable>(baseVar)) { context.InputAttributes.Set(baseVar, new DerivedVariable()); } } else if (!info.IsAssignedTo) { // change: // x = f(array[0]) // into: // array_item0 = Copy(array[0]) // x = f(array_item0) IExpression copy = Builder.StaticGenericMethod(new Func <PlaceHolder, PlaceHolder>(Clone.Copy), new Type[] { tp }, iaie); IStatement copySt = Builder.AssignStmt(Builder.VarRefExpr(newvd), copy); //if (attr != null) context.OutputAttributes.Set(copySt, attr); stmts.Add(copySt); context.InputAttributes.Set(newvd, new DerivedVariable()); } } } // Reduce memory consumption by declaring the clone outside of unnecessary loops. // This way, the item is cloned outside the loop and then replicated, instead of replicating the entire array and cloning the item. containers = Containers.RemoveUnusedLoops(containers, context, rhsExpr); if (context.InputAttributes.Has <DoNotSendEvidence>(originalBaseVar)) { containers = Containers.RemoveStochasticConditionals(containers, context); } if (true) { IStatement st = GetBindingSetContainer(FilterBindingSet(info.bindings, binding => Containers.ContainsExpression(containers.inputs, context, binding.GetExpression()))); if (st != null) { containers.Add(st); } } // To put the declaration in the desired containers, we find an ancestor which includes as many of the containers as possible, // then wrap the declaration with the remaining containers. int ancIndex = containers.GetMatchingAncestorIndex(context); Containers missing = containers.GetContainersNotInContext(context, ancIndex); stmts = Containers.WrapWithContainers(stmts, missing.outputs); context.AddStatementsBeforeAncestorIndex(ancIndex, stmts); stmtsAfter = Containers.WrapWithContainers(stmtsAfter, missing.outputs); context.AddStatementsAfterAncestorIndex(ancIndex, stmtsAfter); context.InputAttributes.Set(newvd, containers); info.clone = newExpr; return(newExpr); }