Click here to Skip to main content
15,867,756 members
Articles / Programming Languages / C# 4.0

Parameter Substitution within Expression Trees

Rate me:
Please Sign up or sign in to vote.
4.95/5 (27 votes)
6 Jan 2011CPOL6 min read 53K   409   26   12
This article describes how to substitute a parameter within an Expression tree by another expression, similar to how one can substitute an argument within a mathematical function by a different function

Introduction

This article describes a method to transform a System.Linq.Expression object by substituting some of its parameters by different expressions. This technique can be applied, e.g., when one uses lambda expressions to describe mathematical functions - it will allow generating new functions by argument substitution technique. Correspondingly, it can be useful in creating plots, 2-D and 3-D graphics.

This article will only show how to apply this technique to generating new functions and creating Silverlight plots of these functions. A future article will describe applying this technique to 2-D graphics and animations.

Background

There is a very common problem in mathematics. Suppose, we have a function y = f(x). There is also another function x = g(t); we can combine these two functions to obtain dependency y = (f*g)(t) where f*g is a combined function.

This problem can be generalized for functions with multiple arguments: suppose we have a function y = F(x<sub>1</sub>, x<sub>2</sub>, ..., x<sub>n</sub>). Suppose there is also a function that makes one of its arguments xj dependent on a set of other arguments: x<sub>j</sub> = G(t<sub>1</sub>, ..., t<sub>m</sub>). The functions F and G can be combined into the function (F*G) operating on the same arguments as function F, except that argument xj will be replaced by t<sub>1</sub>, ..., t<sub>m</sub>:
y = (F*G)(x<sub>1</sub>, ..., x<sub>j-1</sub>, t<sub>1</sub>, ..., t<sub>m</sub>, x<sub>j+1</sub>, ... ,x<sub>n</sub>).

In .NET, mathematical functions can be described by lambda expressions or corresponding expression trees. The problem we are trying to solve is best shown by an example: suppose we have an expression corresponding to e.g. some polynomial:

C#
Expression<Func<double, double>> polynomialExpression =
  x => x*x*x*x + 3*x*x*x + 5*x*x + 10*x + 20;

Suppose we want to create and use other expressions that represent the arbitrary shifts of the expression above along variable x. In a straightforward way, we can simply create another expression dependent on 2 arguments: x and shift:

C#
Expression<Func<double, double>> polynomialExpressionWithShifts =
  (x, shift) =>
      (x-shift)*(x-shift)*(x-shift)*(x-shift) +
      3*(x-shift)*(x-shift)*(x-shift) +
      5*(x-shift)*(x-shift) +
      10*(x-shift) +
      20;

This however is, not a very pleasant expression to write. Furthermore, if more degrees of freedom are required, e.g., we need to have arbitrary dilations (making the function thinner or wider along axis x) in addition to arbitrary shifts, the expression will become increasingly more complex.

Using the approach described in this article, all one has to do is to write the following code in order to obtain the polynomialExpressionWithShifts:

C#
Expression<Func<double, double>> shiftExpression = (x, shift) => x - shift
Expression<Func<double, double>> polynomialExpressionWithShifts =
      (Expression<Func<double, double>>)polynomialExpression.Substitute
  ("x", shiftExpression);

The extension method Substitute will replace the ParameterExpression x in polynomialExpression with ParameterExpressions x and shift and modify the code of the expression replacing any occurrence of variable 'x' with 'x - shift' (the body of shiftExpression).

To add the dilation functionality, all we have to do is to write the following code:

C#
Expression<Func<double, double, double>> dilationAndShiftExpression =
          (x, dilationFactor, shift) = x * dilationFactor - shift
Expression<Func<double, double, double>> polynomialExpressionWithShifts =
      (Expression<Func<double, double, double>>)polynomialExpression.Substitute
  ("x", dilationAndShiftExpression);

The approach is generic enough to handle multiple variables also in the original expression. This is the reason for specifying the variable name in the extension function Substitute - we only replace a variable whose name is passed to the Expression as one of its parameters.

Using the Code

The attached code consists of the following four projects:

  1. ExpressionModifier - Contains the core functionality - it produces .NET 4.0 DLL
  2. ExpressionModifierTester - Contains Microsoft tests for ExpressionModifier functionality
  3. SilverlightExpressionModifier - The same as ExpressionModifier, but it is a Silverlight 4.0 project
  4. SimplePlots - Silverlight 4.0 project that plots different expressions - its purpose is to give some visual examples of parameter substitutions.

The usage examples can be taken from SimplePlot and ExpressionModifierTester projects.

One should set SimplePlot as the startup project of the solution.

The substitution within SimplePlot project takes place within the function MainPage.PlotMainAndModified:

C#
public void PlotMainAndModified
(
    Expression<Func<double, double>> mainExpression, 
    Expression<Func<double, double>> substExpression, 
    double delta = 0.1, 
    double pointSize = POINT_SIZE
)
{
    AddPlot(Colors.Red, delta, mainExpression.Compile(), pointSize);

    LambdaExpression modifiedExpression =
          mainExpression.Substitute("x", substExpression);

AddPlot
(
	Colors.Blue, 
	delta, 
	modifiedExpression.Compile() as Func<double, double>,
	pointSize
);
}

This function plots the original function in red and the resulting function in blue.

Make sure that the sinusoidal example lines are uncommented at the end of MainPage.MainPage constructor while the parabola example lines are commented out as below:

C#
// uncomment the two lines below for a sinusoidal example
PlotMainAndModified(x => Math.Sin(-x), x => x * 2 + 3, 0.01, 10d);
AddPlot(Colors.Yellow, 0.01, x =>Math.Sin(-(x * 2 + 3)), 3d);

/*
// uncomment the two lines below for a parabola example
PlotMainAndModified(x => x * x, x => x * x - 2, 0.01, 10d);
AddPlot(Colors.Yellow, 0.01, x => (x * x - 2) * (x * x - 2), 3d);
*/

Rebuilding and running SimplePlot project will then result in the following plot:

Image 1

The original function sin(-x) is shown in red. The function that was a result of substituting 2x + 3 in place of x into the original function is shown in blue. Finally, we plot the combined function sin(-(2x + 3)) in a thinner yellow to show that it does match the result of the substitution (in thicker blue).

Now, if we comment out the two lines related to the sinusoidal example and uncomment the lines related to a parabola example as shown below:

C#
/*
// uncomment the two lines below for a sinusoidal example
PlotMainAndModified(x => Math.Sin(-x), x => x * 2 + 3, 0.01, 10d);
AddPlot(Colors.Yellow, 0.01, x =>Math.Sin(-(x * 2 + 3)), 3d);
*/

// uncomment the two lines below for a parabola example
PlotMainAndModified(x => x * x, x => x * x - 2, 0.01, 10d);
AddPlot(Colors.Yellow, 0.01, x => (x * x - 2) * (x * x - 2), 3d);

and then rebuild and re-run the project, we'll have the following plot displayed:

Image 2

As in the first example, the original parabola in red corresponds to x2 function. Thick blue line is a plot of the result of substituting x<sup>2</sup> - 2 for x into the first function. Thin yellow is a plot of the (x<sup>2</sup> - 2)<sup>2</sup> function just to show that it matches the result of the substitution.

ExpressionModifierTester project can also be used to learn how to use the substitution functionality. Below the code for TestSimpleFunction is shown:

C#
[TestMethod]
public void TestSimpleFunction()
{
    Expression<Func<double, double>> 
    	originalExpression = t => t * t * t + 20;

    Expression<Func<double, double>> substituteExpression = t => t * t;

    LambdaExpression modifiedExpression = 
        originalExpression.Substitute("t", substituteExpression);

    Func<double, double> modifiedFunction =
               (Func<double, double>) modifiedExpression.Compile();
 
    Assert.AreEqual((int) modifiedFunction(1), 21);
    Assert.AreEqual((int) modifiedFunction(2), 2 * 2 * 2 * 2 * 2 * 2 + 20);
}

One can see that our original expression is t<sup>3</sup> + 20. We substitute t<sup>2</sup> for t. The resulting expression corresponds to t<sup>6</sup> + 20. Then we test that the resulting expression indeed produces correct values.

TestComplexPolynomial has similar functionality except that its original function is much more complex.

Review of the Core Substitution Functionality

The core functionality is located within two files: ExpressionVariableSubstituteHelper.cs and SubstExpressionVisitor.cs which contain classes that have the same names. The files are located under ExpressionModifier project. There are links to the same files under SilverlightExpressionModifier project.

Static class ExpressionVariableSubstituteHelper contains the Substitute extension function. It finds the ParameterExpression corresponding to the parameter to be substituted within the main function (it throws an exception if it cannot find such parameter by name). It also does some other checks, e.g. it checks that with the possibly expanded list of parameters after substitution, there will be no name clashes. Then it calls the Visit function of SubstExpressionVisitor class to actually create the resulting Expression tree.

SubstExpressionVisitor class is derived from ExpressionVisitor with three functions overridden: VisitUnary, VisitMethodCall and VisitBinary. Perhaps I overlooked some other Expressions that might have ParameterExpression objects as children and then, perhaps, the code might break on some complex Expression, but most of the Expressions seem to be covered by the above three functions. Each of the above three functions is implemented to check if one or more of its children is a ParameterExpression that we are substituting, and if it is, it returns a new Expression of the same type that replaces that ParameterExpression children with the body of the substituting Expression.

Points of Interest

This new way of manipulating the Expression trees can become very helpful in math intensive projects e.g. in 2-D and 3-D graphics and animations. This article described the application of this approach to generating plots for different functions. My next article will use this approach for 2-D animations along arbitrary paths.

History

  • 6th January, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect AWebPros
United States United States
I am a software architect and a developer with great passion for new engineering solutions and finding and applying design patterns.

I am passionate about learning new ways of building software and sharing my knowledge with others.

I worked with many various languages including C#, Java and C++.

I fell in love with WPF (and later Silverlight) at first sight. After Microsoft killed Silverlight, I was distraught until I found Avalonia - a great multiplatform package for building UI on Windows, Linux, Mac as well as within browsers (using WASM) and for mobile platforms.

I have my Ph.D. from RPI.

here is my linkedin profile

Comments and Discussions

 
QuestionMy vote of 5 Pin
Filip D'haene8-Jun-12 7:43
Filip D'haene8-Jun-12 7:43 
GeneralMy vote of 5 Pin
thatraja9-Jan-12 20:35
professionalthatraja9-Jan-12 20:35 
GeneralMy vote of 5 Pin
MDNadeemAkhter12-Jan-11 20:35
MDNadeemAkhter12-Jan-11 20:35 
Awesome article
GeneralRe: My vote of 5 Pin
Nick Polyak13-Jan-11 6:45
mvaNick Polyak13-Jan-11 6:45 
GeneralMy vote of 5 Pin
Kanasz Robert10-Jan-11 11:07
professionalKanasz Robert10-Jan-11 11:07 
GeneralRe: My vote of 5 Pin
Nick Polyak10-Jan-11 17:00
mvaNick Polyak10-Jan-11 17:00 
GeneralMy vote of 5 Pin
RaviRanjanKr7-Jan-11 5:01
professionalRaviRanjanKr7-Jan-11 5:01 
GeneralRe: My vote of 5 Pin
Nick Polyak7-Jan-11 6:18
mvaNick Polyak7-Jan-11 6:18 
GeneralYou may want to add some 'formal' information Pin
leppie6-Jan-11 20:56
leppie6-Jan-11 20:56 
GeneralRe: You may want to add some 'formal' information [modified] Pin
Nick Polyak7-Jan-11 6:17
mvaNick Polyak7-Jan-11 6:17 
GeneralMy vote of 5 Pin
Luc Pattyn6-Jan-11 9:22
sitebuilderLuc Pattyn6-Jan-11 9:22 
GeneralRe: My vote of 5 Pin
Nick Polyak6-Jan-11 9:23
mvaNick Polyak6-Jan-11 9:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.