iOS Swift3 - Convert coordinate - ios

I'm trying to create annotations on the MapKit from a geoJSON file, but the problem is that the coordinates provided by the geoJSON file don't match the coordinate system that MapKit uses.
Question : How do I convert read the geoJSON file and convert the coordinates from `` to WGS84S?
Here is an example of what the geoJSON file looks like:
{"name":"MAPADDRESSPOINT","type":"FeatureCollection"
,"crs":{"type":"name","properties":{"name":"EPSG:3008"}}
,"features":[
{"type":"Feature","geometry":{
"type":"Point","coordinates": [97973.4655999987,6219081.53249992,0]},
"properties":{
"ADDRESSAREA_resolved":"Sadelvägen",
"multi_reader_id":1,
"multi_reader_full_id":1,
"BALSTATUS_resolved":"Gällande",
"REMARKTYPE_resolved":"",
"FARMADDRESSAREA_resolved":"",
"geodb_type":"geodb_point",
"multi_reader_keyword":"GEODATABASE_SDE_2",
"DEVIATEFROMSTANDARD_resolved":"",
"geodb_feature_is_simple":"yes",
"STATUS_resolved":"Ingen information",
"ADDRESSEDCONSTRUCTIONTYPE_resolved":"",
"SUPPLIER_resolved":"",
"multi_reader_type":"GEODATABASE_SDE",
"geodb_oid":18396,
"STAIRCASEIDENTIFIER_resolved":"",
"LOCATIONADDRESSSTATUS_resolved":"Gällande",
"POSITIONKIND_resolved":"Byggnad",
"BALADDRESSTYPE_resolved":"Gatuadressplats",
"COMMENTARY":"","
DTYPE":"",
"EXTERNALID":2,"GID":"{DEEA1685-2FF3-4BEB-823D-B9FA51E09F71}",
"MODIFICATIONDATE":"20170301173751",
"MODIFICATIONSIGN":"BAL service",
"OBJECTID":18396,
"REGDATE":"20110321151134",
"REGSIGN":"BAL service",
"STATUS":0,
"ADDRESSEDCONSTRUCTIONVALUE":"",
"LABELROTATIONANGLE":0,
"POSTCODE":"25483",
"POSITIONKIND":1,
"REALPROPERTYKEY":"120320803",
"BALSTATUS":2,
"BALADDRESSTYPE":1,
"BALID":"D5650F0B-EE54-4C4C-9C40-A8162118288C",
"DESIGNATIONVALUE":"",
"SYNCDATE":"20170301173751",
"STREETNAME":"Sadelvägen",
"ADDRESSAREA":554,
"YARDSNAME":"",
"PLACENAMEID":"",
"ADDRESSLABEL":"Sadelvägen 6",
"DESIGNATIONNUMBERLETTER":"",
"LOCATIONADDRESSSTATUS":3,
"CITY":"Helsingborg",
"ENUMERATOR":"6",
"SYMBOLROTATIONANGLE":0,
"POPULARNAME":"",
"geodb_feature_dataset":"Adress"
}
}
}]
}

https://en.m.wikipedia.org/wiki/Transverse_Mercator_projection
Longitude of natural origin 0° 00' 00.000" N
13° 30' 00.000" E
Scale factor at natural origin
1
False easting
150000
meters
False northing
0
FINAL (Playground) version
//: [Previous](#previous)
import Foundation
extension Double {
var rad: Double {
get {
return .pi * self / 180.0
}
}
var deg: Double {
get {
return 180.0 * self / .pi
}
}
}
// SWEREF99 13 30 (GRS80)
let φ0 = 0.0
let λ0 = 13.5.rad
let N0 = 0.0
let E0 = 150000.0
let k0 = 1.0
// GRS80
let a = k0 * 6378137.0
let b = k0 * 6356752.31414034
let n = (a - b)/(a + b)
let n2 = n * n
let n3 = n2 * n
let n4 = n3 * n
let a2 = a * a
let b2 = b * b
let e2 = (a2 - b2)/a2
let H0 = 1.0 + 1.0/4.0*n2 + 1.0/64.0*n4
let H2 = -3.0/2.0*n + 3.0/16.0*n3
let H4 = 15.0/16.0*n2 - 15.0/64.0*n4
let H6 = -35.0/48.0*n3
let H8 = 315.0/512.0*n4
let ν:(Double)->Double = { φ in
return a/(sqrt(1.0 - e2 * sin(φ) * sin(φ)))
}
let ρ:(Double)->Double = { φ in
return ν(φ) * (1.0 - e2) / (1.0 - e2 * sin(φ) * sin(φ))
}
let η2:(Double)->Double = { φ in
return ν(φ) / ρ(φ) - 1.0
}
var arcMeridian1:(Double)->Double = { φ in
let m = (a + b) / 2 * (H0 * φ + H2 * sin(2.0 * φ) + H4 * sin(4.0 * φ) + H6 * sin(6.0 * φ) + H8 * sin(8.0 * φ))
return m
}
var arcMeridian:(Double, Double)->Double = { φ1, φ2 in
return arcMeridian1(φ2) - arcMeridian1(φ1)
}
var cartografic:(Double,Double)->(Double,Double) = { φ, λ in
let νφ = ν(φ)
let ρφ = ρ(φ)
let η2φ = νφ / ρφ - 1.0
let s1 = sin(φ)
let s2 = s1 * s1
let c1 = cos(φ)
let c2 = c1 * c1
let c3 = c2 * c1
let c5 = c3 * c2
let t2 = s2/c2
let t4 = t2 * t2
let k1 = νφ * c1
let k2 = νφ/2.0 * s1 * c1
let k3 = νφ/6.0 * c3 * (νφ / ρφ - t2)
let k4 = νφ/24.0 * s1 * c3 * (5.0 - t2 + 9.0 * η2φ)
let k5 = νφ/120.0 * c5 * (5.0 - 18.0 * t2 + t4 + 14.0 * η2φ - 58.0 * t2 * η2φ)
let k6 = νφ/720.0 * s1 * c5 * (61.0 - 58.0 * t2 + t4)
let Δλ = λ - λ0
let Δλ2 = Δλ * Δλ
let Δλ3 = Δλ2 * Δλ
let Δλ4 = Δλ3 * Δλ
let Δλ5 = Δλ4 * Δλ
let Δλ6 = Δλ4 * Δλ
let N = arcMeridian(φ0,φ) + N0 + Δλ2 * k2 + Δλ4 * k4 + Δλ6 * k6
let E = E0 + Δλ * k1 + Δλ3 * k3 + Δλ5 * k5
return (N,E)
}
var geodetic:(Double,Double)->(Double,Double) = { N, E in
var φ = (N - N0) / a + φ0
var M = arcMeridian(φ0, φ)
var diff = 1.0
repeat {
φ += (N - N0 - M) / a
M = arcMeridian(φ0, φ)
diff = N - N0 - M
} while abs(diff) > 0.0000000001 // max 3 - 4 iterations
let E1 = E - E0
let E2 = E1 * E1
let E3 = E2 * E1
let E4 = E3 * E1
let E5 = E4 * E1
let E6 = E5 * E1
let E7 = E6 * E1
let νφ = ν(φ)
let νφ3 = νφ * νφ * νφ
let νφ5 = νφ3 * νφ * νφ
let νφ7 = νφ5 * νφ * νφ
let ρφ = ρ(φ)
let η2φ = νφ / ρφ - 1.0
let s1 = sin(φ)
let s2 = s1 * s1
let c1 = cos(φ)
let t1 = s1 / c1
let t2 = t1 * t1
let t4 = t2 * t2
let t6 = t4 * t2
let k1 = 1.0 / (c1 * νφ)
let k2 = t1 / (2.0 * ρφ * νφ)
let k3 = 1.0 / (6.0 * νφ3) * (νφ / ρφ + 2.0 * t2)
let k4 = (t1 / (24.0 * ρφ * νφ3)) * (5.0 + 3.0 * t2 + η2φ - 9.0 * t2 * η2φ)
let k5 = 1.0 / (120.0 * νφ5) * (5.0 + 28.0 * t2 + 24.0 * t4)
let k6 = (t1 / (720.0 * ρφ * νφ5)) * (61.0 + 90.0 * t2 + 45.0 * t4)
let k7 = (t1 / (5040.0 * ρφ * νφ7)) * (61.0 + 662.0 * t2 + 1320.0 * t4 + 720.0 * t6)
φ = φ - E2 * k2 + E4 * k4 - E6 * k6
let λ = λ0 + E1 * k1 - E3 * k3 + E5 * k5 - E7 * k7
return (φ, λ)
}
print("pecision check")
let carto0 = cartografic(55.0.rad, 12.75.rad)
print(carto0,"err:", carto0.0 - 6097487.637, carto0.1 - 102004.871)
let carto1 = cartografic(61.0.rad, 14.25.rad)
print(carto1,"err:", carto1.0 - 6765725.847, carto1.1 - 190579.995)
print()
print("given position: N 6219081.53249992, E 97973.4655999987")
let geo = geodetic(6219081.53249992, 97973.4655999987)
print("geodetic: φ =", geo.0.deg,"λ =", geo.1.deg)
//: [Next](#next)
prints
pecision check
(6097487.6372101102, 102004.87085248799) err: 0.00021011009812355 -0.000147512007970363
(6765725.8471242301, 190579.99493182387) err: 0.000124230049550533 -6.81761302985251e-05
given position: N 6219081.53249992, E 97973.4655999987
geodetic: φ = 56.0916410844269 λ = 12.6641326192406
position on map

I dont understand what are you looking for exactly but what I would do (since you are using swift) is create an extension of MKPointAnnotation and add a new initialiser that takes the geoJSON, parse it, fetch the coordinates (and whatever data you intend to put in the annotation object) and return a MKPointAnnotation with the required data.
just incase you dont know how to parse a JSON here s an example :
do {
let dictionary = try JSONSerialization.jsonObject(with: geoJSON!,
options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: AnyObject]
let features = dictionary["features"] as! [[String:AnyObject]]
let geometry = features[0]["geometry"] as! [String: AnyObject]
....
}catch{
//incase the json doesnt get parsed
}
keep going until you reach the coordinates ..;
obviously dont use force cast incase you are hesitate about a value instead use guard or if let ....

Related

Cost does not converge/converges very slowly in the soft-coded version?

I don't understand. When I hardcode my script, it converges excellent, but in the softcode version, given the same structure and learning rate, it converges very slowly and then simply stops converging from some point on.
Here is the softcode version:
def BCE_loss(Y_hat, Y):
m = Y_hat.shape[1]
cost = (-1 / m) * (np.dot(Y, np.log(Y_hat+1e-5).T) + np.dot(1-Y, np.log(1-Y_hat+1e-5).T))
cost = np.squeeze(cost)
return cost
def BCE_loss_backward(Y_hat, Y):
dA_prev = - (np.divide(Y, Y_hat) - np.divide(1-Y, 1-Y_hat))
return dA_prev
def gradient(dZ, A_prev):
dW = np.dot(dZ, A_prev.T) * (1 / A_prev.shape[1])
db = np.sum(dZ, axis=1, keepdims=True) * (1 / A_prev.shape[1])
return dW, db
def update(W, b, dW, db, learning_rate):
W -= np.dot(learning_rate, dW)
b -= np.dot(learning_rate, db)
return W, b
for i in range(epochs+1):
## Forward pass
for l in range(1, L):
if l==L-1:
if out_dim==1:
grads_GD['Z'+str(l)] = linear(params_GD['W'+str(l)], grads_GD['A'+str(l-1)], params_GD['b'+str(l)])
grads_GD['A'+str(l)] = sigmoid(grads_GD['Z'+str(l)])
else:
grads_GD['Z'+str(l)] = linear(params_GD['W'+str(l)], grads_GD['A'+str(l-1)], params_GD['b'+str(l)])
grads_GD['A'+str(l)] = softmax(grads_GD['Z'+str(l)])
else:
grads_GD['Z'+str(l)] = linear(params_GD['W'+str(l)], grads_GD['A'+str(l-1)], params_GD['b'+str(l)])
grads_GD['A'+str(l)] = relu(grads_GD['Z'+str(l)])
## Compute cost
if out_dim==1:
cost_GD = BCE_loss(grads_GD['A'+str(L-1)], Y)
cost_list_GD.append(cost_GD)
else:
cost_GD = CE_loss(grads_GD['A'+str(L-1)], Y)
cost_list_GD.append(cost_GD)
## Print cost
if i % print_num == 0:
print(f"Cost for gradient descent optimizer after epoch {i}: {cost_GD: .4f}")
elif cost_GD < cost_lim or i == epochs:
last_epoch_GD = i
print(f"Cost for gradient descent optimizer after epoch {i}: {cost_GD: .4f}")
break
else:
continue
## Backward pass
if out_dim==1:
grads_GD['dA'+str(L-1)] = BCE_loss_backward(grads_GD['A'+str(L-1)], Y)
grads_GD['dZ'+str(L-1)] = sigmoid_backward(grads_GD['dA'+str(L-1)], grads_GD['Z'+str(L-1)])
else:
grads_GD['dA'+str(L-1)] = CE_loss_backward(grads_GD['A'+str(L-1)], Y)
grads_GD['dZ'+str(L-1)] = softmax_backward(grads_GD['dA'+str(L-1)], grads_GD['Z'+str(L-1)])
grads_GD['dW'+str(L-1)], grads_GD['db'+str(L-1)] = gradient(grads_GD['dZ'+str(L-1)], grads_GD['A'+str(L-2)])
for l in reversed(range(1, L-1)):
grads_GD['dA'+str(l)] = linear_backward(params_GD['W'+str(l+1)], grads_GD['dZ'+str(l+1)])
grads_GD['dZ'+str(l)] = relu_backward(grads_GD['dA'+str(l)], grads_GD['Z'+str(l)])
grads_GD['dW'+str(l)], grads_GD['db'+str(l)] = gradient(grads_GD['dZ'+str(l)], grads_GD['A'+str(l-1)])
## Update parameters
for l in range(1, L):
params_GD['W'+str(l)], params_GD['b'+str(l)] = update(params_GD['W'+str(l)], params_GD['b'+str(l)], grads_GD['dW'+str(l)], grads_GD['db'+str(l)], learning_rate)
and here is the hardcode version:
def cost_function(Y, A4, N, epsilon):
cost = (-1 / N) * np.sum(np.multiply(Y, np.log(A4 + epsilon)) + np.multiply(1 - Y, np.log(1 - A4 + epsilon)))
return cost
for i in range(epochs):
Z1_GD = np.dot(W1_GD, X) + b1_GD
A1_GD = np.maximum(0, Z1_GD)
Z2_GD = np.dot(W2_GD, A1_GD) + b2_GD
A2_GD = np.maximum(0, Z2_GD)
Z3_GD = np.dot(W3_GD, A2_GD) + b3_GD
A3_GD = np.maximum(0, Z3_GD)
Z4_GD = np.dot(W4_GD, A3_GD) + b4_GD
A4_GD = class_layer(Z4_GD)
dZ4_GD = A4_GD - Y
dW4_GD = np.dot(dZ4_GD, A3_GD.T) * (1. / A3_GD.shape[1])
db4_GD = np.sum(dZ4_GD, axis=1, keepdims=True) * (1. / A3_GD.shape[1])
dA3_GD = np.dot(W4_GD.T, dZ4_GD)
dZ3_GD = np.array(dA3_GD, copy=True)
dZ3_GD[Z3_GD <= 0] = 0
dW3_GD = np.dot(dZ3_GD, A2_GD.T) * (1. / A2_GD.shape[1])
db3_GD = np.sum(dZ3_GD, axis=1, keepdims=True) * (1. / A2_GD.shape[1])
dA2_GD = np.dot(W3_GD.T, dZ3_GD)
dZ2_GD = np.array(dA2_GD, copy=True)
dZ2_GD[Z2_GD <= 0] = 0
dW2_GD = np.dot(dZ2_GD, A1_GD.T) * (1. / A1_GD.shape[1])
db2_GD = np.sum(dZ2_GD, axis=1, keepdims=True) * (1. / A1_GD.shape[1])
dA1_GD = np.dot(W2_GD.T, dZ2_GD)
dZ1_GD = np.array(dA1_GD, copy=True)
dZ1_GD[Z1_GD <= 0] = 0
dW1_GD = np.dot(dZ1_GD, X.T) * (1. / X.shape[1])
db1_GD = np.sum(dZ1_GD, axis=1, keepdims=True) * (1. / X.shape[1])
W1_GD = W1_GD - learning_rate * dW1_GD
b1_GD = b1_GD - learning_rate * db1_GD
W2_GD = W2_GD - learning_rate * dW2_GD
b2_GD = b2_GD - learning_rate * db2_GD
W3_GD = W3_GD - learning_rate * dW3_GD
b3_GD = b3_GD - learning_rate * db3_GD
W4_GD = W4_GD - learning_rate * dW4_GD
b4_GD = b4_GD - learning_rate * db4_GD
cost_GD = cost_function(Y, A4_GD, N, epsilon)
cost_GD = np.squeeze(cost_GD)
cost_list_GD.append(cost_GD)
I suppose something went wrong during softcoding.
I solved it myself. Apparently, the "else: continue" line in the print cost section caused the algorithm to do a backward pass only once. After that, it was just looping through the forward pass. Can anyone please explain the reason for such behavior?

How can I change the Double value in my dictionary outside the function?

var grades: [String : Double]
grades = ["A": 0.0, "A-": 0.0, "B+": 0.0, "B": 0.0, "B-": 0.0, "C+": 0.0, "C": 0.0, "C-": 0.0, "D+": 0.0, "D": 0.0, "D-": 0.0, "F": 0.0]
func calcGPA() {
if let a = grades["A"], amin = grades["A-"], bplu = grades["B+"], b = grades["B"], bmin = grades["B-"], cplu = grades["C+"], c = grades["C"], cmin = grades["C-"], dplu = grades["D+"], d = grades["D"], dmin = grades["D-"], f = grades["F"] {
// Divide by this
let gradesAdded = a + amin + bplu + b + bmin + cplu + c + cmin + dplu + d + dmin + f
//grades multiplied by their values and added ex. a * 4.0 + amin * 3.7
let gradesCalculated = a * 4.0 + amin * 3.7 + bplu * 3.3 + b * 3.0 + bmin * 2.7 + cplu * 2.3 + c * 2.0 + cmin * 1.7 + dplu * 1.3 + d * 1.0 + dmin * 0.7 // Dont do F because it would just add 0
var gpa = gradesCalculated / gradesAdded
if gpa.isNaN {
gpa = 0.0
}
}
}
Is there a way to do something like grades["A"] += 1.0 so that it goes up by one and I can call calcGPA()? I can't figure out how to make this work. Any help on this would be awesome
You can increase a value in your dictionary by force unwrapping the lookup:
grades["A"]! += 1.0
but this is dangerous because it will crash if the key isn't in the dictionary. So, you should check:
if let count = grades["A"] {
grades["A"] = count + 1
}
Here is an updated version of your program:
func calcGPA(_ termGrades: [String]) -> Double? {
var grades: [String: Double] = ["A": 0.0, "A-": 0.0, "B+": 0.0, "B": 0.0, "B-": 0.0, "C+": 0.0, "C": 0.0, "C-": 0.0, "D+": 0.0, "D": 0.0, "D-": 0.0, "F": 0.0]
var gpa: Double?
for grade in termGrades {
if let count = grades[grade] {
grades[grade] = count + 1
} else {
print("Hmmm, \(grade) is not a valid value for a grade")
return nil
}
}
if let a = grades["A"], amin = grades["A-"], bplu = grades["B+"], b = grades["B"], bmin = grades["B-"], cplu = grades["C+"], c = grades["C"], cmin = grades["C-"], dplu = grades["D+"], d = grades["D"], dmin = grades["D-"], f = grades["F"] {
// Divide by this
let gradesAdded = a + amin + bplu + b + bmin + cplu + c + cmin + dplu + d + dmin + f
//grades multiplied by their values and added ex. a * 4.0 + amin * 3.7
let gradesCalculated = a * 4.0 + amin * 3.7 + bplu * 3.3 + b * 3.0 + bmin * 2.7 + cplu * 2.3 + c * 2.0 + cmin * 1.7 + dplu * 1.3 + d * 1.0 + dmin * 0.7 // Dont do F because it would just add 0
gpa = gradesAdded == 0 ? nil : gradesCalculated / gradesAdded
}
return gpa
}
// example calls
calcGPA(["E"]) // nil "Hmmm, E is not a valid value for a grade"
calcGPA(["A-"]) // 3.7
calcGPA(["A", "B"]) // 3.5
calcGPA(["B", "B+", "A-"]) // 3.333333333333333
calcGPA([]) // nil
Notes:
I changed your function to take an array of grades and return a Double? (Optional Double).
nil is returned if anything goes wrong (input array is empty, input array contains invalid grade such as "E").
Divide by zero is avoided ahead of time by checking the divisor before the calculation.
Alternate Solution:
And now for something completely different...
Inspired by #CodeBender's comment, here is an implementation that uses an enum with an associated value to represent the grades:
enum Grade: Double {
case A = 4.0
case Aminus = 3.7
case Bplus = 3.3
case B = 3.0
case Bminus = 2.7
case Cplus = 2.3
case C = 2.0
case Cminus = 1.7
case Dplus = 1.3
case D = 1.0
case Dminus = 0.7
case F = 0
}
func calcGPA(_ termGrades: [Grade]) -> Double? {
if termGrades.count == 0 {
return nil
} else {
let total = termGrades.reduce(0.0) { (total, grade) in total + grade.rawValue }
return total / Double(termGrades.count)
}
}
// example calls
calcGPA([.A, .B]) // 3.5
calcGPA([.B, .Bplus, .Aminus]) // 3.3333333333
calcGPA([.A, .A, .Bplus]) // 3.7666666666
calcGPA([.F, .F, .F]) // 0
calcGPA([]) // nil

Auxiliary function Grade calculator

Why am I being told the let is unfinished?
let comp_grade (perc_exam, perc_homework, perc_attendance) = let (grade_exam, grade_homework, grade_attendance) =
(float)(perc_exam / 100 * grade_exam + perc_homework / 100 * grade_homework + perc_attendance / 100 * grade_attendance);
let COP4555_grade_computer = comp_grade(50,30,20);
let CEN3723_grade_computer = comp_grade(70,30,0);
let COP4555_student1_grade = COP4555_grade_computer (90, 95, 100);
let COP4555_student2_grade = COP4555_grade_computer (70, 80, 100);
let CEN3723_student1_grade = COP4555_grade_computer (90, 95, 100);
let CEN3723_student2_grade = COP4555_grade_computer (70, 80, 100);
Edit: Individually they work, but when I put them together the inner function now says it is unfinished.
let comp_grade ( perc_exam, perc_homework, perc_attendance)
(grade_exam, grade_homework, grade_attendance) =
float (perc_exam / 100 * grade_exam +
perc_homework / 100 * grade_homework +
perc_attendance / 100 * grade_attendance)
Return an function
let comp_grade ( perc_exam, perc_homework, perc_attendance) =
fun (grade_exam, grade_homework, grade_attendance) ->
float (perc_exam / 100 * grade_exam +
perc_homework / 100 * grade_homework +
perc_attendance / 100 * grade_attendance)

Cannot Invoke '^' with an argument list of type '($T27, IntegerLiteralConvertible)'

func FindDistance(currentLatitude: Double, currentLongtitude: Double, compareLatitude: Double, compareLongtitdue: Double) -> Double {
var dlon = compareLongtitdue - currentLongtitude
var dlat = compareLatitude - currentLatitude
let WorldRadius = 6371
var a = sin(dlat/2)^2 + cos(currentLatitude) * cos(compareLatitude) * sin(dlon/2)^2
var c = 2 * atan2(sqrt(a), sqrt(1-a))
var d = WorldRadius * c
}
println(FindDistance(11.583431, 104.920141, 11.584966, 104.918569))
There is an Error in Line in Variable "a". said 'Cannot Invoke '^' with an argument list of type '($T27, IntegerLiteralConvertible)'.
I guess you want to use pow(x,y) function which returns x raised to the power of y.
So in order to access this function you need to import Darwin first and rewrite code as follow:
import Darwin
func FindDistance(currentLatitude:Double, currentLongtitude:Double, compareLatitude:Double, compareLongtitdue:Double) -> Double {
var dlon = compareLongtitdue - currentLongtitude
var dlat = compareLatitude - currentLatitude
let WorldRadius: Double = 6371
var a = pow(sin(dlat/2), 2) + cos(currentLatitude) * cos(compareLatitude) * pow(sin(dlon/2),2)
var c = 2 * atan2( sqrt(a), sqrt(1-a) )
var d = WorldRadius * c
return d
}

How to compute with Quaternion numbers in Z3?

In Complex numbers in Z3 Leonardo de Moura was able to introduce and to compute with complex numbers in Z3.
Using the code proposed by Leonardo I am introducing and computing with quaternion numbers in Z3 according with the code presented here . Using this "quaternion " code I am solving the following problem:
x = Quaternion("x")
s = Tactic('qfnra-nlsat').solver()
s.add(x*x + 30 == 0, x.i3 > 0, x.i2 >0, x.i1 > 0)
print(s.check())
m = s.model()
print m
and the corresponding output is:
sat
[x.r = 0, x.i1 = 1, x.i2 = 1, x.i3 = 5.2915026221?]
This result was verified using Maple.
Other example:
x = Quaternion("x")
y = Quaternion("y")
z = Quaternion("z")
s = Tactic('qfnra-nlsat').solver()
s.add(x*y + 30 + x + y*z == 0, x - y + z == 10)
print(s.check())
m = s.model()
print m
and the output is:
sat
[y.r = 1/8,
z.r = 2601/64,
y.i1 = 1/2,
z.i1 = 45/8,
y.i2 = -1/2,
z.i2 = -45/8,
y.i3 = -1/2,
z.i3 = -45/8,
x.i3 = 41/8,
x.i2 = 41/8,
x.i1 = -41/8,
x.r = -1953/64]
Other example:
Proving that
x * y != y * x
Code:
x = Quaternion("x")
y = Quaternion("y")
a1, b1, c1, d1 = Reals('a1 b1 c1 d1')
a2, b2, c2, d2 = Reals('a2 b2 c2 d2')
x.r = a1
x.i1 = b1
x.i2 = c1
x.i3 = d1
y.r = a2
y.i1 = b2
y.i2 = c2
y.i3 = d2
print simplify((x * y - y * x).r)
print simplify((x * y - y * x).i1)
print simplify((x * y - y * x).i2)
print simplify((x * y - y * x).i3)
Output:
0
2·c2·d1 + -2·c1·d2
-2·b2·d1 + 2·b1·d2
2·b2·c1 + -2·b1·c2
Other example : Proving that the quaternions
A = (1+ I)/sqrt(2),
B =(1 + J)/sqrt(2),
C = (1 + K)/sqrt(2)
generate a representation of the Braid Group, it is to say, we have that
ABA = BAB, ACA = CAC, BCB = CBC.
Code:
A = Quaternion('A')
B = Quaternion('B')
C = Quaternion('C')
A.r = 1/Sqrt(2)
A.i1 = 1/Sqrt(2)
A.i2 = 0
A.i3 = 0
B.r = 1/Sqrt(2)
B.i1 = 0
B.i2 = 1/Sqrt(2)
B.i3 = 0
C.r = 1/Sqrt(2)
C.i1 = 0
C.i2 = 0
C.i3 = 1/Sqrt(2)
print simplify((A*B*A-B*A*B).r)
print simplify((A*B*A-B*A*B).i1)
print simplify((A*B*A-B*A*B).i2)
print simplify((A*B*A-B*A*B).i3)
print "Proved : ABA = BAB:"
print simplify((A*C*A-C*A*C).r)
print simplify((A*C*A-C*A*C).i1)
print simplify((A*C*A-C*A*C).i2)
print simplify((A*C*A-C*A*C).i3)
print "Proved : ACA = CAC:"
print simplify((B*C*B-C*B*C).r)
print simplify((B*C*B-C*B*C).i1)
print simplify((B*C*B-C*B*C).i2)
print simplify((B*C*B-C*B*C).i3)
print "Proved : BCB = CBC:"
Output:
0
0
0
0
Proved : ABA = BAB.
0
0
0
0
Proved : ACA = CAC.
0
0
0
0
Proved : BCB = CBC.
Other example: Proving that
x / x = 1
for all invertible quaternion:
Code:
x = Quaternion("x")
a, a1, a2, a3 = Reals('a a1 a2 a3')
x.r = a
x.i1 = a1
x.i2 = a2
x.i3 = a3
s = Solver()
s.add(Or(a != 0, a1 != 0, a2 != 0, a3 != 0), Not((x/x).r == 1))
print s.check()
s1 = Solver()
s1.add(Or(a != 0, a1 != 0, a2 != 0, a3 != 0), Not((x/x).i1 == 0))
print s1.check()
s2 = Solver()
s2.add(Or(a != 0, a1 != 0, a2 != 0, a3 != 0), Not((x/x).i2 == 0))
print s2.check()
s3 = Solver()
s3.add(Or(a != 0, a1 != 0, a2 != 0, a3 != 0), Not((x/x).i3 == 0))
print s3.check()
Output:
unsat
unsat
unsat
unsat
Please let me know what do you think about the "quaternion" code and how the "quaternion" code can be improved. Many thanks.

Resources