Is it possible to solve for function operations given inputs and outputs? - z3

I have a function that takes 2 integers as input, and outputs an integer.
I have a set of inputs and their known outputs.
Is it possible to figure out what operations the function is applying to the inputs to arrive at the output?
I'm not sure how to even begin modeling such a problem with z3. Any help would be greatly appreciated.
Example data:
f(1, 1) = 1
f(3, 7) = 28
f(3, 2) = 3
f(4, 6) = 56
f(10, 3) = 55
f(x, y) = f(y, x)

Yes you can. But the results are not going to be interesting by any means, unless you at least prescribe some structure on what f looks like.
The obvious way to do this is to declare f as un interpreted-function, and then look at the model constructed for it. But this will be a rudimentary definition of f; that is, it'll satisfy your axioms in the most uninteresting way. Here's how you'd write it:
from z3 import *
f = Function('f', IntSort(), IntSort(), IntSort())
s = Solver()
s.add(f( 1, 1) == 1)
s.add(f( 3, 7) == 28)
s.add(f( 3, 2) == 3)
s.add(f( 4, 6) == 56)
s.add(f(10, 3) == 55)
x, y = Ints('x y')
s.add(ForAll([x, y], f(x, y) == f(y, x)))
print(s.check())
print(s.model())
And here's what z3 prints:
sat
[f = [else ->
If(And(Not(Var(0) == 6),
Not(Var(0) == 4),
Not(Var(0) == 10),
Var(0) == 3,
Not(Var(1) == 6),
Not(Var(1) == 4),
Var(1) == 10),
55,
If(And(Var(0) == 6, Not(Var(1) == 6), Var(1) == 4),
56,
If(And(Not(Var(0) == 6),
Not(Var(0) == 4),
Not(Var(0) == 10),
Not(Var(0) == 3),
Not(Var(0) == 1),
Var(0) == 2,
Not(Var(1) == 6),
Not(Var(1) == 4),
Not(Var(1) == 10),
Var(1) == 3),
3,
If(And(Not(Var(0) == 6),
Not(Var(0) == 4),
Not(Var(0) == 10),
Not(Var(0) == 3),
Not(Var(0) == 1),
Not(Var(0) == 2),
Not(Var(1) == 6),
Not(Var(1) == 4),
Not(Var(1) == 10),
Var(1) == 3),
28,
If(And(Not(Var(0) == 6),
Not(Var(0) == 4),
Var(0) == 10,
Not(Var(1) == 6),
Not(Var(1) == 4),
Not(Var(1) == 10),
Var(1) == 3),
55,
If(And(Not(Var(0) == 6),
Var(0) == 4,
Var(1) == 6),
56,
If(And(Not(Var(0) == 6),
Not(Var(0) == 4),
Not(Var(0) == 10),
Var(0) == 3,
Not(Var(1) == 6),
Not(Var(1) == 4),
Not(Var(1) == 10),
Not(Var(1) == 3),
Not(Var(1) == 1),
Var(1) == 2),
3,
If(And(Not(Var(0) == 6),
Not(Var(0) == 4),
Not(Var(0) == 10),
Var(0) == 3,
Not(Var(1) == 6),
Not(Var(1) == 4),
Not(Var(1) == 10),
Not(Var(1) == 3),
Not(Var(1) == 1),
Not(Var(1) == 2)),
28,
If(And(Not(Var(0) == 6),
Not(Var(0) == 4),
Not(Var(0) == 10),
Not(Var(0) == 3),
Var(0) == 1,
Not(Var(1) == 6),
Not(Var(1) == 4),
Not(Var(1) == 10),
Not(Var(1) == 3),
Var(1) == 1),
1,
12)))))))))]]
The way to read this output is to substitute x for Var(0) and y for Var(1), and take the nested-if-then-elses as your defining clauses of the definition.
While I haven't checked the output line-by-line, I'm sure it is correct; in the sense that it satisfies your requirements perfectly. But I can hear you say "that's not what I really wanted!" And indeed, this is not what you wanted to see as a general/minimal function that satisfies the requirements. The way SMT solvers work, you'll never get a minimal answer unless you describe some skeleton for z3 to fill in.
Note that this is an active research area: How to use (semi-)automated theorem provers, SMT solvers, etc. to "write" code for us. The general area is known as SyGus (Syntax-Guided Synthesis). If you want to learn more, start with https://sygus.org, which contains a general description of the problem and read this paper: https://sygus.org/assets/pdf/FMCAD'13_SyGuS.pdf

A somewhat simplified model with assumed limited range for input variables:
from z3 import *
s = Solver()
# upper limit for an input integer
dimIn = 10+1
vars = [[0 for _ in range(dimIn)] for _ in range(dimIn)]
# define decision variables and constraints
for a in range(dimIn):
for b in range(dimIn):
vars[a][b] = Int('v' + str(a).zfill(2) + str(b).zfill(2))
for a in range(dimIn):
for b in range(dimIn):
x = min(a, b)
y = max(a, b)
v = vars[x][y]
xy = str(x) + ',' + str(y);
# we don't have Python 3.10 with match/case ...
if xy == '1,1':
s.add(v == 1)
elif xy == '3,7':
s.add(v == 28)
elif xy == '2,3':
s.add(v == 3)
elif xy == '4,6':
s.add(v == 56)
elif xy == '3,10':
s.add(v == 55)
s.add(v == vars[y][x])
print(s.check())
m = s.model()
for a in range(dimIn):
for b in range(dimIn):
if a <= b:
r = str(m.eval(vars[a][b], model_completion=True))
print('f(' + str(a) + ',' + str(b) + ') = ' + r)
Resulting output:
sat
f(0,0) = 0
f(0,1) = 0
f(0,2) = 0
f(0,3) = 0
f(0,4) = 0
f(0,5) = 0
f(0,6) = 0
f(0,7) = 0
f(0,8) = 0
f(0,9) = 0
f(0,10) = 0
f(1,1) = 1
f(1,2) = 0
f(1,3) = 0
f(1,4) = 0
f(1,5) = 0
f(1,6) = 0
f(1,7) = 0
f(1,8) = 0
f(1,9) = 0
f(1,10) = 0
f(2,2) = 0
f(2,3) = 3
f(2,4) = 0
f(2,5) = 0
f(2,6) = 0
f(2,7) = 0
f(2,8) = 0
f(2,9) = 0
f(2,10) = 0
f(3,3) = 0
f(3,4) = 0
f(3,5) = 0
f(3,6) = 0
f(3,7) = 28
f(3,8) = 0
f(3,9) = 0
f(3,10) = 55
f(4,4) = 0
f(4,5) = 0
f(4,6) = 56
f(4,7) = 0
f(4,8) = 0
f(4,9) = 0
f(4,10) = 0
f(5,5) = 0
f(5,6) = 0
f(5,7) = 0
f(5,8) = 0
f(5,9) = 0
f(5,10) = 0
f(6,6) = 0
f(6,7) = 0
f(6,8) = 0
f(6,9) = 0
f(6,10) = 0
f(7,7) = 0
f(7,8) = 0
f(7,9) = 0
f(7,10) = 0
f(8,8) = 0
f(8,9) = 0
f(8,10) = 0
f(9,9) = 0
f(9,10) = 0
f(10,10) = 0

Related

Newer versions of Z3 no longer keep/print all function instances

Notice in the new versions of Z3, the model only prints the arguments to the function that have a value other than the default. Is there a way to return to the old method of receiving all instances, including those that map to the default value?
Code:
import z3
config_init = z3.Function('config_init', z3.IntSort(), z3.IntSort(), z3.IntSort(), z3.IntSort())
def fooY(W, X, Y, Z):
return config_init(W, X, Y) == Z
s = z3.Solver()
s.add(fooY(1, 2, 3, 4))
s.add(fooY(2, 3, 4, 5))
s.add(fooY(1, 2, 8, 4))
s.add(fooY(2, 3, 9, 5))
print("Z3 Version", z3.get_version())
if s.check() == z3.sat:
mod = s.model()
print(mod)
else:
print('failed')
print(s.unsat_core())
Old Z3 Output
('Z3 Version', (4L, 5L, 1L, 0L))
[config_init = [(1, 2, 3) -> 4,
(2, 3, 4) -> 5,
(1, 2, 8) -> 4,
(2, 3, 9) -> 5,
else -> 4]]
New Z3 Output
Z3 Version (4, 8, 7, 0)
[config_init = [(2, 3, 4) -> 5, (2, 3, 9) -> 5, else -> 4]]

Love2d / LUA grid locked movement NO DIAGONAL

I thought this would be a common problem but after days of research I can't find a solution. Very new to programming in general and LUA specifically. I'm building a SUPAPLEX clone as a CS50 personal project: the character moves along the grid based map and there's a code that everyone seems to suggest (attached). On release of arrow buttons the movement is continued until the end of a tile, smoothly. But if 2 movement buttons are pushed, it causes brief diagonal movement and that's the problem I'm unsuccessfully trying to solve.
Basically I'm trying to either ignore any input until the movement of the sprite is finished at the end of the grid tile or prevent updating until movement in one direction is complete. Seems like a simple thing but I'm about to give up this whole thing. Frustrating. Any input is hiiiighly appreciated and I'm sure this would be a lot of help for very many people...
function love.load()
love.keyboard.setKeyRepeat(true)
player = {
grid_x = 256,
grid_y = 256,
act_x = 256,
act_y = 256,
speed = 5,
}
map = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 },
{ 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 },
{ 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1 },
{ 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
}
function testMap(x, y)
if map[(player.grid_y / 32) + y][(player.grid_x / 32) + x] == 1 then
return false
end
return true
end
function love.keypressed(key)
if key == "up" then
player.grid_y = player.grid_y - 32
elseif key == "down" then
player.grid_y = player.grid_y + 32
elseif key == "left" then
player.grid_x = player.grid_x - 32
elseif key == "right" then
player.grid_x = player.grid_x + 32
end
end
end
function love.update(dt)
player.act_y = player.act_y - ((player.act_y - player.grid_y) * player.speed * dt)
player.act_x = player.act_x - ((player.act_x - player.grid_x) * player.speed * dt)
end
function love.draw()
love.graphics.rectangle("fill", player.act_x, player.act_y, 32, 32)
for y=1, #map do
for x=1, #map[y] do
if map[y][x] == 1 then
love.graphics.rectangle("line", x * 32, y * 32, 32, 32)
end
end
end
end
you're trying to get it to only walk along grid-lines?
take out love.keyboard.setKeyRepeat(true)
and don't use love.keypressed(key)
that's for one-at-a-time keypresses, and it would be hard to use that
with love.keyreleased() to see if all the other keys are released.
use isDown instead, and if one of them isDown, then none of the other dir keys allow input. (along with the couple player.act lines you already have in your update)
player = {
grid_x = 256,
grid_y = 256,
act_x = 256,
act_y = 256,
speed = 5,
dir = ''
}
function love.update(dt)
if love.keyboard.isDown("up", "down", "left", "right") then
if love.keyboard.isDown("up") and ( player.dir == 'up' or player.dir == '' ) then
player.dir = 'up' -- only go up if currently held, or no other dir key being pressed
player.grid_y = player.grid_y - 32
elseif love.keyboard.isDown("down") and ( player.dir == 'down' or player.dir == '' ) then
player.dir = 'down' -- only go down if currently held...
player.grid_y = player.grid_y + 32
elseif key == "left" and ( player.dir == 'left' or player.dir == '' ) then
player.dir = 'left'
player.grid_x = player.grid_x - 32
elseif key == "right" and ( player.dir == 'right' or player.dir == '' ) then
player.dir = 'right'
player.grid_x = player.grid_x + 32
end
else -- none of those keys are being pressed, so player is idle
player.dir = ''
end -- isDown()
player.act_y = player.act_y - ((player.act_y - player.grid_y) * player.speed * dt)
player.act_x = player.act_x - ((player.act_x - player.grid_x) * player.speed * dt)
end -- update()

Iterate through table using selected keys

The below table and for loop is how we generally access all the key, value pairs in lua
local t = {a1 =11, a2=22, c=3, d=4}
for k, v in pairs(t) do
print(k,v)
end
-- Output: k = a1, a2, c, d & v = 11, 22, 3, 4 respectively
a1 11
a2 22
c 3
d 4
If I want to iterate only on a subset of this table where loop only iterates on certain keys as shown here
k = a1,a2
Since I am intending to add more tables in t as
local t = {
{a1 = 11, a2 = 22, c = 3, d = 4},
{a1 = 12, a2 = 23, c = 2, d = 4},
{a1 = 13, a2 = 24, c = 1, d = 4},
{a1 = 14, a2 = 25, c = 0, d = 4},
{a1 = 15, a2 = 26, c = 0, d = 4}
}
What I want to use something like
for k = {a1, a2} in pairs (t) do
-- something
end
Is there a way to do this other than adding an if condition within a loop, Since this will iterate through all k,v pairs & not desired
for k,v in pairs (t) do
if (k == a1 or k == a2) then
-- something
you can do it this way
local t = {
{a1 = 11, a2 = 22, c = 3, d = 4},
{a1 = 12, a2 = 23, c = 2, d = 4},
{a1 = 13, a2 = 24, c = 1, d = 4},
{a1 = 14, a2 = 25, c = 0, d = 4},
{a1 = 15, a2 = 26, c = 0, d = 4}
}
local keys_to_iterate = {"a1", "a2"}
for index = 1, #t do
for k = 1, #keys_to_iterate do
if t[index][keys_to_iterate[k]] then
print(keys_to_iterate[k] , t[index][keys_to_iterate[k]])
end
end
end
you can see it here
https://repl.it/repls/CoralIndianredVaporware

Detect all occurrences of same image

My script recognizes the given image, but if two of the same images are present it will ignore the second one. I want to count it as found 2 times at different locations. There are five rows where the same text-image can appear:
Screen
Image
Output from line 113
[Results found]: Additional Damage of Critical Hits +1% found.
Source code (excerpt)
Func Awakes_adoch()
ToolTip('Scanning for adoch awakes', 0, 0)
$adoch1 = _ImageSearchArea("images/adoch/adoch1.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch1 = 1) Then
$awake_adoch_attribute_count += 1
EndIf
$adoch3 = _ImageSearchArea("images/adoch/adoch3.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch3 = 1) Then
$awake_adoch_attribute_count += 3
EndIf
$adoch5 = _ImageSearchArea("images/adoch/adoch5.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch5 = 1) Then
$awake_adoch_attribute_count += 5
EndIf
$adoch7 = _ImageSearchArea("images/adoch/adoch7.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch7 = 1) Then
$awake_adoch_attribute_count += 7
EndIf
$adoch9 = _ImageSearchArea("images/adoch/adoch9.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch9 = 1) Then
$awake_adoch_attribute_count += 9
EndIf
$adoch11 = _ImageSearchArea("images/adoch/adoch11.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch11 = 1) Then
$awake_adoch_attribute_count += 11
EndIf
$adoch13 = _ImageSearchArea("images/adoch/adoch13.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch13 = 1) Then
$awake_adoch_attribute_count += 13
EndIf
$adoch15 = _ImageSearchArea("images/adoch/adoch15.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch15 = 1) Then
$awake_adoch_attribute_count += 15
EndIf
$adoch17 = _ImageSearchArea("images/adoch/adoch17.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch17 = 1) Then
$awake_adoch_attribute_count += 17
EndIf
$adoch19 = _ImageSearchArea("images/adoch/adoch19.png", 1, 0, 0, 400, 390, $x, $y, 0)
If ($adoch19 = 1) Then
$awake_adoch_attribute_count += 19
EndIf
Sleep(50)
ToolTip('[Scan]: Additional Damage of Critical Hits +' & $awake_adoch_attribute_count & '% found.', 0, 0)
EndFunc
Source code (full)
If the output window shows like below, it should be able to add up to 25:
Additional Damage of Critical Hits +3%
DEF + 40
Additional Damage of Critical Hits +3%
DEF + 4
Additional Damage of Critical Hits +19%
Another example:
I’m looking for a solution that doesn’t skip numbers or adds them from a previous shot to the next.
Source code (new)
relatively easy, as all searched images are below each other (would be much more difficult, when searched images were at random locations):
#include "ImageSearch64.au3"
$searchpath = "c:\users\50022505\Downloads\" ; where are my images?
$x = 0 ; dummy parameter for coordinates
$y = 0 ; dummy parameter for coordinates
$Damage = 100
ConsoleWrite("Initial Damage: " & $Damage & #CRLF)
AddDamage()
ConsoleWrite("Resulting Damage: " & $Damage & #CRLF)
Func AddDamage()
$Damage += GetLocations($searchpath & "adoch1.png")
$Damage += GetLocations($searchpath & "adoch5.png") * 5
$Damage += GetLocations($searchpath & "adoch9.png") * 9
$Damage += GetLocations($searchpath & "adoch11.png") * 11
Return $Damage
EndFunc ;==>AddDamage
Func GetLocations($search)
Local $Findings = 0, $found = 0
Local $dx = 0, $dy = 0 ;sub coordinates
While 1
Local $found = _ImageSearchArea($search, 1, $dx, $dy, 400, 390, $x, $y, 0)
If $found = 0 Then Return $Findings ; no more findings
$Findings += 1
$dy = $y ; set new coordinate window to search (excluding last finding)
WEnd
EndFunc ;==>GetLocations

How to use Z3Py online to determine the DC operating point of a MOSFET amplifier?

Z3Py online is a very powerful tool to determine the DC operating point of a given MOSFET amplifier. With the aim to illustrate that we will consider the following circuit
The parameters of the MOSFET amplifier are:
k = 1.25 * 10^(-4) , V_T = 1.5 , R_D = 10^4 , V = 15
To determine the DC operating point we use the following Z3Py code:
I_D, k, V_GS, V_T, V_DS, V, R_D = Reals('I_D k V_GS V_T V_DS V R_D')
equations = [
I_D == k * (V_GS - V_T)**2, V_GS == V_DS,
V_DS == V - R_D * I_D,
]
print "MOSFET equations:"
print equations
problem = [
k == 1.25*10**(-4),
V_T == 1.5,
R_D == 10**4,
V == 15, V_GS > V_T
]
print "Problem:"
print problem
print "Solution:"
solve(equations + problem)
Using this code online, we obtain the following output
MOSFET equations:
[I_D = k·(V_GS - V_T)2, V_GS = V_DS, V_DS = V - R_D·I_D]
Problem:
[k = 1/8000, V_T = 3/2, R_D = 10000, V = 15, V_GS > V_T]
Solution:
[I_D = 0.0010589410?,
V = 15,
R_D = 10000,
V_T = 3/2,
k = 1/8000,
V_GS = 4.4105890714?,
V_DS = 4.4105890714?]
It can also try it online here.
As we can see Z3PY is able to compute the correct solution I_D , V_GS and V_DS in a very compact and efficient form.
Other example
This problem is solved using the following code
I_D, k, V_GS = Reals('I_D k V_GS')
V_T, V_I, R_S = Reals('V_T V_I R_S')
V_O, V_DD, R_L = Reals('V_O V_DD R_L')
equations = [
I_D == (k/2) * (V_GS - V_T)**2, V_I == V_GS + R_S * I_D,
V_O == V_DD - R_L * I_D,
]
print "MOSFET equations:"
print equations
problem = [
k == 2,
V_T == 2,
V_DD == 145,
V_I == 6.5, R_S == 22, R_L ==82, V_GS > V_T
]
print "Problem:"
print problem
print "Solution:"
solve(equations + problem)
problem1 = [
k == 2,
V_T == 2,
V_DD == 145,
V_I == 6.5 + 1, R_S == 22, R_L ==82, V_GS > V_T
]
print "Problem 1"
print problem1
print "Solution 1:"
solve(equations + problem1)
problem2 = [
k == 2,
V_T == 2,
V_DD == 145,
V_I == 6.5 + 10**(-3), R_S == 22, R_L ==82, V_GS > V_T
]
print "Problem 2"
print problem2
print "Solution 2:"
solve(equations + problem2)
Using this code online we obtain the following output
MOSFET equations:
[I_D = (k/2)·(V_GS - V_T)2, V_I = V_GS + R_S·I_D, V_O = V_DD - R_L·I_D]
Problem:
[k = 2, V_T = 2, V_DD = 145, V_I = 13/2, R_S = 22, R_L = 82, V_GS > V_T]
Solution:
[I_D = 0.1849949805?,
R_L = 82,
R_S = 22,
V_I = 13/2,
V_DD = 145,
V_T = 2,
k = 2,
V_O = 129.8304115963?,
V_GS = 2.4301104282?]
Problem 1
[k = 2, V_T = 2, V_DD = 145, V_I = 15/2, R_S = 22, R_L = 82, V_GS > V_T]
Solution 1:
[I_D = 0.2282823186?,
R_L = 82,
R_S = 22,
V_I = 15/2,
V_DD = 145,
V_T = 2,
k = 2,
V_O = 126.2808498705?,
V_GS = 2.4777889896?]
Problem 2
[k = 2, V_T = 2, V_DD = 145, V_I = 6501/1000, R_S = 22, R_L = 82, V_GS > V_T]
Solution 2:
[I_D = 0.1850381539?,
R_L = 82,
R_S = 22,
V_I = 6501/1000,
V_DD = 145,
V_T = 2,
k = 2,
V_O = 129.8268713797?,
V_GS = 2.4301606140?]
It can also try it online here.
Other example
This problem is solved using the following code
I_D, k, V_GS = Reals('I_D k V_GS')
V_T, V_EQ, R_S = Reals('V_T V_EQ R_S')
V_DS, V_DD, R_D = Reals('V_DS V_DD R_D')
equations = [
I_D == (k/2) * (V_GS - V_T)**2, V_EQ == V_GS + R_S * I_D,
V_DS == V_DD - (R_D + R_S )* I_D,
]
print "MOSFET equations:"
print equations
problem = [
k == 25*10**(-6),
V_T == 1,
V_DD == 10,
V_EQ == 4, R_S == 39*10**3, R_D == 75*10**3, V_GS > V_T
]
print "Problem:"
print problem
print "Solution:"
solve(equations + problem)
Using this code online we obtain the following output
MOSFET equations:
[I_D = (k/2)·(V_GS - V_T)2, V_EQ = V_GS + R_S·I_D, V_DS = V_DD - (R_D + R_S)·I_D]
Problem:
[k = 1/40000, V_T = 1, V_DD = 10, V_EQ = 4, R_S = 39000, R_D = 75000, V_GS > V_T]
Solution:
[I_D = 0.0000343918?,
R_D = 75000,
R_S = 39000,
V_EQ = 4,
V_DD = 10,
V_T = 1,
k = 1/40000,
V_DS = 6.0793307846?,
V_GS = 2.6587184263?]
It can also try it online here .
Other example:
This problem is solved using the following code
I_D, k, V_GS = Reals('I_D k V_GS')
V_T = Real('V_T')
R_D = Real('R_D')
V_DS, V_DD = Reals('V_DS V_DD')
equations = [
I_D == (k) * (V_GS - V_T - (V_DS) /2)*V_DS, V_DD == V_GS,
V_DS == V_DD - (R_D)* I_D,
]
print "MOSFET equations:"
print equations
problem = [
k == 250*10**(-6),
V_T == 1,
V_DD == 4,
R_D == 1600, V_DS < V_GS - V_T
]
print "Problem:"
print problem
print "Solution:"
solve(equations + problem)
Using this code online we obtain the following output
MOSFET equations:
[I_D = k·(V_GS - V_T - V_DS/2)·V_DS, V_DD = V_GS, V_DS = V_DD - R_D·I_D]
Problem:
[k = 1/4000, V_T = 1, V_DD = 4, R_D = 1600, V_DS < V_GS - V_T]
Solution:
[I_D = 0.0010634763?,
R_D = 1600,
V_DD = 4,
V_T = 1,
k = 1/4000,
V_GS = 4,
V_DS = 2.2984378812?]
It can also try it online here .
Please let me know what do you think about this and if you know a more efficient code for such class of problems. Many thnaks.

Resources