Sympy container objects that respond to simplify(...) - sympy

I'm trying to create a sympy container object with multiple sympy expressions inside it, representing the x, y and z observables of a qubit.
I started by creating a class that extends sympy.Basic. I was hoping that pushing x, y and z into the sympy args would result in an object that would respond to simplify(qubit) by returning an object with the x, y, and z args simplified. When that didn't work naturally, I redefined the simplify(self, **kwargs) method.
Is there a recommended way to get simplify(qubit) working rather than qubit.simplify()?
from sympy import Symbol, cos, sin, Basic
aa = Symbol('aa')
# simplifies to 1
expression_to_simplify = sin(aa)**2 + cos(aa)**2
class Qubit(Basic):
def __new__(cls, qubitnumber, x, y, z):
obj = super().__new__(cls, qubitnumber, x, y, z)
obj.qubitnumber = qubitnumber
return obj
def simplify(self, **kwargs):
from sympy.simplify import simplify
return Qubit(
self.args[0],
simplify(self.args[1]),
simplify(self.args[2]),
simplify(self.args[3])
)
qb = Qubit(1, expression_to_simplify, expression_to_simplify, expression_to_simplify)
print(qb.simplify())
from sympy.simplify import simplify
print(simplify(qb))
Gives output:
Qubit(1, 1, 1, 1)
Qubit(1, sin(aa)**2 + cos(aa)**2, sin(aa)**2 + cos(aa)**2, sin(aa)**2 + cos(aa)**2)

simplify is not written to provide support for simplifying the contents of containers, but you can make it so (or only do so for Qubit as follows):
>>> from sympy import simplify as _simplify
>>> def simplify(x, *y, **k):
... if isinstance(x, Expr): return _simplify(x, *y, **k)
... elif isinstance(x, Qubit): return Qubit(*[_simplify(i, *y, **k) for i in x.args])
... raise NotImplementedError
This then gives Qubit(1, 1, 1, 1) for simplify(qb). The trick is in using the original simplify as _simplify inside the new simplify.

Related

sympy: post order traversal over expression tree: skips top-level operation

I am trying to loop post order through a sympy-expression.
It works fine except for the fact that it skips the last addition (im my example case the addition of sympy.sin(x * y) and z**2).
import sympy
def post(expr):
if not expr:
return
for arg in expr.args:
post(arg)
print(f'arg {arg}')
print(f'arg.func: {arg.func}')
print(f'arg.args: {arg.args}')
x, y, z = sympy.symbols('x,y,z')
expr = sympy.sin(x * y) + z**2
post(expr)
I think it's because of the for arg in expr.args part yet if I ditch the .args and
loop over the whole expression
import sympy
def post(expr):
if not expr:
return
for arg in expr:
post(arg)
print(f'arg {arg}')
print(f'arg.func: {arg.func}')
print(f'arg.args: {arg.args}')
x, y, z = sympy.symbols('x,y,z')
expr = sympy.sin(x * y) + z**2
post(expr)
I get TypeError: 'Add' object is not iterable.
Can I somehow fix this such that it loops over all functions of the expression and doesn't skip the top-level function?
This battery is already included if you want to use it:
from sympy.core.traversal import postorder_traversal
for i in postorder_traversal(expr):print(i)
gives
z
2
z**2
x
y
x*y
sin(x*y)
z**2 + sin(x*y)
Otherwise, consider this modification for your function:
def post(expr):
for arg in expr.args:
post(arg)
print(f'arg {expr}')
print(f'arg.func: {expr.func}')
print(f'arg.args: {expr.args}')

How to divide by variable that belongs to y in Sympy

I am making an separable differential equation solver. In order to make an expression that separated by x and y variables I have to divide expression on the right by every variable that belong to s such as sin(y), e**y, y**2, ...
I am using Sympy
def equationseparator(diffeq):
x, y, z, e= sym.symbols("x y z e")
separateddiff, separatedeq = diffeq.split("=")
variables_of_eq = re.split('[(|)]', separatedeq)
eq = sym.parse_expr(separatedeq)
variables_of_eq_ordered = []
variables_of_eq_ord_var = []
for var in variables_of_eq:
if var == " * " or var == "":
pass
else:
variables_of_eq_ordered.append(var)
for var in variables_of_eq_ordered:
var = sym.Symbol(var)
variables_of_eq_ord_var.append(var)
print(sym.simplify(separatedeq))
print(variables_of_eq_ordered)
print(variables_of_eq_ord_var)
equationseparator("dy/dx=(6 * x) * (y) * (e**y)")
By using variables_of_eq_ord_var I get all the variables and append to the list. And I want to choose all the expressions that belong to y. But I couldn't make it. Thanks in advance!
Since you are using SymPy, why not use its solver for such equations?
>>> from sympy import S, Function
>>> from sympy.abc import x
>>> f = Function('f')
>>> S('dydx-6*x*y*exp(y)').subs(y,f(x)).subs('dydx',f(x).diff(x))
-6*x*f(x)*exp(f(x)) + Derivative(f(x), x)
>>> dsolve(_)
Eq(Ei(exp_polar(I*pi)*f(x)), C1 + 3*x**2)
Else, if you have a product of factors and want those that contain a certain symbol you can just use as_independent to separate them:
>>> nony, withy = (x*y*exp(y)).as_independent(y); (nony, withy)
(x, y*exp(y))

What is the correct usage of TransformedPiecewiseLinearFunctionND in Pyomo?

Background
I'm trying to use a surrogate model in Pyomo. Given a set of data labeled x, y, and z, I would like to write z as an inexpensive function of x and y.
Issue
Pyomo has tools for multivariate piecewise linear functions. See here. I setup a simple example and my function is evaluating correctly. But there there doesn't seem to be any constraint getting added for Z. I would expect the model value to be equal to the interpolated value below. I'm guessing I did something wroing in setting up TransformedPiecewiseLinearFunctionND, but I couldn't find any examples in the documentation. Any insights would be appreciated.
Code
from pyomo.core import ConcreteModel, Var, Constraint
import pyomo.environ as pe
from pyomo.core.kernel.piecewise_library.transforms_nd import (
PiecewiseLinearFunctionND,
TransformedPiecewiseLinearFunctionND
)
from pyomo.core.kernel.variable import (
variable,
variable_list
)
import pyomo.core.kernel.piecewise_library.util as util
import numpy as np
from scipy.spatial import Delaunay
npts = 100
vlist = variable_list([variable(lb=-1, ub=1),
variable(lb=-1, ub=1)])
tri = util.generate_delaunay(vlist, num=npts)
x, y = tri.points.T
z = np.cos(x) * np.sin(y)
model = ConcreteModel()
model.X = Var(initialize=0)
model.Y = Var(initialize=0)
model.Z = Var(initialize=999)
f = PiecewiseLinearFunctionND(tri, z)
model.g = TransformedPiecewiseLinearFunctionND(
f=f,
input=(model.X, model.Y),
output=model.Z
)
def x_rule(model):
return model.X == 0.5
def y_rule(model):
return model.Y == 0.5
model.x_const = Constraint(rule=x_rule)
model.y_const = Constraint(rule=y_rule)
solver = pe.SolverFactory('ipopt')
solver.solve(model)
z_exact = np.cos(0.5) * np.sin(0.5)
z_interp = f([0.5, 0.5])
x_model = pe.value(model.X)
y_model = pe.value(model.Y)
z_model = pe.value(model.Z)
print(f'Z Exact: {z_exact}')
print(f'Z Interpolated: {z_interp}')
print(f'Model values (X, Y, Z): {x_model}, {y_model}, {z_model}')
Output
Z Exact: 0.42073549240394825
Z Interpolated: 0.42067082611089646
Model values (X, Y, Z): 0.5, 0.5, 999
I've also tried adding a constraint for Z manually. This produces an error:
def z_rule(model):
return model.Z == f([model.X, model.Y])
model.z_const = Constraint(rule=z_rule)
You are mixing modeling components between the pyomo.kernel and pyomo.environ modeling layers. This is not supported (this page has more information).
The multi-dimensional piecewise functionality is currently only available using the pyomo.kernel interface. An example of how to use it can be found here:
https://github.com/Pyomo/pyomo/blob/main/examples/kernel/piecewise_nd_functions.py

Error using multiprocessing library: "got multiple values for keyword argument 'x' "

I am trying to parallelize a penalized linear model using the multiprocessing library in python.
I created a function that solves my model:
from __future__ import division
import numpy as np
from cvxpy import *
def lm_lasso_solver(x, y, lambda1):
n = x.shape[0]
m = x.shape[1]
lambda1_param = Parameter(sign="positive")
betas_var = Variable(m)
response = dict(model='lm', penalization='l')
response["parameters"] = {"lambda_vector": lambda1}
lasso_penalization = lambda1_param * norm(betas_var, 1)
lm_penalization = 0.5 * sum_squares(y - x * betas_var)
objective = Minimize(lm_penalization + lasso_penalization)
problem = Problem(objective)
lambda1_param.value = lambda1
try:
problem.solve(solver=ECOS)
except:
try:
problem.solve(solver=CVXOPT)
except:
problem.solve(solver=SCS)
beta_sol = np.asarray(betas_var.value).flatten()
response["solution"] = beta_sol
return response
In this function x is a matrix of predictors and y is the response variable. lambda1 is the parameter that must be optimized, and so, is the parameter that I want to parallelize. I saved this script in a python file called "ms.py"
Then I created another python file called "parallelization.py" and in that file I defined the following:
import multiprocessing as mp
import ms
import functools
def myFunction(x, y, lambda1):
pool = mp.Pool(processes=mp.cpu_count())
results = pool.map(functools.partial(ms.lm_lasso_solver, x=x, y=y), lambda1)
return results
So the idea was now, on the python interpreter, execute:
from sklearn.datasets import load_boston
boston = load_boston()
x = boston.data
y = boston.target
runfile('parallelization.py')
lambda_vector = np.array([1,2,3])
myFunction(x, y, lambda_vector)
But when I do this, I get the following error message:
The problem is on the line:
results = pool.map(functools.partial(ms.lm_lasso_solver, x=x, y=y), lambda1)
You are calling the functools.partial() method with keyworded arguments whereas in your lm_lasso_solver method, you don't define them as keyworded arguments. You should call it with x and y as positional arguments as follows:
results = pool.map(functools.partial(ms.lm_lasso_solver, x, y), lambda1)
or simply use the apply_async() method the pool object :
results = pool.apply_async(ms.lm_lasso_solver, args=[x, y, lambda1])

Returning np.array or np.matrix objects in a theano function

I have to do something like this.
import theano as th
import theano.tensor as T
x, y = T.dscalars('x', 'y')
z = np.matrix([[x*y, x-y], [x/y, x**2/(2*y)]])
f = th.function([x, y], z) # causes error
# next comes calculations like f(2, 1)*f(3, 2)*some_matrix
I know the last line is not a valid code as th.function doesn't support returning these objects. Is there an efficient way to do this without returning all elements of matrix and casting it as an np.matrix?
The problem with your approach is that z needs to be a list of theano variables not a numpy matrix.
You can achieve the same result using:
z1,z2,z3,z4 = x*y,x-y,x/y,x**2/(2*y)
f = th.function([x, y], [z1,z2,z3,z4])
def createz(z1,z2,z3,z4) :
return np.matrix([[z1,z2],[z3,z4]])
print(createz(*f(1,2)))