Problems with EquationMgr & SelectionManger Solidworks Api C# - c#

I'm trying to understand principles of solidworks API, but have several problems.
Here is my code:
for (var i = 0; i < selMgr.GetSelectedObjectCount(); i++)
{
var Face = selMgr.GetSelectedObject(i+1);
surfaces.Add(Face.GetSurface());
measure = swModel.Extension.CreateMeasure();
if (surfaces[i].IsCylinder())
{
// Problem # 1
Console.WriteLine("Cylinder " + i);
measure.Calculate(surfaces[i]);
var diameter = measure.Diameter * 1000;
var length = 1000 * measure.Perimeter / (measure.Diameter * Math.PI);
var temp = swApp.OpenDoc6(#"E:\OAK\Locator9.SLDPRT", 1, 1, "", 0, 0);
var part = component.AddComponent5(#"E:\OAK\Locator9.SLDPRT", 0, "", true, "", 0, 0, 0.3);
swApp.CloseDoc(#"E:\OAK\Locator9.SLDPRT");
ModelDoc2 locator = part.GetModelDoc();
var eqMgr = locator.GetEquationMgr();
Console.WriteLine("Evaluated diameter " + diameter);
Console.WriteLine("Evaluated length " + length);
Console.WriteLine(eqMgr.Equation[1] + " " + eqMgr.Equation[2]);
//Problem #2
eqMgr.set_Equation(1, $#"""D""={diameter}");
eqMgr.set_Equation(2, $#"""L""={length}");
eqMgr.EvaluateAll();
locator.EditRebuild3();
locator.ForceRebuild3(false);
}
else
{
// TODO: Handle other type of surface
}
}
1) I want to measure perimeter & diameter of the selected surface. But if a return value of GetSelectedObjectCount() method is greater than 1, measure.Diameter & measure.Perimeter both returns -1. And I kinda understand why, 'cause such operation isn't possible via UI as well, but can I do smth to solve the problem?
2) The code above has no influence on the equation of the inserted component, even if it writes it on the console.
Help please!

1 For primitive surfaces you can use *Params property of the ISurface object to get the information you need. For cylinder it would be CylinderParams. I can't find the link right now but I remember reading that measure shouldn't be used for any precise calculations as it is not guaranteed to be accurate at all times. If you don't care about precision and still want to use measure you can manually manipulate set of selected objects.
2 I haven't used IEquationMgr but in general I tried to stay away from VB styled parameterized properties like Equation , I'd suggest trying to Delete and then Add equation.

Related

Translate Excel Spreadsheets with SOLVER in C#

After several searches and mistakes on my part, I finally managed to get out of it and get a result from the MSF solver.
However, it's not perfect, because I still have a difference against me in my C# code.
In the Excel workbook I have 6 solvers, relatively identical.
Only one solver per tab, but I have a lot of calculations.
In order to best stick to the Excel workbook, I created one method per cell containing a formula.
My code works, in the sense that if I give it the same data as Excel I have the same results, but with the solver I have a little difference.
Here's what I did, and I'd like you to tell me if there's anything I can improve by trying to keep my methods (representing my Excel Cells)
Each representation of the cells is created twice.
I need to have the value of my cell to do other calculations and it seems that I can't put methods returning a double, in the solver.
Classic method:
private double Cell_I5()
{
double res = 0;
res = (Math.Exp(-Var.Calc.Var4 * Var.Calc.De * M23) - 1) / (-Var.Calc.Var4 * Var.Calc.De * M23);
return res;
}
Method for the solver:
private Term Solv_I5()
{
Term res = 0;
res = (Model.Exp(-Var.Calc.Var4 * Var.Calc.De * Solver_M23) - 1) / (-Var.Calc.Var4 * Var.Calc.De * Solver_M23);
return res;
}
'M23' is a double
'Solver_M23' is a Decision
'Var4' is a double as well as 'De'.
So I use the return value with "Term" and I change all the Math functions to 'Model', except Math.Pi which is a constant.
You can imagine that there are close to 60 to 70 methods involved like that.
My method for the solver:
public void StartSolver()
{
var solver = SolverContext.GetContext();
solver.ClearModel();
var model = solver.CreateModel();
//Instanciation des variables du Solver en format Real(double) Non Negative
Solver_M22 = new Decision(Domain.RealNonnegative, "M22");
Solver_M23 = new Decision(Domain.RealNonnegative, "M23");
Solver_M24 = new Decision(Domain.RealNonnegative, "M24");
Solver_M25 = new Decision(Domain.RealNonnegative, "M25");
Solver_M26 = new Decision(Domain.RealNonnegative, "M26");
model.AddDecision(Solver_M22);
model.AddDecision(Solver_M23);
model.AddDecision(Solver_M24);
model.AddDecision(Solver_M25);
model.AddDecision(Solver_M26);
model.AddConstraint("M22a", Solver_M22 <= 4);
model.AddConstraint("M22b", Solver_M22 >= 0);
model.AddConstraint("M23a", Solver_M23 <= 2);
model.AddConstraint("M23b", Solver_M23 >= 0.001);
model.AddConstraint("M24a", Solver_M24 <= 2);
model.AddConstraint("M24b", Solver_M24 >= 0);
model.AddConstraint("M25a", Solver_M25 <= 2);
model.AddConstraint("M25b", Solver_M25 >= 0);
model.AddConstraint("M26a", Solver_M26 <= 2);
model.AddConstraint("M26b", Solver_M26 >= 0.001);
//Test with classical calculation methods
double test = Cell_H33() + Cell_H23();
//Adding Solver Methods
model.AddGoal("SommeDesCarresDesEquartsGlobal", GoalKind.Minimize, Solv_H33() + Solv_H23());
// Solve our problem
var solution = solver.Solve();
// Get our decisions
M22 = Solver_M22.ToDouble();
M23 = Solver_M23.ToDouble();
M24 = Solver_M24.ToDouble();
M25 = Solver_M25.ToDouble();
M26 = Solver_M26.ToDouble();
string s = solution.Quality.ToString();
//For test
double testSortie = Cell_H33() + Cell_H23();
}
Questions:
1)
At no time do I indicate whether it is a linear calculation or not. How to indicate if necessary?
In Excel it is declared nonlinear
I saw that the solver was looking for the best method on its own.
2)
Is there something I'm not doing right, because I don't have the same value (with Excel)? I checked several times all the methods by one, with the amount that I missed, maybe, something, I will recheck tomorrow.
3)
Apart from doing the calculation with the classic methods, I have not found a way to find my result. From the 'solution' object
How to extract it from the result if possible?
4)
Here is the result of the 5 variables I find MSF C#:
0.06014756519010750
0.07283670953453890
0.07479568348101340
0.02864805010533950
0.00100000002842722
And what I find the Excel solver:
0.0000
0.0010
0.0141
0.0000
0.0010
Is there a way to restrict the number of decimal places directly in the calculations?
Because when I reduce manually (after calculation) that changes my result quite a bit?
Thank you.
[EDIT] Forgot to post this message it was still pending.
This morning I ran the C# solver calculation again and the result is really different with a huge difference in the result.
I remind you that I want to minimize the result.
Excel = 3.92
C#=8122.34
Result not acceptable at all.
[EDIT 2]
I may have a clue:
When I doing a simple calculation, such as:
private Term Solv_I5()
{
Term res = 0;
res = Model.Exp(-Var.Calc.Var4 * Var.Calc.Den * Solver_M25);
return res;
}
the result is:
{Exp(Times(-4176002161226263/70368744177664, M25))}
Why "Times"
All formulas with multiplication contain Times.
For divisions there is 'Quotient', additions 'Plus', but multiplications 'Times !!!
Question 4)
Am I doing the multiplications wrong in a 'Term'.?
Do you have an idea?
[EDIT 3]
I just saw that "times" was not a stupid term, another misunderstanding on my part of the English language, sorry.
So that doesn't solve my problem.
Can you help me please.

CNTK Sequences in C#

I made a working script in python to train a CNTK Model with some data samples. Now I'm trying to translate it to C# in CNTK V2.2 but I'm getting different results.
This is what I got in Python to create a model:
def create_model_function(num_hidden_layers,hidden_layers_dim,num_output_classes):
return Sequential([For(range(num_hidden_layers)
, lambda i: Dense(hidden_layers_dim
, activation=cntk.tanh))
, Dense(num_classes,init=cntk.glorot_uniform()
, activation=cntk.softmax)])
Thanks
My C# function looks like this:
private Function CreateModel(DeviceDescriptor device, int HiddenLayerCount, int HiddenLayerDimension,int OutputClassesCount, Variable Input)
{
Function[] HiddenLayers= new Function[HiddenLayerCount];
for (int i = 1; i < HiddenLayerCount - 1; i++)
{
HiddenLayers[i] = Dense(HiddenLayers[i - 1], HiddenLayerDimension, device, Activation.Tanh, "");
}
return Dense(HiddenLayers[HiddenLayerCount-1], OutputClassesCount, device, Activation.Sigmoid, "");
}
I'm just not sure this is the equivalent of the Python sequential.
Python Dense function is not directly supported in C# yet. The Dense function you used in C# might be different than CNTK Python implementation. May you build a model in both C# and python with operators available in C# and see if they are the same?
I am attaching a C# function to help you check the model graph. Use it with python model loaded into C# and compare with the one you created in C#. Thanks.
static void PrintGraph(Function function, int spaces, bool useName = false)
{
string indent = new string('.', spaces);
if (function.Inputs.Count() == 0)
{
Console.WriteLine(indent + "(" + (useName ? function.Name : function.Uid) + ")" +
"(" + function.OpName + ")" + function.AsString());
return;
}
foreach (var input in function.Inputs)
{
Console.WriteLine(indent + "(" + (useName ? function.Name : function.Uid) + ")" +
"(" + function.OpName + ")" + "->" +
"(" + (useName ? input.Name : input.Uid) + ")" + input.AsString());
}
foreach (var input in function.Inputs)
{
if (input.Owner != null)
{
Function f = input.Owner;
PrintGraph(f, spaces + 4, useName);
}
}
}
Following example shows a simple feed forward path from left to right.
To generate the deep network,
control the FOR LOOP per your requirements.
In this example,
Loop control is modifying number of nodes with even and odd loopcount.
CreateUniteLayer builds a unit layer
- LXNodes on left side connected to LYNodes on right side.
Other variables are self explained.
ParameterVector NetParamVec is needed to create the trainer.
Pass this as parameter if you use CNTKLib.xxxx_learner function.
Carefully check on connectivity of Input features to first layer,
First layer to intermediate layers
and then last layer connection
finally leading to sigmoid.
Adjust LXNodes and LYNodes variables appropriately per your need.
Add this code in a class or pull it inside a method as appropriate for your application.
If building all layers of same node size,
LXNodes = LYNodes = number of nodes per layer
NetOut represents final output of the deep network.
Hope this helps to build the net you are looking for.
Best wishes.
List<Function> Layers = new List<Function>() ;
ParameterVector NetParamVec = new ParameterVector ();
// Define first layer immediately after input.
Function layer1 = CreateUnitLayer(features, LXNodes, inputDim, "NetLayer0", InitWeight, InitBias);
Layers.Add(layer1);
//Defines Intermediate hidden layers
for (int i = 1; i < LayerCount; i++)
{
Function ly;
if (i % 2 == 0)
ly = CreateUnitLayer(Layers[i - 1], LXNodes, LYNodes, "NetLayer" + i.ToString(), InitWeight, InitBias);
else
ly = CreateUnitLayer(Layers[i - 1], LYNodes, LXNodes, "NetLayer" + i.ToString(), InitWeight, InitBias);
Layers.Add(ly);
}
//Defines Last layer
int lastDim = LXNodes;
if (LayerCount % 2 == 0)lastDim = LYNodes;
Function layerLast = CreateUnitLayer(Layers[LayerCount - 1], outDim, lastDim, "NetLayerOut", InitWeight, InitBias);
Layers.Add(layerLast);
Function NetOut = CNTKLib.Sigmoid(layerLast);
public Function CreateUnitLayer(Variable LXIn, int LYNodes, int LXNodes, string LYName, float InitWeight, float InitBias)
{
Parameter weightParamy = new Parameter(new int[] { LYNodes, LXNodes }, DataType.Float, InitWeight, device, "W" + LYName);
Parameter biasParamy = new Parameter(new int[] { LYNodes }, DataType.Float, InitBias, device, "B" + LYName);
Function LayerY = CNTKLib.Plus(CNTKLib.Times(weightParamy, LXIn), biasParamy);
NetParamVec.Add(weightParamy);
NetParamVec.Add(biasParamy);
return LayerY;
}

C# String Concatenation OutOfRange error

I'm utilizing Get String functions from a "Label" class to put together lines to print on a bitmap. The program compiles fine, and the previous form passes the LabelQueue properly (it would appear with no issue before I tried to print the bitmap). All the code of this particular initializer/constructor is below. The erroneous lines of code are the final three lines of the function before the "c++".
Let me know if you need me to add any more necessary code.
I'm getting an IndexOutofRange exception, claiming it was outside of the bounds of the array.
private LabelQueue lq;
public Print(LabelQueue queue)
{
InitializeComponent();
lq = queue;
pictureBox1.Image = new Bitmap(2550, 3300);
System.Drawing.Graphics formGraphics = this.CreateGraphics();
System.Drawing.Font textFont = new System.Drawing.Font("Times New Roman", 8);
System.Drawing.SolidBrush textBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);
System.Drawing.StringFormat textFormat = new System.Drawing.StringFormat();
int x, y, c = 0;
while (c < 30)
{
// Get coordinates for where to put values.
x = ((c % 3) * 600) + 300;
// Accounts for column gap
if (c % 3 > 0)
x = x + ((c % 3) - 1) * 75;
y = ((c % 10) * 270) + 300;
string firstLine, secondLine, thirdLine;
firstLine = lq.labels[c].GetLastName() + ", " + lq.labels[c].GetFirstName() + " " + lq.labels[c].GetMiddleName();
secondLine = lq.labels[c].GetNewStreet();
thirdLine = lq.labels[c].GetNewCity() + ", " + lq.labels[c].GetNewState() + lq.labels[c].GetNewZIP() + lq.labels[c].GetNewCountry();
formGraphics.DrawString(firstLine, textFont, textBrush, x, y, textFormat); // Line turning up the error
formGraphics.DrawString(firstLine, textFont, textBrush, x, y + 10, textFormat); // Naturally, both these lines would need to be fixed too
formGraphics.DrawString(firstLine, textFont, textBrush, x, y + 20, textFormat);
c++;
}
}
Without a good, minimal, complete code example that reliably demonstrates the problem, it's impossible to know for sure the exact fix you need.
However, based on the information on this question and the comments so far, it appears that you simply aren't limiting your loop correctly. The while statement should look like this:
while (c < lq.Count())
Note that the above uses the Enumerable.Count() extension method. I chose that as the answer, because you didn't include the declaration/implementation of your LabelQueue object, so there's no way to know for sure what the correct syntax would be, but the extension method is likely to work because pretty much any reasonable collection type that supports an indexer will implement some interface that allows the Enumerable.Count() method to work well.
That said, your type probably has a Count property, which you can use instead of the Count() extension method. Either will work equally well.
Finally, for future reference it is fine to answer your own question. It's just that your answer should actually be an answer. I.e. it needs to explain clearly what was wrong, and what you did to fix it. Writing "Issue is fixed" doesn't count as an answer.
For that matter, if you don't like my answer here and you want to write your own, you can still do that. You can even accept your own answer instead of mine if you'd rather. Just make sure it's a real answer.

math equation for fighting

I have the following code for a fight but the user will win around 99.9% of the time (tested with 5000 random loops)
I have the following variables that affect the fight
strength | defence | dexterity | damage | portal difficulty (always + 1 to stop * by 0) | critical
This is what I have so far
//player variable
//(int) player.itemDamage = 20
//(int) player.itemStr = 2
//(int) player.itemDex = 4
int defense = (int)Math.Round((((portal + 1) * ((rand.NextDouble() + 0.5))) + 5) / 2, 0);
int damage = (int)Math.Round((((portal + 1) * ((rand.NextDouble() + 0.5))) + player.itemDamage), 0);
int str = (int)Math.Round((((portal + 1) * ((rand.NextDouble() + 0.5))) + player.itemStr), 0);
int dex = (int)Math.Round((((portal + 1) * ((rand.NextDouble() + 0.5))) + player.itemDex), 0);
while(true)
{
for (int i = 0; i < 10; i++)
{
critical += rand.NextDouble();
}
eHP -= (int)Math.Round((((player.itemDamage + player.itemStr) - defense) * critical) / 2, 0);
critical = 1;
for (int i = 0; i < 10; i++)
{
critical += rand.NextDouble();
}
HP -= (int)Math.Round((((damage + str) - 5) * critical) / 2, 0);
if (eHP <= 0)
{
return;
}
else if (HP <= 0)
{
return;
}
}
What could I change in the following code to let the user (HP) win 70% of the time can I get some suggestions please? I am terrible with algorithms
Edit: Although I want the user to win 70% of the time I still want it to be round based on damage not using a simple if(0 > 70) win else loose statement because that wouldn't be a very interesting fight.
if (rand.next(10) > 7) {
//user wins
} else {
//user loses
}
In all seriousness, it is understandable why you are having trouble with this based on how you are approaching it with code. You should instead treat it like a math function.
Create a pure function f from a set of inputs (x, y, ..., z) to a boolean output. There should not be any usage of rand in this function. If you need random input, pass then into the function. Try doing it first without overwriting existing variables (don't use +=/-=). For every (x, y, z), f(x, y, z) should always result in the same output. If you do this, it should be pretty easy to use maths to solve for the missing values.
As drstevens says, your implementation would be better off split into smaller, pure functions.
In particular, the number of hit points lost appears to be proportional to the value of critical. The code you give does not show what the value of critical is when the loop starts, but after the first iteration the value will be about 5 higher when reducing 'enemy' hit points than when reducing 'user' hit points in the previous iteration, because you don't set it to 1 before adding ten random numbers to it. As critical for user is generally around 11 and for enemy around 6, it would take a strong enemy to win ( for example, one where defence is greater than the user's strength and item damage, so it gains hit points if you hit them!).
If you had written a pure function which calculated either critical or the number of hit points to subtract, this error would not have happened.

Compare each element in an array to each other

I need to compare a 1-dimensional array, in that I need to compare each element of the array with each other element. The array contains a list of strings sorted from longest to the shortest. No 2 items in the array are equal however there will be items with the same length. Currently I am making N*(N+1)/2 comparisons (127.8 Billion) and I'm trying to reduce the number of over all comparisons.
I have implemented a feature that basically says: If the strings are different in length by more than x percent then don't bother they not equal, AND the other guys below him aren't equal either so just break the loop and move on to the next element.
I am currently trying to further reduce this by saying that: If element A matches element C and D then it stands to reason that elements C and D would also match so don't bother checking them (i.e. skip that operation). This is as far as I've factored since I don't currently know of a data structure that will allow me to do that.
The question here is: Does anyone know of such a data structure? or Does anyone know how I can further reduce my comparisons?
My current implementation is estimated to take 3.5 days to complete in a time window of 10 hours (i.e. it's too long) and my only options left are either to reduce the execution time, which may or may not be possible, or distrubute the workload accross dozens of systems, which may not be practical.
Update: My bad. Replace the word equal with closely matches with. I'm calculating the Levenstein distance
The idea is to find out if there are other strings in the array which closely matches with each element in the array. The output is a database mapping of the strings that were closely related.
Here is the partial code from the method. Prior to executing this code block there is code that loads items into the datbase.
public static void RelatedAddressCompute() {
TableWipe("RelatedAddress");
decimal _requiredDistance = Properties.Settings.Default.LevenshteinDistance;
SqlConnection _connection = new SqlConnection(Properties.Settings.Default.AML_STORE);
_connection.Open();
string _cacheFilter = "LevenshteinCache NOT IN ('','SAMEASABOVE','SAME')";
SqlCommand _dataCommand = new SqlCommand(#"
SELECT
COUNT(DISTINCT LevenshteinCache)
FROM
Address
WHERE
" + _cacheFilter + #"
AND
LEN(LevenshteinCache) > 12", _connection);
_dataCommand.CommandTimeout = 0;
int _addressCount = (int)_dataCommand.ExecuteScalar();
_dataCommand = new SqlCommand(#"
SELECT
Data.LevenshteinCache,
Data.CacheCount
FROM
(SELECT
DISTINCT LevenshteinCache,
COUNT(LevenshteinCache) AS CacheCount
FROM
Address
WHERE
" + _cacheFilter + #"
GROUP BY
LevenshteinCache) Data
WHERE
LEN(LevenshteinCache) > 12
ORDER BY
LEN(LevenshteinCache) DESC", _connection);
_dataCommand.CommandTimeout = 0;
SqlDataReader _addressReader = _dataCommand.ExecuteReader();
string[] _addresses = new string[_addressCount + 1];
int[] _addressInstance = new int[_addressCount + 1];
int _itemIndex = 1;
while (_addressReader.Read()) {
string _address = (string)_addressReader[0];
int _count = (int)_addressReader[1];
_addresses[_itemIndex] = _address;
_addressInstance[_itemIndex] = _count;
_itemIndex++;
}
_addressReader.Close();
decimal _comparasionsMade = 0;
decimal _comparisionsAttempted = 0;
decimal _comparisionsExpected = (decimal)_addressCount * ((decimal)_addressCount + 1) / 2;
decimal _percentCompleted = 0;
DateTime _startTime = DateTime.Now;
Parallel.For(1, _addressCount, delegate(int i) {
for (int _index = i + 1; _index <= _addressCount; _index++) {
_comparisionsAttempted++;
decimal _percent = _addresses[i].Length < _addresses[_index].Length ? (decimal)_addresses[i].Length / (decimal)_addresses[_index].Length : (decimal)_addresses[_index].Length / (decimal)_addresses[i].Length;
if (_percent < _requiredDistance) {
decimal _difference = new Levenshtein().threasholdiLD(_addresses[i], _addresses[_index], 50);
_comparasionsMade++;
if (_difference <= _requiredDistance) {
InsertRelatedAddress(ref _connection, _addresses[i], _addresses[_index], _difference);
}
}
else {
_comparisionsAttempted += _addressCount - _index;
break;
}
}
if (_addressInstance[i] > 1 && _addressInstance[i] < 31) {
InsertRelatedAddress(ref _connection, _addresses[i], _addresses[i], 0);
}
_percentCompleted = (_comparisionsAttempted / _comparisionsExpected) * 100M;
TimeSpan _estimatedDuration = new TimeSpan((long)((((decimal)(DateTime.Now - _startTime).Ticks) / _percentCompleted) * 100));
TimeSpan _timeRemaining = _estimatedDuration - (DateTime.Now - _startTime);
string _timeRemains = _timeRemaining.ToString();
});
}
InsertRelatedAddress is a function that updates the database, and there are 500,000 items in the array.
OK. With the updated question, I think it makes more sense. You want to find pairs of strings with a Levenshtein Distance less than a preset distance. I think the key is that you don't compare every set of strings and rely on the properties of Levenshtein distance to search for strings within your preset limit. The answer involves computing the tree of possible changes. That is, compute possible changes to a given string with distance < n and see if any of those strings are in your set. I supposed this is only faster if n is small.
It looks like the question posted here: Finding closest neighbour using optimized Levenshtein Algorithm.
More info required. What is your desired outcome? Are you trying to get a count of all unique strings? You state that you want to see if 2 strings are equal and that if 'they are different in length by x percent then don't bother they not equal'. Why are you checking with a constraint on length by x percent? If you're checking for them to be equal they must be the same length.
I suspect you are trying to something slightly different to determining an exact match in which case I need more info.
Thanks
Neil

Categories

Resources